[
  {
    "path": ".gdignore",
    "content": "This file prevents Godot from directly importing these files.\n"
  },
  {
    "path": ".gitignore",
    "content": "# Demo Project\nDemoProject/Test.tscn\n\n# Godot-specific ignores\n.godot\n.import/\nexport.cfg\nexport_presets.cfg\n*.import\n\n# Mono-specific ignores\n.mono/\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nehthumbs.db\nehthumbs_vista.db\n\n# Dump file\n*.stackdump\n\n# Folder config file\n[Dd]esktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n# Rogo\n.rogo\nBuildLocal.rogue\n\n# Vim\n*.swp\n\n# macOS\n.DS_Store\n"
  },
  {
    "path": "Build.rogue",
    "content": "# To run this build file, install Rogue from github.com/brombres/Rogue then cd\n# to this folder and type \"rogo\" at the command line, or \"rogo help\" for a list\n# of supported commands.\n\n$requireRogue \"2.28\"\n\nuses Utility/FilePatcher\nuses Utility/VersionNumber\n\nroutine rogo_default\n  # Runs 'rogo help'.\n  rogo_help\nendRoutine\n\nroutine rogo_clean\n  # Deletes the .rogo build folders.\n  File( \".rogo\" ).delete\nendRoutine\n\nroutine rogo_update_version( version:String )\n  # Updates the version number and date in the project source and README.md.\n  if (not version) version = \"\"\n  version .= after_any(\"v\")\n  localize current_version\n  if (version.count == 0) throw Error( \"New version number expected, e.g. '1.0.2'. Current version: $\"(current_version) )\n  if (current_version != \"(none)\" and VersionNumber(version) <= current_version)\n    error.println( \"[WARNING] The new version number ($) is not higher than the current version number ($).\"(version,current_version) )\n  endIf\n\n  block file = File( \"README.md\" )\n    if (file.exists)\n      local og_content = String(file)\n      local content = String( og_content.count+20 )\n      forEach (line in LineReader(og_content))\n        if (line.contains_pattern(\"Version|$(I)\"))\n          content.println( \"$| $\" (line.before_last('|'),version) )\n        elseIf (line.contains_pattern(\"Date|\"))\n          local today = Date.today->String(&verbose)\n          content.println( \"$| $\" (line.before_last('|'),today) )\n        else\n          content.println( line )\n        endIf\n      endForEach\n      if (content != og_content)\n        println \"Updating version and date in $\"(file)\n        file.save( content )\n      endIf\n    endIf\n  endBlock\n\n  block patcher = FilePatcher( File(\"addons/GameGUI/plugin.cfg\") )\n    patcher.replace_line( \"version=\", ''version=\"$\"''(version) )\n    patcher.save\n  endBlock\n\n  Files(\"addons/GameGUI/**\").sync_to( File(\"DemoProject/addons/GameGUI\"), &keep_unused, &verbose )\nendRoutine\n\nroutine rogo_commit( new_version:String )\n  # Updates source and README version number and git-commits with a version number message.\n  new_version .= after_any(\"v\")\n  if (not String.exists(new_version)) throw Error( \"Expected version number after 'rogo commit'. Current version: $\"(current_version) )\n  rogo_update_version( new_version )\n\n  if (not System.find_executable(\"git\")) throw Error( \"Git must be installed.\" )\n  if (project_has_uncommitted_changes)\n    execute ''git commit -am \"[v$]\"'' (new_version)\n  else\n    println \"No changes to commit.\"\n  endIf\nendRoutine\n\nroutine rogo_publish( new_version:String )\n  # Updates source and README version number, commits, and publishes a release to GitHub.\n  new_version .= after_any(\"v\")\n  if (not String.exists(new_version)) throw Error( \"Expected version number after 'rogo publish'. Current version: $\"(current_version) )\n  if (not System.find_executable(\"gh\")) throw Error( \"The GitHub command line tool 'gh' must be installed.\" )\n\n  if (not String(File(\"ChangeLog.md\")).contains(new_version))\n    throw Error( \"The change log needs updating.\" )\n  endIf\n\n  rogo_commit( new_version )\n\n  # These can be hard-coded to suit your project\n  local cur_branch : String\n  local main_branch : String\n\n  if (not cur_branch)\n    local result = Process.run(\"git branch --show-current\",&env)\n    if (result.success) cur_branch = result->String.trimmed\n    else                throw Error( \"Unable to detect current git branch.\" )\n  endIf\n\n  if (not main_branch)\n    local result = Process.run(\"git branch --list\",&env)\n    if (result.success)\n      local branches = result->String.replacing('*',' ').split('\\n').[modify($.trimmed)]\n      forEach (branch in [\"main\",\"master\"])\n        if (branches.contains(branch)) main_branch=branch; escapeForEach\n      endForEach\n    endIf\n    if (not main_branch) throw Error( \"Unable to detect name of main (release) branch.\" )\n  endIf\n\n  execute \"git push origin $\"(cur_branch)\n  if (cur_branch != main_branch)\n    if (not Console.input(\"$ will be merged into $. Continue? \"(cur_branch,main_branch)).to_lowercase.begins_with('y')) return\n    execute \"git checkout $\"(main_branch)\n    execute \"git pull\"\n    execute \"git merge $\"(cur_branch)\n    execute \"git push origin $\"(main_branch)\n    execute \"git checkout $\"(cur_branch)\n  endIf\n\n  if (not Console.input(\"Continue publishing release to GitHub? \").to_lowercase.begins_with('y')) return\n\n  execute ''gh release create v$ --title \"v$\" --notes \"\"''(new_version,new_version)\nendRoutine\n\nroutine project_has_uncommitted_changes->Logical\n  local result = Process.run( \"git status --porcelain\", &env )\n  if (not result.success)\n    Console.error.println result->String\n    System.exit 1\n  endIf\n\n  local lines = result->String.trimmed.split('\\n').[discard($.begins_with(\"??\"))]\n  lines.discard( $ == \"\" )\n  return not lines.is_empty\nendRoutine\n\nroutine current_version->String\n  contingent\n    local file = File(\"README.md\")\n    necessary (file.exists)\n\n    if local line = String(file).split('\\n').find( $.contains_pattern(\"Version|$(I)\") )\n      local v = line.after_first('|').trimmed\n      necessary (v.count)\n      return v\n    endIf\n  endContingent\n\n  return \"(none)\"\nendRoutine\n\nroutine execute( commands:String, error_message=null:String, &suppress_error )->Logical\n  forEach (cmd in LineReader(commands))\n    print( \"> \" )\n    println( cmd )\n    if (0 != System.run(cmd))\n      if (suppress_error)\n        return false\n      else\n        if (not error_message) error_message = \"Build failed.\"\n        throw Error( error_message )\n      endIf\n    endIf\n  endForEach\n  return true\nendRoutine\n\n#-------------------------------------------------------------------------------\n# Introspection-based Launcher Framework\n#-------------------------------------------------------------------------------\n# Rogo is a \"build your own build system\" facilitator. At its core Rogo just\n# recompiles build files if needed and then runs the build executable while\n# forwarding any command line arguments. This file contains a default framework\n# which uses introspection to turn command line arguments into parameterized\n# routine calls.\n\n# Example: to handle the command \"rogo abc xyz 5\", define\n# \"routine rogo_abc_xyz( n:Int32 )\".\n\n# \"rogo_default\" will run in the absence of any other command line argument.\n\n# The following \"comment directives\" can be used in this file to control how\n# RogueC compiles it and to manage automatic dependency installation and\n# linking.\n\n# Each of the following should be on a line beginning with the characters #$\n# (preceding whitespace is fine). Sample args are given.\n\n#   ROGUEC       = roguec       # Path to roguec to compile this file with\n#   ROGUEC_ARGS  = --whatever   # Additional options to pass to RogueC\n#   CC           = gcc -Wall -fno-strict-aliasing\n#   CC_ARGS      = -a -b -c          # Additional C args\n#   LINK         = -lalpha -lbeta    # Link this build file with these options\n#   LINK(macOS)  = ...               # Options applying only to\n#                                    # System.os==\"macOS\" (use with any OS and\n#                                    # any comment directive)\n#   LINK_LIBS    = true              # Links following LIBRARIES with this Build\n#                                    # file (otherwise just installs them)\n#   LINK_LIBS    = false             # Linking turned off for following\n#                                    # LIBRARIES - info can still be obtained\n#                                    # from $LIBRARY_FLAGS or $LIBRARIES(libname,...)\n#   LIBRARIES    = libalpha\n#   LIBRARIES    = libbeta(library-name)\n#   LIBRARIES    = libfreetype6-dev(freetype2)\n#   DEPENDENCIES = Library/Rogue/**/*.rogue\n#\n#   LIBRARIES    = name(package)\n#   LIBRARIES    = name(\n#                    exe:<which-name>\n#                    exists-cmd:<exists-cmd>\n#                    flags:<library-flags>\n#                    ignore-exe-only:<setting>\n#                    info:<info-name>\n#                    info-cmd:<get-info-cmd>\n#                    install:<install-name>\n#                    install-cmd:<install-cmd>\n#                    link:<setting>\n#                    package:<package-name>\n#                  )\n#\n# The following macro is replaced within this file (Build.rogue) - the libraries\n# should normally also be declared in #$ LIBRARIES:\n#\n#   $LIBRARY_FLAGS(lib1,lib2)                              # sample macro\n#     ->\n#   -Ipath/to/lib1/include -Lpath/to/lib1/library -I ...   # sample replacement\n\nroutine syntax( command:String, text:String )\n  Build.rogo_syntax[ command ] = text\nendRoutine\n\nroutine description( command:String, text:String )\n  Build.rogo_descriptions[ command ] = text\nendRoutine\n\nroutine help( command:String, description_text=null:String, syntax_text=null:String )\n  if (description_text) description( command, description_text )\n  if (syntax_text)      syntax( command, syntax_text )\nendRoutine\n\ntry\n  Build.launch\ncatch (err:Error)\n  Build.rogo_error = err\n  Build.on_error\nendTry\n\nclass Build [singleton]\n  PROPERTIES\n    rogo_syntax         = [String:String]\n    rogo_descriptions   = [String:String]\n    rogo_prefix         = \"rogo_\"\n    rogo_command        = \"default\"\n    rogo_args           = @[]\n    rogo_error          : Error\n\n    LOCAL_SETTINGS_FILE = \"Local.rogo\"\n\n  METHODS\n    method launch\n      rogo_args.add( forEach in System.command_line_arguments )\n      read_defs\n      on_launch\n      parse_args\n      dispatch_command\n\n    method dispatch_command\n      local m = find_command( rogo_command )\n      if (not m) throw Error( \"No such routine rogo_$()\" (rogo_command) )\n\n      local args = @[]\n      forEach (arg in rogo_args)\n        which (arg)\n          case \"true\":  args.add( true )\n          case \"false\": args.add( false )\n          case \"null\":  args.add( null )\n          others:       args.add( arg )\n        endWhich\n      endForEach\n      m( args )\n\n    method find_command( name:String )->MethodInfo\n      return <<Routine>>.find_global_method( rogo_prefix + name )\n\n    method on_error\n      local w = Console.width.or_smaller( 80 )\n      Console.error.println \"=\" * w\n      Console.error.println rogo_error->String.word_wrapped(w)\n      Console.error.println \"=\" * w\n      on_exit\n      System.exit 1\n\n    method on_command_found\n      noAction\n\n    method on_command_not_found\n      local w = Console.width.or_smaller( 80 )\n      println \"=\" * w\n      println \"ERROR: No such command '$'.\" (rogo_args.first)\n      println \"=\" * w\n      println\n      rogo_command = \"help\"\n      rogo_args.clear\n      on_command_found\n\n    method on_launch\n      noAction\n\n    method on_exit\n      noAction\n\n    method parse_args\n      block\n        if (rogo_args.count)\n          local parts = String[]\n          parts.add( forEach in rogo_args )\n          rogo_args.clear\n\n          while (parts.count)\n            local cmd = parts.join(\"_\")\n            if (find_command(cmd))\n              rogo_command = cmd\n              on_command_found\n              escapeBlock\n            endIf\n            rogo_args.insert( parts.remove_last )\n          endWhile\n\n          on_command_not_found\n        endIf\n\n        # Use default command\n        on_command_found\n      endBlock\n\n    method read_defs\n      read_defs( LOCAL_SETTINGS_FILE )\n\n    method read_defs( defs_filepath:String )\n      # Attempt to read defs from Local.rogo\n      local overrides = String[]\n      if (File(defs_filepath).exists)\n        forEach (line in LineReader(File(defs_filepath)))\n          if (line.contains(\"=\"))\n            local name  = line.before_first('=').trimmed\n            local rhs   = line.after_first('=').trimmed\n            local value : Value\n            if (rhs.begins_with('\"') or rhs.begins_with('\\''))\n              value = rhs.leftmost(-1).rightmost(-1)\n            elseIf (rhs.begins_with('{') or rhs.begins_with('['))\n              value = JSON.parse( rhs )\n            else\n              value = rhs\n            endIf\n            local p = <<Build>>.find_property( name )\n            if (p)\n              overrides.add( \"$ = $\" (name,value) )\n              p.set_value( this, value )\n            endIf\n          endIf\n        endForEach\n      endIf\n\n    method _join( value:Value )->String\n      local args = String[]\n      args.add( forEach in value )\n      return args.join( \"_\" )\nendClass\n\n\nroutine rogo_help( command=\"\":String )\n  # SYNTAX: rogo help [command]\n  # Displays help for a specified command or else all build commands.\n  command = Build._join( Build.rogo_args )\n  if (command.count)\n    local syntax = get_syntax( command )\n    local success = false\n    if (syntax)\n      println \"SYNTAX\"\n      println \"  \" + syntax\n      println\n      success = true\n    endIf\n    local description = get_description( command )\n    if (description)\n      description .= replacing(\"<br>\",\"\\n\")\n      local max_w = Console.width - 2\n      println \"DESCRIPTION\"\n      forEach (line in LineReader(description.word_wrapped(max_w)))\n        print( \"  \" )\n        println( line )\n      endForEach\n      println\n      success = true\n    endIf\n    if (success)\n      return\n    else\n      local w = Console.width.or_smaller( 80 )\n      println \"=\" * w\n      println \"ERROR: No such command '$'.\" (command)\n      println \"=\" * w\n      println\n    endIf\n  endIf\n\n  println \"USAGE\"\n  local entries = CommandInfo[]\n  local max_len = 0\n  forEach (m in <<Routine>>.global_methods)\n    if (m.name.begins_with(Build.rogo_prefix))\n      local name = m.name.after_first( Build.rogo_prefix )\n      local entry = CommandInfo( name, get_syntax(name), get_description(name) )\n      max_len .= or_larger( entry.syntax.count )\n      entries.add entry\n    endIf\n  endForEach\n\n  entries.sort( $1.name < $2.name )\n  max_len += 2\n\n  local max_w = Console.width\n  forEach (entry in entries)\n    print \"  \" + entry.syntax\n    if (entry.@description)\n      local description = entry.@description.before_first( '\\n' )\n      loop (max_len - entry.syntax.count) print ' '\n      contingent\n        sufficient (2 + max_len + description.count <= max_w)\n        if (description.contains(\". \"))\n          description = description.before_first( \". \" ) + \".\"\n          sufficient (max_len + description.count <= max_w)\n        endIf\n        necessary (max_len + 10 <= max_w)\n        description = description.unright( (description.count - (max_w - max_len))+5 ) + \"...\"\n      satisfied\n        print description\n      endContingent\n    endIf\n    println\n  endForEach\n  println\nendRoutine\n\nroutine get_syntax( m_name:String )->String\n  if (Build.rogo_syntax.contains(m_name))\n    return \"rogo \" + Build.rogo_syntax[ m_name ]\n  else\n    local m = <<Routine>>.find_global_method( Build.rogo_prefix + m_name )\n    if (not m) return null\n    local line = \"rogo $\" (m_name.replacing('_',' '))\n    line += \" <$>\" ((forEach in m.parameters).name)\n    return line\n  endIf\nendRoutine\n\n\nroutine get_description( m_name:String )->String\n  if (Build.rogo_descriptions.contains(m_name))\n    return Build.rogo_descriptions[ m_name ]\n  else\n    return null\n  endIf\nendRoutine\n\nclass CommandInfo( name:String, syntax:String, description:String );\n"
  },
  {
    "path": "ChangeLog.md",
    "content": "# GameGUI Change Log\n\n## v1.5 (October 18, 2023)\n\n### Text Scaling Bugfix\n- Scaling text nodes previously failed to update their text size on the first layout. This is fixed in v1.5.\n- If defined on any GameGUI node or child of a GameGUI node, the new `_on_resolve_size(available_size:Vector2)` event callback is called prior to resolving each component's size.\n- No action is required. However, a component can adjust its sizing mode, its layout size, and/or any other properties in this callback.\n- Scaling text components GGLabel, GGRichTextLabel, and GGButton formerly set their text size in `_on_update_size()`. However the reference nodes they use to determine their current font size most likely did not have their node sizes set on the first layout.\n- The scaling text components now update their font size in `_on_resolve_size()` instead because any nodes higher in the tree will have resolved their size.\n\n## v1.4.2 (October 15, 2023)\n\n### Structural Changes For Godot AssetLib\n\n- No actual code changes.\n- Version 1.4.1 was published on Godot Asset Library.\n- The 1.4.1 download is bloated with media files and there is no `.gdignore` inside the folder.\n- A mininmal plug-in branch has been created, called `addon`.\n- This `addon` branch will be the one linked to in the asset library.\n- `addon` v1.4.1 was submitted to AssetLib but it seems like the maintainers may not be willing to approve the commit number change without a corresponding version number bump.\n- Consequently this v1.4.2 release is in hopes of getting the compact version live on the asset library.\n\n## v1.4.1 (October 14, 2023)\n\n### [GGNinePatchRect](README.md#GGNinePatchRect)\nNew component GGNinePatchRect replicates NinePatchRect functionality and makes the following improvement: when the bounds of a GGNinePatchRect are smaller than its corners, the corners are proportionally shrunk to fit the available bounds.\n\n### [Safe Area Margin Parameters](README.md#Built-In-Parameters)\nBuilt-in parameters `safe_area_top_margin` (etc.) are now automatically set and maintained by GameGUI.\n\n## v1.3.1 (October 10, 2023)\n### GGMarginLayout fixes\n- Fixed margin calculation bugs (`x` <-> `y` errors in two spots).\n\n## v1.3 (September 23, 2023)\nAspect-Fit and Aspect-Fill sizing modes can now be mixed and matched, with e.g. one dimension using Fit and the other using Fill.\n\nThere are four possible aspect-mode combinations:\n\nHorizontal Mode | Vertical Mode | Effect\n----------------|---------------|-------\nAspect-Fit      | Aspect-Fit    | Component maintains the specified aspect ratio and is sized as large as possible while still fitting in the available area.\nAspect-Fill     | Aspect-Fill   | Component maintains the specified aspect ratio and is sized as small as possible while still completely filling the available area.\nAspect-Fill     | Aspect-Fit    | Component occupies all available width while maintaining the specified aspect ratio.\nAspect-Fit      | Aspect-Fill   | Component occupies all available height while maintaining the specified aspect ratio.\n\n## v1.2 (September 9, 2023)\n\n### GGOverlay supports scale > 1.0\n\nH&V Scale Constants can now be manually set to values > 1.0.\n\n### GGHBox and GGVBox Content Alignment <font color=red>[breaking change]</font>\n\nGGHBox and GGVBox now have a **Content Alignment** property which specifies how the content as a whole is aligned\nif it is larger than or smaller than the size of the box. For GGHBox the options are **Left**, **Center**, and **Right**.\nFor GGVBox the options are **Top**, **Center**, and **Bottom**.\n\nThis is a breaking change because the default for both is **Center** whereas the previous layout behavior matched\na default of **Left** for GGHBox or **Top** for GGVBox. If an existing GameGUI project has misaligned elements\nafter the update then there are GGHBox and GGVBox components that need their Content Alignment to be set to Left or Top.\n\n## v1.1 (September 5, 2023)\n\n### GGHBox and GGVBox more robust\n\nMore robust layout of Shrink-to-Fit children.\n\n- OLD:\n    - Shrink-to-Fit children counted as fixed size for layout purposes.\n    - Assumption was that STF children had fixed-size children.\n\n- NEW\n    - STF children given their maximum size during layout.\n    - STF children may base their size on the available space given to STF, which in turn affects STF final size.\n    - STF child sizes no longer prematurely assumed.\n\n## v1.0.1 (September 3, 2023)\n- Original release.\n"
  },
  {
    "path": "DemoProject/.gdignore",
    "content": "Godot will ignore this folder when installing the GameGUI addon.\n"
  },
  {
    "path": "DemoProject/Border.gd",
    "content": "@tool\n\nextends Control\n\nfunc _draw():\n\tdraw_rect( Rect2(position,size), Color(1,1,1), false, 1 )\n"
  },
  {
    "path": "DemoProject/DemoScene.tscn",
    "content": "[gd_scene load_steps=15 format=3 uid=\"uid://s72astt3jqi8\"]\n\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGInitialWindowSize.gd\" id=\"1_vk4tw\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGComponent.gd\" id=\"2_g0xq4\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bho7el5ufsu4h\" path=\"res://Background.png\" id=\"2_yy4fm\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGTextureRect.gd\" id=\"3_yjvjq\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGOverlay.gd\" id=\"5_1wg16\"]\n[ext_resource type=\"Texture2D\" uid=\"uid://bebonjfbutevo\" path=\"res://Godot.png\" id=\"6_a1gd3\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGLabel.gd\" id=\"7_7fgfl\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGVBox.gd\" id=\"7_u7o6r\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGFiller.gd\" id=\"8_evd4p\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGMarginLayout.gd\" id=\"9_jlyx2\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGHBox.gd\" id=\"11_y6opp\"]\n[ext_resource type=\"Script\" path=\"res://TextArea.gd\" id=\"12_kat07\"]\n[ext_resource type=\"Script\" path=\"res://addons/GameGUI/GGButton.gd\" id=\"13_a6138\"]\n[ext_resource type=\"Script\" path=\"res://Border.gd\" id=\"14_6xi7d\"]\n\n[node name=\"GGInitialWindowSize\" type=\"Container\"]\ntexture_filter = 4\noffset_right = 800.0\noffset_bottom = 600.0\nscript = ExtResource(\"1_vk4tw\")\ninitial_window_size = Vector2(800, 600)\nlayout_size = Vector2(1, 1)\n\n[node name=\"GGComponent - Size Ref\" type=\"Container\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"2_g0xq4\")\nhorizontal_mode = 1\nvertical_mode = 1\nlayout_size = Vector2(1, 1)\n\n[node name=\"BG\" type=\"Container\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"2_g0xq4\")\n\n[node name=\"GGTextureRect\" type=\"TextureRect\" parent=\"BG\"]\nlayout_mode = 2\ntexture = ExtResource(\"2_yy4fm\")\nexpand_mode = 1\nscript = ExtResource(\"3_yjvjq\")\nhorizontal_mode = 2\nvertical_mode = 2\nlayout_size = Vector2(960, 448)\nis_configured = true\n\n[node name=\"ColorRect\" type=\"ColorRect\" parent=\"BG\"]\nlayout_mode = 2\ncolor = Color(0, 0, 0, 0.266667)\n\n[node name=\"Overlays\" type=\"Container\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"2_g0xq4\")\n\n[node name=\"GGOverlay\" type=\"Container\" parent=\"Overlays\"]\nlayout_mode = 2\nscript = ExtResource(\"5_1wg16\")\nh_scale_constant = 0.3561\n\n[node name=\"GGTextureRect - Icon\" type=\"TextureRect\" parent=\"Overlays/GGOverlay\"]\nlayout_mode = 2\ntexture = ExtResource(\"6_a1gd3\")\nexpand_mode = 1\nscript = ExtResource(\"3_yjvjq\")\nhorizontal_mode = 1\nvertical_mode = 1\nlayout_size = Vector2(128, 128)\nis_configured = true\n\n[node name=\"GGOverlay2\" type=\"Container\" parent=\"Overlays\"]\nlayout_mode = 2\nscript = ExtResource(\"5_1wg16\")\nchild_y = 0.1515\nh_scale_constant = 0.3561\n\n[node name=\"GGLabel - GameGUI\" type=\"Label\" parent=\"Overlays/GGOverlay2\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 100\ntext = \"GameGUI\"\nhorizontal_alignment = 1\nscript = ExtResource(\"7_7fgfl\")\ntext_size_mode = 1\nreference_node = NodePath(\"../../../BG\")\nreference_node_height = 480\nreference_font_size = 80\nvertical_mode = 5\nlayout_size = Vector2(0, 137)\nis_configured = true\n\n[node name=\"GGOverlay3\" type=\"Container\" parent=\"Overlays\"]\nlayout_mode = 2\nscript = ExtResource(\"5_1wg16\")\nchild_y = 0.25\nh_scale_constant = 0.3561\n\n[node name=\"GGLabel - GGLabel\" type=\"Label\" parent=\"Overlays/GGOverlay3\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_colors/font_color = Color(1, 1, 1, 0.533333)\ntheme_override_font_sizes/font_size = 25\ntext = \"[GGOverlay + GGLabel]\"\nhorizontal_alignment = 1\nscript = ExtResource(\"7_7fgfl\")\ntext_size_mode = 1\nreference_node = NodePath(\"../../../BG\")\nreference_node_height = 480\nreference_font_size = 20\nvertical_mode = 5\nlayout_size = Vector2(0, 35)\nis_configured = true\n\n[node name=\"GGOverlay4\" type=\"Container\" parent=\"Overlays\"]\nlayout_mode = 2\nscript = ExtResource(\"5_1wg16\")\nchild_y = 0.6742\nh_scale_constant = 0.3561\n\n[node name=\"GGLabel - GGTextureRect\" type=\"Label\" parent=\"Overlays/GGOverlay4\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_colors/font_color = Color(1, 1, 1, 0.533333)\ntheme_override_font_sizes/font_size = 25\ntext = \"[GGOverlay + GGTextureRect]\"\nhorizontal_alignment = 1\nscript = ExtResource(\"7_7fgfl\")\ntext_size_mode = 1\nreference_node = NodePath(\"../../../BG\")\nreference_node_height = 480\nreference_font_size = 20\nvertical_mode = 5\nlayout_size = Vector2(0, 35)\nis_configured = true\n\n[node name=\"GGVBox - Text Area\" type=\"Container\" parent=\".\"]\nlayout_mode = 2\nscript = ExtResource(\"7_u7o6r\")\n\n[node name=\"GGFiller\" type=\"Container\" parent=\"GGVBox - Text Area\"]\nlayout_mode = 2\nsize_flags_stretch_ratio = 2.5\nmouse_filter = 2\nscript = ExtResource(\"8_evd4p\")\n\n[node name=\"GGMarginLayout - Outer\" type=\"Container\" parent=\"GGVBox - Text Area\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.0333333\nright_margin = 0.0333333\nbottom_margin = 0.0333333\nreference_node = NodePath(\"../../GGComponent - Size Ref\")\n\n[node name=\"GGMarginLayout - Inner\" type=\"Container\" parent=\"GGVBox - Text Area/GGMarginLayout - Outer\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.00833333\ntop_margin = 0.00833333\nright_margin = 0.00833333\nbottom_margin = 0.00833333\nreference_node = NodePath(\"../../../GGComponent - Size Ref\")\n\n[node name=\"GGHBox\" type=\"Container\" parent=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"11_y6opp\")\n\n[node name=\"GGRichTextLabel - Text Area\" type=\"RichTextLabel\" parent=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner/GGHBox\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_font_sizes/normal_font_size = 16\ntheme_override_font_sizes/bold_font_size = 16\ntheme_override_font_sizes/italics_font_size = 16\ntheme_override_font_sizes/bold_italics_font_size = 16\ntheme_override_font_sizes/mono_font_size = 16\nbbcode_enabled = true\ntext = \"GameGUI is a set of Godot Control nodes that provide alternative layout capabilities to the built-in Container classes. With Godot's built-in containers it is easy to make fixed-layout UI's that scale with the screen resolution OR dynamic (responsive) layouts with fixed-size controls (such as labels), but it can be difficult to make dynamic layouts with scaling controls. GameGUI solves the latter case. Click the buttons on the right for additional information.\"\nscript = ExtResource(\"12_kat07\")\ntext_size_mode = 1\nreference_node = NodePath(\"..\")\nreference_node_height = 145\nreference_font_sizes = {\n\"bold\": 16,\n\"bold_italics\": 16,\n\"italics\": 16,\n\"mono\": 16,\n\"normal\": 16\n}\nlayout_size = Vector2(750, 115)\nis_configured = true\n\n[node name=\"ReferenceRect - Border\" type=\"ReferenceRect\" parent=\"GGVBox - Text Area/GGMarginLayout - Outer\"]\nlayout_mode = 2\nmouse_filter = 2\nborder_color = Color(1, 1, 1, 1)\neditor_only = false\n\n[node name=\"GGVBox2 - Side Buttons\" type=\"Container\" parent=\".\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"7_u7o6r\")\n\n[node name=\"GGFiller\" type=\"Container\" parent=\"GGVBox2 - Side Buttons\"]\nlayout_mode = 2\nsize_flags_stretch_ratio = 0.84\nscript = ExtResource(\"8_evd4p\")\n\n[node name=\"GGHBox\" type=\"Container\" parent=\"GGVBox2 - Side Buttons\"]\nlayout_mode = 2\nscript = ExtResource(\"11_y6opp\")\n\n[node name=\"GGFiller\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox\"]\nlayout_mode = 2\nscript = ExtResource(\"8_evd4p\")\n\n[node name=\"GGVBox - Buttons\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox\"]\nlayout_mode = 2\nsize_flags_stretch_ratio = 0.33\nscript = ExtResource(\"7_u7o6r\")\n\n[node name=\"GGMarginLayout\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.0125\ntop_margin = 0.00416667\nright_margin = 0.0\nbottom_margin = 0.00416667\nreference_node = NodePath(\"../../../../GGComponent - Size Ref\")\n\n[node name=\"GGButton - Layout\" type=\"Button\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nsize_flags_horizontal = 4\nsize_flags_vertical = 4\ntheme_override_font_sizes/font_size = 17\ntext = \"Layout\"\nscript = ExtResource(\"13_a6138\")\ntext_size_mode = 1\nreference_node = NodePath(\"..\")\nreference_node_height = 36\nreference_font_size = 14\nis_configured = true\n\n[node name=\"Control\" type=\"Control\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout/GGButton - Layout\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\nscript = ExtResource(\"14_6xi7d\")\n\n[node name=\"GGMarginLayout2\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.0125\ntop_margin = 0.00416667\nright_margin = 0.0\nbottom_margin = 0.00416667\nreference_node = NodePath(\"../../../../GGComponent - Size Ref\")\n\n[node name=\"GGButton - Text\" type=\"Button\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout2\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 17\ntext = \"Text\"\nscript = ExtResource(\"13_a6138\")\ntext_size_mode = 1\nreference_node = NodePath(\"..\")\nreference_node_height = 36\nreference_font_size = 14\nis_configured = true\n\n[node name=\"Control2\" type=\"Control\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout2/GGButton - Text\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\nscript = ExtResource(\"14_6xi7d\")\n\n[node name=\"GGMarginLayout3\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.0125\ntop_margin = 0.00416667\nright_margin = 0.0\nbottom_margin = 0.00416667\nreference_node = NodePath(\"../../../../GGComponent - Size Ref\")\n\n[node name=\"GGButton - Images\" type=\"Button\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout3\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 17\ntext = \"Images\"\nscript = ExtResource(\"13_a6138\")\ntext_size_mode = 1\nreference_node = NodePath(\"..\")\nreference_node_height = 36\nreference_font_size = 14\nis_configured = true\n\n[node name=\"Control3\" type=\"Control\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout3/GGButton - Images\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\nscript = ExtResource(\"14_6xi7d\")\n\n[node name=\"GGMarginLayout4\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\nscript = ExtResource(\"9_jlyx2\")\nleft_margin = 0.0125\ntop_margin = 0.00416667\nright_margin = 0.0\nbottom_margin = 0.00416667\nreference_node = NodePath(\"../../../../GGComponent - Size Ref\")\n\n[node name=\"GGButton - Misc\" type=\"Button\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout4\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_font_sizes/font_size = 17\ntext = \"Misc\"\nscript = ExtResource(\"13_a6138\")\ntext_size_mode = 1\nreference_node = NodePath(\"../../GGMarginLayout3\")\nreference_node_height = 36\nreference_font_size = 14\nis_configured = true\n\n[node name=\"Control4\" type=\"Control\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout4/GGButton - Misc\"]\nlayout_mode = 1\nanchors_preset = 15\nanchor_right = 1.0\nanchor_bottom = 1.0\ngrow_horizontal = 2\ngrow_vertical = 2\nmouse_filter = 2\nscript = ExtResource(\"14_6xi7d\")\n\n[node name=\"GGLabel\" type=\"Label\" parent=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons\" node_paths=PackedStringArray(\"reference_node\")]\nlayout_mode = 2\ntheme_override_colors/font_color = Color(1, 1, 1, 0.533333)\ntheme_override_font_sizes/font_size = 18\ntext = \"[GGButtons]\"\nhorizontal_alignment = 1\nscript = ExtResource(\"7_7fgfl\")\ntext_size_mode = 1\nreference_node = NodePath(\"..\")\nreference_node_height = 179\nreference_font_size = 16\nvertical_mode = 5\nlayout_size = Vector2(0, 26)\nis_configured = true\n\n[node name=\"GGFiller2\" type=\"Container\" parent=\"GGVBox2 - Side Buttons/GGHBox\"]\nlayout_mode = 2\nscript = ExtResource(\"8_evd4p\")\nhorizontal_mode = 3\nlayout_size = Vector2(0.025, 1)\n\n[node name=\"GGFiller2\" type=\"Container\" parent=\"GGVBox2 - Side Buttons\"]\nlayout_mode = 2\nmouse_filter = 2\nscript = ExtResource(\"8_evd4p\")\n\n[connection signal=\"pressed\" from=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout/GGButton - Layout\" to=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner/GGHBox/GGRichTextLabel - Text Area\" method=\"_on_gg_button__layout_pressed\"]\n[connection signal=\"pressed\" from=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout2/GGButton - Text\" to=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner/GGHBox/GGRichTextLabel - Text Area\" method=\"_on_gg_button__text_pressed\"]\n[connection signal=\"pressed\" from=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout3/GGButton - Images\" to=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner/GGHBox/GGRichTextLabel - Text Area\" method=\"_on_gg_button__images_pressed\"]\n[connection signal=\"pressed\" from=\"GGVBox2 - Side Buttons/GGHBox/GGVBox - Buttons/GGMarginLayout4/GGButton - Misc\" to=\"GGVBox - Text Area/GGMarginLayout - Outer/GGMarginLayout - Inner/GGHBox/GGRichTextLabel - Text Area\" method=\"_on_gg_button__misc_pressed\"]\n"
  },
  {
    "path": "DemoProject/LayoutConfig.gd",
    "content": "@tool\n\nextends GGLayoutConfig\n\nfunc _on_begin_layout( display_size:Vector2 ):\n\tset_parameter( \"half_width\", int(display_size.x/2) )\n\tset_parameter( \"half_height\", int(display_size.y/2) )\n\n"
  },
  {
    "path": "DemoProject/TextArea.gd",
    "content": "@tool\n\nextends GGRichTextLabel\n\nfunc _on_gg_button__layout_pressed():\n\ttext = \"[b]GGComponent[/b] - GameGUI base node type. Useful as container, sizer, filler, spacer. Lays out children in a layered stack.\\n\" \\\n\t     + \"[b]GGHBox[/b] - Lays out children in a horizontal row.\\n\" \\\n\t     + \"[b]GGVBox[/b] - Lays out children in a vertical column.\\n\" \\\n\t     + \"[b]GGInitialWindowSize[/b] - IF it is the root node of a scene, sets the window size to its own size on launch. Useful for testing independent UI component scenes at typical aspect ratios.\\n\" \\\n\t     + \"[b]GGMarginLayout[/b] - Adds inside margins to the layout of its child content.\\n\" \\\n\t     + \"[b]GGOverlay[/b] - Positions its child content arbitrarily within its layout area in a sprite-like manner.\\n\" \\\n\t     + \"[b]GGLimitedSizeComponent[/b] - Applies minimum and/or maximum sizes to its child content.\\n\" \\\n\t     + \"[b]GGFiller[/b] - A GGComponent with an icon and default name that indicates it's purpose is to fill up extra space.\"\n\nfunc _on_gg_button__text_pressed():\n\ttext = \"[b]GGLabel[/b] - A Label that can auto-scale its text size.\\n\" \\\n\t     + \"[b]GGRichTextLabel[/b] - A RichTextLabel that can auto-scale its text size.\\n\" \\\n\t     + \"[b]GGButton[/b] - A Button that can auto-scale its text size.\\n\"\n\nfunc _on_gg_button__images_pressed():\n\ttext = \"[b]GGTextureRect[/b] - A TextureRect that uses the GameGUI layout system and automatically configures itself with appropriate defaults.\\n\"\n\nfunc _on_gg_button__misc_pressed():\n\ttext = \"[b]GGLayoutConfig[/b] - Place this near the root of the scene, extend the script, make it a @tool, override [code]func _on_begin_layout(display_size:Vector2)[/code], and call [code]set_parameter(name,value)[/code] with various computed values related to the current display size. Those parameters can be automatically used by other GameGUI nodes by setting their sizing mode to [b]Parameter[/b] and supplying the desired parameter name.\\n\" \\\n\t     + \"[b]GGParameterSetter[/b] - Sets specified subtree parameters to its own width and/or height, which can then be used to set the size of other components when they're in [b]Parameter[/b] sizing mode.\\n\\n\" \\\n\t     + \"Any Godot control node can be the child of a GameGUI component. They are automatically scaled to fit available width and height. Their size can be controlled by wrapping them in various GameGUI components.\\n\\n\"\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGButton.gd",
    "content": "@tool\n\nclass_name GGButton\nextends Button\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n## Check to lock in the current font size and reference node height as reference\n## values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = floor(reference_node.size.y)\n\t\t\t\treference_font_size = get_theme_font_size( \"font_size\" )\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n\t\trequest_layout()\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [Button] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of the font.\n@export var reference_font_size := 0 :\n\tset(value):\n\t\tif reference_font_size == value: return\n\t\treference_font_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use when [member text_size_mode] is [b]Parameter[/b].\n@export var text_size_parameter:String = \"\" :\n\tset(value):\n\t\tif text_size_parameter == value: return\n\t\ttext_size_parameter = value\n\t\tif has_parameter( text_size_parameter ):\n\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_size := 0\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tvar cur_size = get_theme_font_size( \"font_size\" )\n\t\t\tif cur_size != _current_font_size:\n\t\t\t\t_current_font_size = cur_size\n\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif _current_font_size != reference_font_size:\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\trequest_layout()\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\tif text == \"\": text = \"Button\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\t_current_font_size = get_theme_font_size( \"font_size\" )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of the font to dynamically size it\n\t\t\t\tvar cur_size = reference_font_size * cur_scale\n\t\t\t\tif cur_size:\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGComponent.gd",
    "content": "@tool\n\n## General-purpose layout box and base class to other GameGUI layout components. Children are\n## stacked in layers.\n##\n## Nodes that do not extend [GGComponent] can still be children of GameGUI nodes in one of two\n## ways.[br][br]\n## First, GameGUI adapter code can be added to an extended class. See the [GGTextureRect] source\n## code for an example that can be copy-pasted into other extended classes with only small\n## modifications required.[br][br]\n## Second, any non-[GGComponent] Control node that does not contain GameGUI adapter code will be\n## treated as a component with [member horizontal_mode] and [member vertical_mode] both set to\n## [b]Expand To Fill[/b]. The size of the node can be further managed by making it a child of\n## a [GGComponent] that has other sizing modes.[br][br]\nclass_name GGComponent\nextends Container\n\n#-------------------------------------------------------------------------------\n# SIGNALS\n#-------------------------------------------------------------------------------\n\n## Signals the beginning of the layout process for a GGComponent subtree.\n## This signal is only emitted by the top-level root of a GGComponent subtree.\nsignal begin_layout\n\n## Signals the end of the layout process for a GGComponent subtree.\n## This signal is only emitted by the top-level root of a GGComponent subtree.\nsignal end_layout\n\n#-------------------------------------------------------------------------------\n# ENUMS\n#-------------------------------------------------------------------------------\n\n## The possible horizontal and vertical sizing modes for a GameGUI component.\nenum ScalingMode\n{\n\tEXPAND_TO_FILL, ## Fill all available space along this dimension.\n\tASPECT_FIT,     ## Dynamically adjusts size to maintain aspect ratio [member layout_size].x:[member layout_size].y, just small enough to entirely fit available space.\n\tASPECT_FILL,    ## Dynamically adjusts size to maintain aspect ratio [member layout_size].x:[member layout_size].y, just large enough to entirely fill available space.\n\tPROPORTIONAL,   ## The layout size represents a proportional fraction of 1) the available area or 2) the size of the [member reference_node] if defined.\n\tSHRINK_TO_FIT,  ## Make the size just large enough to contain all child nodes in their layout.\n\tFIXED,          ## Fixed pixel size along this dimension.\n\tPARAMETER       ## One of the subtree [member parameters] is used as the size.\n}\n\n## The text sizing mode for [GGLabel], [GGRichTextLabel], and [GGButton].\nenum TextSizeMode\n{\n\tDEFAULT,    ## Text size is whatever size you assign in the editor.\n\tSCALE,      ## Text scales with the size of a reference node.\n\tPARAMETER   ## Text size is set to the value of one of the defined [member parameters].\n}\n\n## The texture fill mode used by [method fill_texture].\nenum FillMode\n{\n\tSTRETCH,  # Stretch or compress each patch to cover the available space.\n\tTILE,     # Repeatedly tile each patch at its original pixel size to cover the available space.\n\tTILE_FIT  # Tile each patche, stretching slightly as necessary to ensure a whole number of tiles fit in the available space.\n}\n\n\n#-------------------------------------------------------------------------------\n# PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## An optional node to use as a size reference for [b]Proportional[/b] scaling\n## mode. The reference node must be in a subtree higher in the scene tree than\n## this node. Often the size reference is an invisible root-level square-aspect\n## component; this allows same-size horizontal and vertical proportional spacers.\n@export var reference_node:Control = null :\n\tset(value):\n\t\tif reference_node != value:\n\t\t\treference_node = value\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Parameter definitions for nodes that use scaling mode PARAMETER. Parameters are stored\n## the root of a GGComponent subtree. Use [method get_parameter], [method has_parameter], and\n## [method set_parameter] to access parameters from any subtree nodes.\n@export var parameters := {} :\n\tset(value):\n\t\tif parameters == value: return\n\t\tparameters = value\n\t\trequest_layout()\n\n# A top-level GGComponent is one that has no GGComponent parent.\n# It oversees the layout of its descendent nodes.\nvar _is_top_level := false\nvar _layout_stage := 0 # top-level component use. 0=layout finished, 1=layout requested, 2=performing layout\n\n#-------------------------------------------------------------------------------\n# EXTERNAL API\n#-------------------------------------------------------------------------------\n\n## Utility method that draws a texture with any combination of horizontal and vertical fill modes: Stretch, Tile, Tile Fit.\n## Used primarily by [GGComponent].\nfunc fill_texture( texture:Texture2D, dest_rect:Rect2, src_rect:Rect2, horizontal_fill_mode:FillMode=FillMode.STRETCH,\n\t\tvertical_fill_mode:FillMode=FillMode.STRETCH, modulate:Color=Color(1,1,1,1) ):\n\tif dest_rect.size.x <= 0 or dest_rect.size.y <= 0: return\n\n\tif horizontal_fill_mode == FillMode.TILE and src_rect.size.x > dest_rect.size.x:\n\t\thorizontal_fill_mode = FillMode.TILE_FIT\n\n\tif vertical_fill_mode == FillMode.TILE and src_rect.size.y > dest_rect.size.y:\n\t\tvertical_fill_mode = FillMode.TILE_FIT\n\n\tmatch horizontal_fill_mode:\n\t\tFillMode.TILE:\n\t\t\tvar tile_size = src_rect.size\n\t\t\tvar dest_pos  = dest_rect.position\n\t\t\tvar dest_w = dest_rect.size.x\n\t\t\tvar dest_h = dest_rect.size.y\n\t\t\twhile dest_w > 0:\n\t\t\t\tif tile_size.x <= dest_w:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(tile_size.x,dest_h) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\telse:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\tvar _src_rect = Rect2( src_rect.position, Vector2(dest_w,src_rect.size.y) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, _src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\treturn\n\n\t\t\t\tdest_pos += Vector2( tile_size.x, 0 )\n\t\t\t\tdest_w   -= tile_size.x\n\t\t\treturn\n\n\t\tFillMode.TILE_FIT:\n\t\t\tvar n = int( (dest_rect.size.x / src_rect.size.x) + 0.5 )\n\t\t\tif n == 0:\n\t\t\t\tfill_texture( texture, dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\telse:\n\t\t\t\tvar tile_size = Vector2( dest_rect.size.x / n, src_rect.size.y )\n\t\t\t\tvar dest_pos  = dest_rect.position\n\t\t\t\tvar dest_w = dest_rect.size.x\n\t\t\t\tvar dest_h = dest_rect.size.y\n\t\t\t\twhile dest_w > 0:\n\t\t\t\t\tif tile_size.x <= dest_w:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(tile_size.x,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\telse:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\t\treturn\n\n\t\t\t\t\tdest_pos += Vector2( tile_size.x, 0 )\n\t\t\t\t\tdest_w   -= tile_size.x\n\t\t\treturn\n\n\tmatch vertical_fill_mode:\n\t\tFillMode.TILE:\n\t\t\tvar tile_size = src_rect.size\n\t\t\tvar dest_pos  = dest_rect.position\n\t\t\tvar dest_w = dest_rect.size.x\n\t\t\tvar dest_h = dest_rect.size.y\n\t\t\twhile dest_h > 0:\n\t\t\t\tif tile_size.y <= dest_h:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w, tile_size.y) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\telse:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\tvar _src_rect = Rect2( src_rect.position, Vector2(src_rect.size.x,dest_h) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, _src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\treturn\n\n\t\t\t\tdest_pos += Vector2( 0, tile_size.y )\n\t\t\t\tdest_h   -= tile_size.y\n\t\t\treturn\n\n\t\tFillMode.TILE_FIT:\n\t\t\tvar n = int( (dest_rect.size.y / src_rect.size.y) + 0.5 )\n\t\t\tif n == 0:\n\t\t\t\tfill_texture( texture, dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\telse:\n\t\t\t\tvar tile_size = Vector2( src_rect.size.x, dest_rect.size.y / n )\n\t\t\t\tvar dest_pos  = dest_rect.position\n\t\t\t\tvar dest_w = dest_rect.size.x\n\t\t\t\tvar dest_h = dest_rect.size.y\n\t\t\t\twhile dest_h > 0:\n\t\t\t\t\tif tile_size.y <= dest_h:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w, tile_size.y) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\telse:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\t\treturn\n\n\t\t\t\t\tdest_pos += Vector2( 0, tile_size.y )\n\t\t\t\t\tdest_h   -= tile_size.y\n\t\t\treturn\n\n\t# Horizontal and vertical fill are both STRETCH\n\tdraw_texture_rect_region( texture, dest_rect, src_rect, modulate )\n\n## Returns the specified parameter's value if it exists in the [member parameters]\n## of this node or a [GGComponent] ancestor. If it doesn't exist, returns\n## [code]0[/code] or a specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this GGComponent subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in the\n## [member parameters] of this node or one of its ancestors.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tif _is_top_level:\n\t\tif _layout_stage == 0:\n\t\t\t_layout_stage = 1\n\t\t\tqueue_sort()\n\telse:\n\t\tvar top = get_top_level_component()\n\t\tif top: top.request_layout()\n\n#-------------------------------------------------------------------------------\n# KEY OVERRIDES\n#-------------------------------------------------------------------------------\nfunc _on_resolve_size( available_size:Vector2 ):\n\t# Overrideable.\n\t# Called just before this component's size is resolved.\n\t# Override and adjust this component's size if desired.\n\tpass\n\nfunc _on_update_size():\n\t# Overrideable.\n\t# Called at the beginning of layout.\n\t# Override and adjust this GGComponent's size if desired.\n\tpass\n\nfunc _perform_child_layout( available_bounds:Rect2 ):\n\tfor i in range(get_child_count()):\n\t\t_perform_component_layout( get_child(i), available_bounds )\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\tfor i in range(get_child_count()):\n\t\t_resolve_child_size( get_child(i), available_size, limited )\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\t# Override in extended classes.\n\tsize.y = _get_largest_child_size().y\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\t# Override in extended classes.\n\tsize.x = _get_largest_child_size().x\n\nfunc _with_margins( rect:Rect2 )->Rect2:\n\treturn rect\n\n#-------------------------------------------------------------------------------\n# INTERNAL GAMEGUI API\n#-------------------------------------------------------------------------------\n\nfunc _get_largest_child_size()->Vector2:\n\t# 'x' and 'y' will possibly come from different children.\n\tvar max_w := 0.0\n\tvar max_h := 0.0\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:  # includes GGComponent\n\t\t\tmax_w = max( max_w, child.size.x )\n\t\t\tmax_h = max( max_h, child.size.y )\n\treturn Vector2(max_w,max_h)\n\nfunc _get_sum_of_child_sizes()->Vector2:\n\tvar sum := Vector2(0,0)\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tsum += child.size\n\treturn sum\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\tvar bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\t_perform_child_layout( bounds )\n\nfunc _place_component( component:Control, available_bounds:Rect2 ):\n\tcomponent.position = _rect_position_within_parent_bounds( component, component.size, available_bounds )\n\nfunc _rect_position_within_parent_bounds( component:Control, rect_size:Vector2, available_bounds:Rect2 )->Vector2:\n\tvar pos = available_bounds.position\n\n\tif component is Control:  # includes GGComponent\n\t\tif component.size_flags_horizontal & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tpos.x += floor( (available_bounds.size.x - rect_size.x) / 2 )\n\t\telif component.size_flags_horizontal & SizeFlags.SIZE_SHRINK_END:\n\t\t\tpos.x += available_bounds.size.x - rect_size.x\n\n\t\tif component.size_flags_vertical & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tpos.y += floor( (available_bounds.size.y - rect_size.y) / 2 )\n\t\telif component.size_flags_vertical & SizeFlags.SIZE_SHRINK_END:\n\t\t\tpos.y += available_bounds.size.y - rect_size.y\n\n\treturn pos\n\nfunc _resolve_child_size( child:Node, available_size:Vector2, limited:bool=false ):\n\tif not child.visible or not child is Control: return\n\n\tif child is GGComponent:\n\t\tchild._resolve_size( available_size, limited )\n\telse:\n\t\t_resolve_component_size( child, available_size )\n\t\t_resolve_shrink_to_fit_size( child, available_size )\n\nfunc _resolve_component_size( component:Node, available_size:Vector2 )->Vector2:\n\tvar component_size := available_size\n\n\tvar is_gg = component is GGComponent\n\n\tif is_gg or component.has_method(\"_on_resolve_size\"):\n\t\tcomponent._on_resolve_size( available_size )\n\n\tvar has_mode = is_gg or component.has_method(\"request_layout\")\n\tvar h_mode = component.horizontal_mode if has_mode else ScalingMode.EXPAND_TO_FILL\n\tvar v_mode = component.vertical_mode if has_mode else ScalingMode.EXPAND_TO_FILL\n\n\tmatch h_mode:\n\t\tScalingMode.EXPAND_TO_FILL:\n\t\t\tpass # use available width\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif not component is GGComponent: component_size.x = component.size.x\n\t\tScalingMode.ASPECT_FIT:\n\t\t\tif v_mode == ScalingMode.ASPECT_FILL:\n\t\t\t\tcomponent_size.x = floor( (available_size.y / component.layout_size.y) * component.layout_size.x )\n\t\t\telse:\n\t\t\t\tvar fit_x = floor( (available_size.y / component.layout_size.y) * component.layout_size.x )\n\t\t\t\tif fit_x <= available_size.x: component_size.x = fit_x\n\t\tScalingMode.ASPECT_FILL:\n\t\t\tif v_mode != ScalingMode.ASPECT_FIT:\n\t\t\t\tvar scale_x = (available_size.x / component.layout_size.x)\n\t\t\t\tvar scale_y = (available_size.y / component.layout_size.y)\n\t\t\t\tcomponent_size.x = floor( max(scale_x,scale_y) * component.layout_size.x )\n\t\tScalingMode.FIXED:\n\t\t\tcomponent_size.x = component.layout_size.x\n\t\tScalingMode.PARAMETER:\n\t\t\tcomponent_size.x = get_parameter( component.width_parameter, component.layout_size.x )\n\t\tScalingMode.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tcomponent_size.x = int(component.layout_size.x * reference_node.size.x)\n\t\t\telse:\n\t\t\t\tcomponent_size.x = int(component.layout_size.x * available_size.x)\n\n\tmatch v_mode:\n\t\tScalingMode.EXPAND_TO_FILL:\n\t\t\tpass # use available height\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif not component is GGComponent: component_size.y = component.size.y\n\t\tScalingMode.ASPECT_FIT:\n\t\t\tif h_mode == ScalingMode.ASPECT_FILL:\n\t\t\t\tcomponent_size.y = floor( (available_size.x / component.layout_size.x) * component.layout_size.y )\n\t\t\telse:\n\t\t\t\tvar fit_y = floor( (available_size.x / component.layout_size.x) * component.layout_size.y )\n\t\t\t\tif fit_y <= available_size.y: component_size.y = fit_y\n\t\tScalingMode.ASPECT_FILL:\n\t\t\tif h_mode != ScalingMode.ASPECT_FIT:\n\t\t\t\tvar scale_x = (available_size.x / component.layout_size.x)\n\t\t\t\tvar scale_y = (available_size.y / component.layout_size.y)\n\t\t\t\tcomponent_size.y = floor( max(scale_x,scale_y) * component.layout_size.y )\n\t\tScalingMode.FIXED:\n\t\t\tcomponent_size.y = component.layout_size.y\n\t\tScalingMode.PARAMETER:\n\t\t\tcomponent_size.y = get_parameter( component.height_parameter, component.layout_size.y )\n\t\tScalingMode.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tcomponent_size.y = int(component.layout_size.y * reference_node.size.y)\n\t\t\telse:\n\t\t\t\tcomponent_size.y = int(component.layout_size.y * available_size.y)\n\n\tif not component is GGComponent or not component._is_top_level: component.size = component_size\n\n\treturn component_size\n\nfunc _perform_component_layout( component:Node, available_bounds:Rect2 ):\n\tif component is GGComponent:\n\t\tcomponent._perform_layout(available_bounds)\n\n\telif component is Control:\n\t\t_place_component( component, available_bounds )\n\nfunc _resolve_shrink_to_fit_size( component:Node, available_size:Vector2 )->Vector2:\n\tif not (component is GGComponent or component.has_method(\"request_layout\")): return available_size\n\n\tvar component_size := available_size\n\n\tmatch component.horizontal_mode:\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif component is GGComponent:\n\t\t\t\t_resolve_shrink_to_fit_width( available_size )\n\t\t\telse:\n\t\t\t\tcomponent_size.x = component.size.x\n\n\tmatch component.vertical_mode:\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif component is GGComponent:\n\t\t\t\t_resolve_shrink_to_fit_height( available_size )\n\t\t\telse:\n\t\t\t\tcomponent_size.y = component.size.y\n\n\treturn component_size\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\t# limited\n\t#   Don't recurse to children unless necessary (Shrink to Fit mode). limited==true indicates\n\t#   that several sizing options are being checked by GGHBox/GGVBox/etc., so we only need\n\t#   to figure out the size of this node.\n\tavailable_size = _resolve_component_size( self, available_size )\n\tvar inner_size = _with_margins( Rect2(Vector2(0,0), available_size) ).size\n\tif not limited or ScalingMode.SHRINK_TO_FIT in [horizontal_mode,vertical_mode]:\n\t\t_resolve_child_sizes( inner_size, limited )\n\t\t_resolve_shrink_to_fit_size( self, available_size )\n\nfunc _update_layout():\n\tif not _is_top_level or _layout_stage == 2: return\n\t_layout_stage = 2\n\n\t_update_safe_area()\n\n\tbegin_layout.emit()\n\n\t_update_size()\n\t_resolve_size( size )\n\t_perform_layout( _with_margins(Rect2(Vector2(0,0),size)) )\n\n\tend_layout.emit()\n\t_layout_stage = 0\n\nfunc _update_size():\n\t_on_update_size()\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is GGComponent:\n\t\t\tchild._update_size()\n\t\telif child.has_method(\"_on_update_size\"):\n\t\t\tchild._on_update_size()\n\n#-------------------------------------------------------------------------------\n# INTERNAL NODE API\n#-------------------------------------------------------------------------------\nfunc _disconnect( sig:Signal, callback:Callable ):\n\tif sig.is_connected(callback):\n\t\tsig.disconnect( callback )\n\nfunc _enter_tree():\n\t_is_top_level = not (get_parent() is GGComponent)\n\tif _is_top_level:\n\t\tresized.connect( request_layout )\n\t\tsort_children.connect( _on_sort_children )\n\t\t_update_safe_area()\n\n\tchild_entered_tree.connect( _on_child_entered_tree )\n\tchild_exiting_tree.connect( _on_child_exiting_tree )\n\tchild_order_changed.connect( request_layout )\n\n\trequest_layout()\n\nfunc _exit_tree():\n\tif _is_top_level:\n\t\t_disconnect( resized, request_layout )\n\t\t_disconnect( sort_children, _on_sort_children )\n\n\t_disconnect( child_entered_tree, _on_child_entered_tree )\n\t_disconnect( child_exiting_tree, _on_child_exiting_tree )\n\t_disconnect( child_order_changed, request_layout )\n\n\trequest_layout()\n\nfunc _on_child_entered_tree( child:Node ):\n\tif child is Control: child.visibility_changed.connect( request_layout )\n\tif Engine.is_editor_hint() and child is Control:\n\t\tchild.minimum_size_changed.connect( request_layout )\n\t\tchild.resized.connect( request_layout )\n\t\tchild.size_flags_changed.connect( request_layout )\n\n\trequest_layout()\n\nfunc _on_child_exiting_tree( child:Node ):\n\tif child is Control: _disconnect( child.visibility_changed, request_layout )\n\tif Engine.is_editor_hint() and child is Control:\n\t\t_disconnect( child.minimum_size_changed, request_layout )\n\t\t_disconnect( child.resized, request_layout )\n\t\t_disconnect( child.size_flags_changed, request_layout )\n\n\trequest_layout()\n\nfunc _on_child_visibility_changed():\n\trequest_layout()\n\nfunc _on_sort_children():\n\t_update_layout()\n\nfunc _update_safe_area():\n\tvar viewport = get_viewport()\n\tif viewport:\n\t\tvar display_size = viewport.get_visible_rect().size\n\t\tvar safe_area = Rect2( Vector2(0,0), display_size )\n\n\t\tmatch DisplayServer.window_get_mode():\n\t\t\tDisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN, \\\n\t\t\tDisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN:\n\t\t\t\tsafe_area = DisplayServer.get_display_safe_area()\n\n\t\tset_parameter( \"safe_area_left_margin\", safe_area.position.x )\n\t\tset_parameter( \"safe_area_top_margin\", safe_area.position.y )\n\t\tset_parameter( \"safe_area_right_margin\", display_size.x - safe_area.end.x )\n\t\tset_parameter( \"safe_area_bottom_margin\", display_size.y - safe_area.end.y )\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGFiller.gd",
    "content": "@tool\n\n## A GameGUI node that by default will expand to fill any extra space within a layout.\n## There is no functional difference between [GGFiller] and [GGComponent], but the name [GGFiller]\n## can make a node tree more readable.\nclass_name GGFiller\nextends GGComponent\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGHBox.gd",
    "content": "@tool\n\n## A GameGUI layout that arranges its child elements in a horizontal row.\nclass_name GGHBox\nextends GGComponent\n\nenum HorizontalContentAlignment\n{\n\tLEFT,    ## Left-align the content.\n\tCENTER,  ## Center the content.\n\tRIGHT    ## Right-align the content.\n}\n\n## Specify the horizontal alignment of the content as a whole.\n@export var content_alignment := HorizontalContentAlignment.CENTER :\n\tset(value):\n\t\tcontent_alignment = value\n\t\trequest_layout()\n\nvar _min_widths:Array[int] = []\nvar _max_widths:Array[int] = []\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\t# Resolve for and collect min and max sizes\n\t_max_widths.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, available_size, true )\n\t\t\t_max_widths.push_back( int(child.size.x) )\n\t\telse:\n\t\t\t_max_widths.push_back( 0 )\n\n\t_min_widths.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, Vector2(0,available_size.y), true )\n\t\t\t_min_widths.push_back( int(child.size.x) )\n\t\telse:\n\t\t\t_min_widths.push_back( 0 )\n\n\tvar expand_count := 0\n\tvar total_stretch_ratio := 0.0\n\tvar fixed_width := 0\n\tvar min_width := 0\n\n\t# Leaving other children at their minimum width, set aspect-fit, proportional,\n\t# and shrink-to-fit width nodes to their maximum size.\n\tvar modes = [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.SHRINK_TO_FIT]\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\n\t\tvar has_mode = child is GGComponent or child.has_method(\"request_layout\")\n\t\tif has_mode and child.horizontal_mode in modes:\n\t\t\t_resolve_child_size( child, available_size, limited )\n\t\t\tvar w = int(child.size.x)\n\t\t\tmin_width += w\n\t\t\tfixed_width += w\n\t\t\t_min_widths[i] = w\n\t\t\t_max_widths[i] = w\n\n\t\telse:\n\t\t\tvar w = _min_widths[i]\n\t\t\tmin_width += w\n\n\t\t\tif _min_widths[i] == _max_widths[i]:\n\t\t\t\tfixed_width += w\n\t\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\t\t\telse:\n\t\t\t\texpand_count += 1\n\t\t\t\ttotal_stretch_ratio += child.size_flags_stretch_ratio\n\n\tif expand_count == 0 or total_stretch_ratio == 0.0 or min_width >= available_size.x: return\n\n\tvar excess_width = int(available_size.x - fixed_width)\n\tvar remaining_width = excess_width\n\n\t# Find children with a min width larger than their portion. Let them keep their min width and adjust remaining.\n\tvar remaining_total_stretch_ratio = total_stretch_ratio\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif w < _min_widths[i]:\n\t\t\tw = _min_widths[i]\n\t\t\tremaining_width -= w\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_widths[i] = _max_widths[i]  # skip this node in the next pass\n\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\n\texcess_width = remaining_width\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# Distribute remaining width next to children with a max width smaller than their portion.\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif w > _max_widths[i]:\n\t\t\tw = _max_widths[i]\n\t\t\t_resolve_child_size( child, Vector2(w,available_size.y), limited )\n\t\t\tremaining_width -= w\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_widths[i] = _max_widths[i]  # skip this node in the next pass\n\n\texcess_width = remaining_width\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# If this GGHBox is shrink-to-fit width then we're done; don't add remaining space to\n\t# the children.\n\tif horizontal_mode == GGComponent.ScalingMode.SHRINK_TO_FIT:\n\t\treturn\n\n\t# Distribute remaining width\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\t_resolve_child_size( child, Vector2(w,available_size.y), limited )\n\t\tremaining_width -= w\n\t\texpand_count -= 1\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\tsize.x = _get_sum_of_child_sizes().x\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\tsize.y = _get_largest_child_size().y\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\n\tvar inner_bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\tvar pos = inner_bounds.position\n\tvar sz = inner_bounds.size\n\n\tvar diff = sz.x - _get_sum_of_child_sizes().x\n\tmatch content_alignment:\n\t\tHorizontalContentAlignment.LEFT:   pass\n\t\tHorizontalContentAlignment.CENTER: pos.x += int(diff/2.0)\n\t\tHorizontalContentAlignment.RIGHT:  pos.x += diff\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:\n\t\t\t_perform_component_layout( child, Rect2(pos,Vector2(child.size.x,sz.y)) )\n\t\t\tpos += Vector2( child.size.x, 0 )\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGInitialWindowSize.gd",
    "content": "@tool\n\n## A control that sets the initial window size to be its own size if it is the base\n## node of the scene. Facilitates testing child scenes individually with a customized aspect\n## ratio similar to what their bounds will be when placed in the parent scene.\nclass_name GGInitialWindowSize\nextends GGComponent\n\n## The desired initial size of the window. Only takes effect when this node is the base node\n## of a scene at runtime.\n@export var initial_window_size := Vector2(1280,720):\n\tset(value):\n\t\tif initial_window_size == value: return\n\t\tinitial_window_size = value\n\n\t\tvar viewport = get_viewport()\n\t\tif not viewport: return  # engine is setting the initial value of this property\n\t\tif not Engine.is_editor_hint() or viewport != get_parent(): return\n\n\t\tif value != size:\n\t\t\tsize = value\n\t\t\trequest_layout()\n\nfunc _ready():\n\tif get_parent() == get_viewport() and not Engine.is_editor_hint():\n\t\tDisplayServer.window_set_size( initial_window_size )\n\n\t\tvar screen_size = Vector2(DisplayServer.screen_get_size())\n\t\tvar pos = Vector2i( (screen_size-initial_window_size)/2.0 )\n\t\tDisplayServer.window_set_position( pos )\n\n\t\tset_anchors_and_offsets_preset( LayoutPreset.PRESET_FULL_RECT )\n\telse:\n\t\tinitial_window_size = Vector2i(size)\n\nfunc _enter_tree():\n\tsuper()\n\tif get_parent() == get_viewport():\n\t\tresized.connect( _on_resized )\n\nfunc _exit_tree():\n\tsuper()\n\tif resized.is_connected( _on_resized ):\n\t\tresized.disconnect( _on_resized )\n\nfunc _on_resized():\n\tif Engine.is_editor_hint(): initial_window_size = size\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGLabel.gd",
    "content": "@tool\n\n## A Label with expanded layout and scaling capabilities. Make this a child of an extended\n## GGComponent, not a standard Control or Container.\nclass_name GGLabel\nextends Label\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n## Check to lock in the current font size and reference node height as reference\n## values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = int(reference_node.size.y)\n\t\t\t\treference_font_size = get_theme_font_size( \"font_size\" )\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [Label] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n## Setting [member text_size_mode] to [b]SCALE[/b] will automatically set this property's value.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of the font. Setting [member text_size_mode] to [b]SCALE[/b] will\n## automatically set this property's value.\n@export var reference_font_size := 0 :\n\tset(value):\n\t\tif reference_font_size == value: return\n\t\treference_font_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use when [member text_size_mode] is [b]Parameter[/b].\n@export var text_size_parameter:String = \"\" :\n\tset(value):\n\t\tif text_size_parameter == value: return\n\t\ttext_size_parameter = value\n\t\tif has_parameter( text_size_parameter ):\n\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_size := 0\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tvar cur_size = get_theme_font_size( \"font_size\" )\n\t\t\tif cur_size != _current_font_size:\n\t\t\t\t_current_font_size = cur_size\n\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif _current_font_size != reference_font_size:\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\trequest_layout()\n\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\thorizontal_mode  = GGComponent.ScalingMode.EXPAND_TO_FILL\n\t\tvertical_mode = GGComponent.ScalingMode.FIXED\n\t\tif abs(layout_size.x) < 0.0001 and abs(layout_size.y) < 0.0001:\n\t\t\tlayout_size = Vector2( 0, get_line_count()*get_line_height() )\n\t\tif text == \"\": text = \"Label\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\t_current_font_size = get_theme_font_size( \"font_size\" )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of the font to dynamically size it\n\t\t\t\tvar cur_size = reference_font_size * cur_scale\n\t\t\t\tif cur_size:\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n\tlayout_size = Vector2( 0, get_line_count()*get_line_height() )\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGLayoutConfig.gd",
    "content": "@tool\n\n## Extend this node and override [method _on_begin_layout] to set GameGUI parameters\n## prior to each layout via [method set_parameter]. Ensure the extended node\n## begins with the [code]@tool[/code] annotation.\nclass_name GGLayoutConfig\nextends Node2D\n\nfunc _enter_tree():\n\tvar top = get_top_level_component()\n\tif top:\n\t\ttop.begin_layout.connect( _dispatch_on_begin_layout )\n\nfunc _exit_tree():\n\tvar top = get_top_level_component()\n\tif top:\n\t\ttop._disconnect( top.begin_layout, _dispatch_on_begin_layout )\n\nfunc _dispatch_on_begin_layout():\n\tvar viewport = get_viewport()\n\tif not viewport: return\n\n\tvar display_size = viewport.get_visible_rect().size\n\tvar top = get_top_level_component()\n\tif Engine.is_editor_hint() and top: display_size = top.size\n\n\t_on_begin_layout( display_size )\n\n## Layout for the [GGComponent] subtree this node is attached to is about to begin.\n## Override this method and set parameters or other configuration needs.\nfunc _on_begin_layout( display_size:Vector2 ):\n\tpass\n\n## Returns the specified parameter's value if it exists in the [member parameters]\n## of the [GGComponent] subtree this node is attached to. If it doesn't exist, returns\n## [code]0[/code] or a specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant )->Variant:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.get_parameter( parameter_name, default_result )\n\telse:   return default_result\n\n## Returns the root of the [GGComponent] subtree this node is attached to.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.get_top_level_component()\n\telse:   return null\n\n## Returns [code]true[/code] if the specified parameter exists in the\n## [member parameters] of the [GGComponent] subtree this node is attached to.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.has_parameter( parameter_name )\n\telse:   return false\n\n## Sets the named parameter's value in the top-level root of the [GGComponent]\n## subtree this node is attached to.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: cur.set_parameter( parameter_name, value )\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGLimitedSizeComponent.gd",
    "content": "@tool\n\n## A component that can limit its size to arbitrary minimum and/or maximum values.\nclass_name GGLimitedSizeComponent\nextends GGComponent\n\nenum LimitType\n{\n\tUNLIMITED,  ## No size limit.\n  FIXED,      ## A limited size in pixels.\n\tPARAMETER   ## A parameter defines the size limit in pixels.\n}\n\n@export_group(\"Minimum Size\")\n\n@export var min_width  := LimitType.UNLIMITED :\n\tset(value):\n\t\tif min_width == value: return\n\t\tmin_width = value\n\t\trequest_layout()\n\n@export var min_height := LimitType.UNLIMITED :\n\tset(value):\n\t\tif min_height == value: return\n\t\tmin_height = value\n\t\trequest_layout()\n\n@export var min_size := Vector2(0,0) :\n\tset(value):\n\t\tif min_size == value: return\n\t\tmin_size = value\n\t\trequest_layout()\n\n@export var min_width_parameter := \"\" :\n\tset(value):\n\t\tif min_width_parameter == value: return\n\t\tmin_width_parameter = value\n\t\trequest_layout()\n\n@export var min_height_parameter := \"\" :\n\tset(value):\n\t\tif min_height_parameter == value: return\n\t\tmin_height_parameter = value\n\t\trequest_layout()\n\n@export_group(\"Maximum Size\")\n\n@export var max_width  := LimitType.UNLIMITED :\n\tset(value):\n\t\tif max_width == value: return\n\t\tmax_width = value\n\t\trequest_layout()\n\n@export var max_height := LimitType.UNLIMITED :\n\tset(value):\n\t\tif max_height == value: return\n\t\tmax_height = value\n\t\trequest_layout()\n\n@export var max_size := Vector2(0,0) :\n\tset(value):\n\t\tif max_size == value: return\n\t\tmax_size = value\n\t\trequest_layout()\n\n@export var max_width_parameter := \"\" :\n\tset(value):\n\t\tif max_width_parameter == value: return\n\t\tmax_width_parameter = value\n\t\trequest_layout()\n\n@export var max_height_parameter := \"\" :\n\tset(value):\n\t\tif max_height_parameter == value: return\n\t\tmax_height_parameter = value\n\t\trequest_layout()\n\nfunc _effective_min_height()->int:\n\tmatch min_height:\n\t\tLimitType.FIXED:\n\t\t\treturn int(min_size.y)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( min_height_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_min_width()->int:\n\tmatch min_width:\n\t\tLimitType.FIXED:\n\t\t\treturn int(min_size.x)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( min_width_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_max_height()->int:\n\tmatch max_height:\n\t\tLimitType.FIXED:\n\t\t\treturn int(max_size.y)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( max_height_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_max_width()->int:\n\tmatch max_width:\n\t\tLimitType.FIXED:\n\t\t\treturn int(max_size.x)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( max_width_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\tif min_width != LimitType.UNLIMITED:\n\t\tavailable_size.x = max( available_size.x, _effective_min_width() )\n\tif min_height != LimitType.UNLIMITED:\n\t\tavailable_size.y = max( available_size.y, _effective_min_height() )\n\n\tif max_width != LimitType.UNLIMITED:\n\t\tavailable_size.x = min( available_size.x, _effective_max_width() )\n\tif max_height != LimitType.UNLIMITED:\n\t\tavailable_size.y = min( available_size.y, _effective_max_height() )\n\n\tsuper( available_size, limited )\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGMarginLayout.gd",
    "content": "@tool\n\n## Lays out its children in a layered stack with pixel or proportional margins.\nclass_name GGMarginLayout\nextends GGComponent\n\nenum MarginType\n{\n\tPROPORTIONAL,  ## Specify margins between 0.0 and 1.0, similar to anchors.\n\tFIXED,         ## Margins have fixed pixel sizes.\n\tPARAMETER      ## Margins use parameters as their pixel sizes.\n}\n\n@export_group(\"Margins\")\n\n## Specifies the type of margin that will surround the child content area.\n@export var margin_type := MarginType.PROPORTIONAL :\n\tset(value):\n\t\tif margin_type == value: return\n\n\t\tif reference_node:\n\t\t\tvar ref_size = reference_node.size\n\t\t\tif value == MarginType.PROPORTIONAL:\n\t\t\t\tif margin_type == MarginType.FIXED and ref_size.x and ref_size.y:\n\t\t\t\t\tleft_margin /= ref_size.x\n\t\t\t\t\ttop_margin /= ref_size.y\n\t\t\t\t\tright_margin /= ref_size.x\n\t\t\t\t\tbottom_margin /= ref_size.y\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0.0\n\t\t\t\t\ttop_margin = 0.0\n\t\t\t\t\tright_margin = 0.0\n\t\t\t\t\tbottom_margin = 0.0\n\t\t\telif value == MarginType.FIXED:\n\t\t\t\tif margin_type == MarginType.PROPORTIONAL and ref_size.x and ref_size.y:\n\t\t\t\t\tleft_margin = int( left_margin * ref_size.x )\n\t\t\t\t\ttop_margin = int( top_margin * ref_size.y )\n\t\t\t\t\tright_margin = int( right_margin * ref_size.x )\n\t\t\t\t\tbottom_margin = int( bottom_margin * ref_size.y )\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0\n\t\t\t\t\ttop_margin = 0\n\t\t\t\t\tright_margin = 0\n\t\t\t\t\tbottom_margin = 0\n\t\telse:\n\t\t\tif value == MarginType.PROPORTIONAL:\n\t\t\t\tif margin_type == MarginType.FIXED and size.x and size.y:\n\t\t\t\t\tleft_margin /= size.x\n\t\t\t\t\ttop_margin /= size.y\n\t\t\t\t\tright_margin = 1.0 - (right_margin/size.x)\n\t\t\t\t\tbottom_margin = 1.0 - (bottom_margin/size.y)\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0.0\n\t\t\t\t\ttop_margin = 0.0\n\t\t\t\t\tright_margin = 1.0\n\t\t\t\t\tbottom_margin = 1.0\n\t\t\telif value == MarginType.FIXED:\n\t\t\t\tif margin_type == MarginType.PROPORTIONAL and size.x and size.y:\n\t\t\t\t\tleft_margin = int( left_margin * size.x )\n\t\t\t\t\ttop_margin = int( top_margin * size.y )\n\t\t\t\t\tright_margin = int( (1.0 - right_margin) * size.x )\n\t\t\t\t\tbottom_margin = int( (1.0 - bottom_margin) * size.y )\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0\n\t\t\t\t\ttop_margin = 0\n\t\t\t\t\tright_margin = 0\n\t\t\t\t\tbottom_margin = 0\n\n\t\tmargin_type = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.25: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var left_margin:float = 0.0 :\n\tset(value):\n\t\tif left_margin == value: return\n\t\tleft_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.25: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var top_margin:float = 0.0 :\n\tset(value):\n\t\tif top_margin == value: return\n\t\ttop_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.75: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var right_margin:float = 1.0 :\n\tset(value):\n\t\tif right_margin == value: return\n\t\tright_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.75: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var bottom_margin:float = 1.0 :\n\tset(value):\n\t\tif bottom_margin == value: return\n\t\tbottom_margin = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no left margin[br]  \"abc\": left margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var left_parameter:String = \"\" :\n\tset(value):\n\t\tif left_parameter == value: return\n\t\tleft_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no top margin[br]  \"abc\": top margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var top_parameter:String = \"\" :\n\tset(value):\n\t\tif top_parameter == value: return\n\t\ttop_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no right margin[br]  \"abc\": right margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var right_parameter:String = \"\" :\n\tset(value):\n\t\tif right_parameter == value: return\n\t\tright_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no bottom margin[br]  \"abc\": bottom margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var bottom_parameter:String = \"\" :\n\tset(value):\n\t\tif bottom_parameter == value: return\n\t\tbottom_parameter = value\n\t\trequest_layout()\n\nfunc _resolve_shrink_to_fit_height( available_size:Vector2 ):\n\tsuper( available_size )\n\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tsize.y += int( top_margin * reference_node.size.y )\n\t\t\t\tsize.y += int( bottom_margin * reference_node.size.y )\n\t\t\telse:\n\t\t\t\tsize.y += int( available_size.y * top_margin )\n\t\t\t\tsize.y += int( available_size.y * bottom_margin )\n\t\tMarginType.FIXED:\n\t\t\tsize.y += top_margin\n\t\t\tsize.y += bottom_margin\n\t\tMarginType.PARAMETER:\n\t\t\tsize.y += get_parameter(top_parameter,0)\n\t\t\tsize.y += get_parameter(bottom_parameter,0)\n\nfunc _resolve_shrink_to_fit_width( available_size:Vector2 ):\n\tsuper( available_size )\n\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tsize.x += int( left_margin * reference_node.size.x )\n\t\t\t\tsize.x += int( right_margin * reference_node.size.x )\n\t\t\telse:\n\t\t\t\tsize.x += int( available_size.x * left_margin )\n\t\t\t\tsize.x += int( available_size.x * right_margin )\n\t\tMarginType.FIXED:\n\t\t\tsize.x += left_margin\n\t\t\tsize.x += right_margin\n\t\tMarginType.PARAMETER:\n\t\t\tsize.x += get_parameter(left_parameter,0)\n\t\t\tsize.x += get_parameter(right_parameter,0)\n\nfunc _with_margins( rect:Rect2 )->Rect2:\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tvar left = int( left_margin * reference_node.size.x )\n\t\t\t\tvar right = int( right_margin * reference_node.size.x )\n\t\t\t\tvar top = int( top_margin * reference_node.size.y )\n\t\t\t\tvar bottom = int( bottom_margin * reference_node.size.y )\n\t\t\t\tvar x = rect.position.x + left\n\t\t\t\tvar y = rect.position.y + top\n\t\t\t\tvar x2 = rect.position.x + (rect.size.x - right)\n\t\t\t\tvar y2 = rect.position.y + (rect.size.y - bottom)\n\t\t\t\tvar w = x2 - x\n\t\t\t\tvar h = y2 - y\n\t\t\t\tif w < 0: w = 0\n\t\t\t\tif h < 0: h = 0\n\t\t\t\treturn Rect2( x, y, w, h )\n\t\t\telse:\n\t\t\t\tvar x = rect.position.x + floor( rect.size.x * left_margin )\n\t\t\t\tvar y = rect.position.y + floor( rect.size.y * top_margin )\n\t\t\t\tvar x2 = rect.position.x + floor( rect.size.x * right_margin )\n\t\t\t\tvar y2 = rect.position.y + floor( rect.size.y * bottom_margin )\n\t\t\t\tvar w = x2 - x\n\t\t\t\tvar h = y2 - y\n\t\t\t\tif w < 0: w = 0\n\t\t\t\tif h < 0: h = 0\n\t\t\t\treturn Rect2( x, y, w, h )\n\t\tMarginType.FIXED:\n\t\t\tvar x = rect.position.x + left_margin\n\t\t\tvar y = rect.position.y + top_margin\n\t\t\tvar x2 = rect.position.x + (rect.size.x - right_margin)\n\t\t\tvar y2 = rect.position.y + (rect.size.y - bottom_margin)\n\t\t\tvar w = x2 - x\n\t\t\tvar h = y2 - y\n\t\t\tif w < 0: w = 0\n\t\t\tif h < 0: h = 0\n\t\t\treturn Rect2( x, y, w, h )\n\t\tMarginType.PARAMETER:\n\t\t\tvar x = rect.position.x + get_parameter(left_parameter,0)\n\t\t\tvar y = rect.position.y + get_parameter(top_parameter,0)\n\t\t\tvar x2 = rect.position.x + (rect.size.x - get_parameter(right_parameter,0))\n\t\t\tvar y2 = rect.position.y + (rect.size.y - get_parameter(bottom_parameter,0))\n\t\t\tvar w = x2 - x\n\t\t\tvar h = y2 - y\n\t\t\tif w < 0: w = 0\n\t\t\tif h < 0: h = 0\n\t\t\treturn Rect2( x, y, w, h )\n\t\t_: return rect\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGNinePatchRect.gd",
    "content": "@tool\n\nclass_name GGNinePatchRect\nextends GGComponent\n\n@export var texture:Texture2D :\n\tset(value):\n\t\ttexture = value\n\t\tif not texture: return\n\n\t\t_texture_region = Rect2( 0, 0, texture.get_width(), texture.get_height() )\n\n\t\t_update_piece_rects()\n\n@export var draw_center := true :\n\tset(value):\n\t\tdraw_center = value\n\t\tqueue_redraw()\n\n@export_group(\"Patch Margin\")\n\n@export var left   := 0:\n\tset(value):\n\t\tleft = clamp( value, 0, _texture_region.size.x )\n\t\t_update_piece_rects()\n\n@export var top    := 0:\n\tset(value):\n\t\ttop = clamp( value, 0, _texture_region.size.y )\n\t\t_update_piece_rects()\n\n@export var right  := 0:\n\tset(value):\n\t\tright = clamp( value, 0, _texture_region.size.x )\n\t\t_update_piece_rects()\n\n@export var bottom := 0:\n\tset(value):\n\t\tbottom = clamp( value, 0, _texture_region.size.y )\n\t\t_update_piece_rects()\n\n@export_group(\"Fill Mode\")\n\n@export var horizontal_fill := FillMode.STRETCH :\n\tset(value):\n\t\thorizontal_fill = value\n\t\tqueue_redraw()\n\n@export var vertical_fill   := FillMode.STRETCH :\n\tset(value):\n\t\tvertical_fill = value\n\t\tqueue_redraw()\n\nvar _texture_region:Rect2\nvar _piece_rects:Array[Rect2] = []\n\nfunc _draw():\n\tif size.x == 0 or size.y == 0 or not texture: return\n\n\tvar _left = left\n\tvar _right = right\n\tvar _top = top\n\tvar _bottom = bottom\n\n\tif left + right > size.x or top + bottom > size.y:\n\t\tvar scale_x = size.x / (_left + _right)\n\t\tvar scale_y = size.y / (_top + _bottom)\n\t\tvar scale = min( scale_x, scale_y )\n\n\t\t_left = floor( _left * scale )\n\t\t_right = ceil( _right * scale )\n\t\t_top = floor( _top * scale )\n\t\t_bottom = ceil( _bottom * scale )\n\n\tvar mid_w = max( size.x - (_left+_right), 0 )\n\tvar mid_h = max( size.y - (_top+_bottom), 0 )\n\n\tvar pos = position\n\tif _top > 0:\n\t\tif _left > 0:   draw_texture_rect_region( texture, Rect2(pos,Vector2(_left,_top)), _piece_rects[0], modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(mid_w,_top)), _piece_rects[1], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tif _right > 0:  draw_texture_rect_region( texture, Rect2(pos,Vector2(_right,_top)), _piece_rects[2], modulate )\n\n\tpos = Vector2( position.x, pos.y + _top )\n\tif mid_h > 0:\n\t\tfill_texture( texture, Rect2(pos,Vector2(_left,mid_h)), _piece_rects[3], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tif draw_center and mid_w > 0:  fill_texture( texture, Rect2(pos,Vector2(mid_w,mid_h)), _piece_rects[4], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(_right,mid_h)), _piece_rects[5], horizontal_fill, vertical_fill, modulate )\n\n\tpos = Vector2( position.x, pos.y + mid_h )\n\tif _bottom > 0:\n\t\tif _left > 0:   draw_texture_rect_region( texture, Rect2(pos,Vector2(_left,_bottom)), _piece_rects[6], modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(mid_w,_bottom)), _piece_rects[7], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tif _right > 0:  draw_texture_rect_region( texture, Rect2(pos,Vector2(_right,_bottom)), _piece_rects[8], modulate )\n\nfunc _update_piece_rects():\n\tvar x = _texture_region.position.x\n\tvar y = _texture_region.position.y\n\tvar w = _texture_region.size.x\n\tvar h = _texture_region.size.y\n\tvar mid_w = max( w - (left+right), 0 )\n\tvar mid_h = max( h - (top+bottom), 0 )\n\n\t_piece_rects = []\n\n\t_piece_rects.push_back( Rect2     (      x, y, left,  top) )  # TL\n\t_piece_rects.push_back( Rect2(      x+left, y, mid_w, top) )  # T\n\t_piece_rects.push_back( Rect2( x+(w-right), y, right, top) )  # TR\n\n\t_piece_rects.push_back( Rect2(           x, y+top, left,  mid_h) )  # L\n\t_piece_rects.push_back( Rect2(      x+left, y+top, mid_w, mid_h) )  # M\n\t_piece_rects.push_back( Rect2( x+(w-right), y+top, right, mid_h) )  # R\n\n\t_piece_rects.push_back( Rect2(           x, y+(h-bottom), left,  bottom) )  # BL\n\t_piece_rects.push_back( Rect2(      x+left, y+(h-bottom), mid_w, bottom) )  # B\n\t_piece_rects.push_back( Rect2( x+(w-right), y+(h-bottom), right, bottom) )  # BR\n\n\tqueue_redraw()\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGOverlay.gd",
    "content": "@tool\n\n## Positions its children at arbitrary coordinates within its own bounds, similiar to a sprite.\n## Not intended for use with an actual Sprite2D; use GGTextureRect or other Control types as\n## children. Typically used with a single child node.\nclass_name GGOverlay\nextends GGComponent\n\nenum PositioningMode\n{\n\tPROPORTIONAL,  ## Specify child position as a fraction between 0.0 and 1.0.\n\tFIXED,         ## Child position is a fixed pixel offset.\n\tPARAMETER      ## Use a parameter as the child's relative pixel offset.\n}\n\nenum ScaleFactor\n{\n\tCONSTANT,      ## Scale using a fixed scale factor.\n\tPARAMETER      ## Scale using a subtree parameter.\n}\n\n@export_group(\"Child Position and Scale\")\n\n## The child positioning mode.\n@export var positioning_mode := PositioningMode.PROPORTIONAL :\n\tset(value):\n\t\tif positioning_mode == value: return\n\t\tmatch value:\n\t\t\tPositioningMode.PROPORTIONAL:\n\t\t\t\tif positioning_mode == PositioningMode.FIXED:\n\t\t\t\t\tchild_x /= size.x\n\t\t\t\t\tchild_y /= size.y\n\t\t\t\telse:\n\t\t\t\t\tchild_x = 0.5\n\t\t\t\t\tchild_y = 0.5\n\t\t\tPositioningMode.FIXED:\n\t\t\t\tif positioning_mode == PositioningMode.PROPORTIONAL:\n\t\t\t\t\tchild_x = int( child_x * size.x )\n\t\t\t\t\tchild_y = int( child_y * size.y )\n\t\t\t\telse:\n\t\t\t\t\tchild_x = int(size.x / 2.0)\n\t\t\t\t\tchild_y = int(size.y / 2.0)\n\t\tpositioning_mode = value\n\t\trequest_layout()\n\n## The child 'x' offset within this component. Use 0.0-1.0 for positioning mode [b]Proportional[/b] and integer values for [b]Fixed[/b].\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var child_x:float = 0.5 :\n\tset(value):\n\t\tif child_x == value: return\n\t\tchild_x = value\n\t\trequest_layout()\n\n## The child 'y' offset within this component. Use 0.0-1.0 for positioning mode [b]Proportional[/b] and integer values for [b]Fixed[/b].\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var child_y:float = 0.5 :\n\tset(value):\n\t\tif child_y == value: return\n\t\tchild_y = value\n\t\trequest_layout()\n\n## The parameter name to use for the child 'x' offset.\n@export var child_x_parameter := \"\" :\n\tset(value):\n\t\tif child_x_parameter == value: return\n\t\tchild_x_parameter = value\n\t\trequest_layout()\n\n## The parameter name to use for the child 'y' offset.\n@export var child_y_parameter := \"\" :\n\tset(value):\n\t\tif child_y_parameter == value: return\n\t\tchild_y_parameter = value\n\t\trequest_layout()\n\n## The horizontal scale mode.\n@export var h_scale_factor := ScaleFactor.CONSTANT :\n\tset(value):\n\t\tif h_scale_factor == value: return\n\t\th_scale_factor = value\n\t\trequest_layout()\n\n## The vertical scale mode.\n@export var v_scale_factor := ScaleFactor.CONSTANT :\n\tset(value):\n\t\tif v_scale_factor == value: return\n\t\tv_scale_factor = value\n\t\trequest_layout()\n\n## The horizontal scale factor to use when [member h_scale_factor] is [b]Constant[/b].\n@export_range(0.0,1.0,0.0001,\"or_greater\") var h_scale_constant:float = 1.0 :\n\tset(value):\n\t\tif h_scale_constant == value: return\n\t\th_scale_constant = value\n\t\trequest_layout()\n\n## The vertical scale factor to use when [member v_scale_factor] is [b]Constant[/b].\n@export_range(0.0,1.0,0.0001,\"or_greater\") var v_scale_constant:float = 1.0 :\n\tset(value):\n\t\tif v_scale_constant == value: return\n\t\tv_scale_constant = value\n\t\trequest_layout()\n\n## The horizontal scale factor to use when [member h_scale_factor] is [b]Parameter[/b].\n@export var h_scale_parameter:String = \"\" :\n\tset(value):\n\t\tif h_scale_parameter == value: return\n\t\th_scale_parameter = value\n\t\trequest_layout()\n\n## The vertical scale factor to use when [member v_scale_factor] is [b]Parameter[/b].\n@export var v_scale_parameter:String = \"\" :\n\tset(value):\n\t\tif v_scale_parameter == value: return\n\t\tv_scale_parameter = value\n\t\trequest_layout()\n\nfunc _get_scale()->Vector2:\n\tvar sx := 0.0\n\tvar sy := 0.0\n\n\tmatch h_scale_factor:\n\t\tScaleFactor.CONSTANT:\n\t\t\tsx = h_scale_constant\n\t\tScaleFactor.PARAMETER:\n\t\t\tsx = get_parameter( h_scale_parameter )\n\n\tmatch v_scale_factor:\n\t\tScaleFactor.CONSTANT:\n\t\t\tsy = v_scale_constant\n\t\tScaleFactor.PARAMETER:\n\t\t\tsy = get_parameter( v_scale_parameter )\n\n\treturn Vector2(sx,sy)\n\nfunc _perform_child_layout( available_bounds:Rect2 ):\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child is Control or not child.visible: continue\n\n\t\tvar x_pos := 0\n\t\tvar y_pos := 0\n\t\tmatch positioning_mode:\n\t\t\tPositioningMode.PROPORTIONAL:\n\t\t\t\tx_pos = available_bounds.size.x * child_x\n\t\t\t\ty_pos = available_bounds.size.y * child_y\n\t\t\tPositioningMode.FIXED:\n\t\t\t\tx_pos = child_x\n\t\t\t\ty_pos = child_y\n\t\t\tPositioningMode.PARAMETER:\n\t\t\t\tx_pos = get_parameter( child_x_parameter, child_x )\n\t\t\t\ty_pos = get_parameter( child_y_parameter, child_y )\n\n\t\t# Adjust x_pos and y_pos for SIZE_SHRINK_X.\n\t\tif child.size_flags_horizontal & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tx_pos -= int(child.size.x / 2.0)\n\t\telif child.size_flags_horizontal & SizeFlags.SIZE_SHRINK_END:\n\t\t\tx_pos -= int(child.size.x)\n\n\t\tif child.size_flags_vertical & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\ty_pos -= int(child.size.y / 2.0)\n\t\telif child.size_flags_vertical & SizeFlags.SIZE_SHRINK_END:\n\t\t\ty_pos -= int(child.size.y)\n\n\t\t_perform_component_layout( child, Rect2(Vector2(x_pos,y_pos),child.size) )\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\tvar scale = _get_scale()\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child is Control or not child.visible: continue\n\n\t\t# Resolve once at full size to get the child's full size\n\t\t_resolve_child_size( child, available_size, limited )\n\n\t\t# Apply the scale factor to the child\n\t\t_resolve_child_size( child, floor( child.size * scale ), limited )\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGParameterSetter.gd",
    "content": "@tool\n\n## A component that sizes normally and then sets subtree parameters to its own width and/or height.\nclass_name GGParameterSetter\nextends GGComponent\n\n@export_group(\"Parameter Names\")\n\n## Optional name of a parameter to save this node's width in.\n@export var width_store := \"\" :\n\tset(value):\n\t\tif width_store == value: return\n\t\twidth_store = value\n\t\tif value != \"\":\n\t\t\tif value != _cur_width_parameter:\n\t\t\t\tvar top = get_top_level_component()\n\t\t\t\tif top: top.parameters.erase( _cur_width_parameter )\n\t\t\t\t_cur_width_parameter = value\n\t\t\tset_parameter( width_store, size.x )\n\n## Optional name of a parameter to save this node's height in.\n@export var height_store := \"\" :\n\tset(value):\n\t\tif height_store == value: return\n\t\theight_store = value\n\t\tif value != \"\":\n\t\t\tif value != _cur_height_parameter:\n\t\t\t\tvar top = get_top_level_component()\n\t\t\t\tif top: top.parameters.erase( _cur_height_parameter )\n\t\t\t\t_cur_height_parameter = value\n\t\t\tset_parameter( height_store, size.y )\n\nvar _cur_width_parameter := \"\"\nvar _cur_height_parameter := \"\"\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\tsuper( available_size, limited )\n\tif width_store != \"\": set_parameter( width_store, size.x )\n\tif height_store != \"\": set_parameter( height_store, size.y )\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGRichTextLabel.gd",
    "content": "@tool\n\nclass_name GGRichTextLabel\nextends RichTextLabel\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n# Check to lock in the current font size and reference node height as reference\n# values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\treference_font_sizes[style_name] = 0\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = floor(reference_node.size.y)\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\treference_font_sizes[style_name] = get_theme_font_size( style_size_name )\n\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\treference_font_sizes[style_name] = 0\n\t\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\t\tif cur_size:\n\t\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [RichTextLabel] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of each font style.\n@export var reference_font_sizes:Dictionary = {\"normal\":0,\"bold\":0,\"italics\":0,\"bold_italics\":0,\"mono\":0}\n\n## The names of the parameters to use when [member text_size_mode] is [b]Parameter[/b].\n## The parameter for style \"normal\" will be the default for any other style that does not specify a\n## parameter.\n@export var text_size_parameters:Dictionary = {\"normal\":\"\",\"bold\":\"\",\"italics\":\"\",\"bold_italics\":\"\",\"mono\":\"\"} :\n\tset(value):\n\t\tif text_size_parameters == value: return\n\t\ttext_size_parameters = value\n\n\t\tif text_size_mode == GGComponent.TextSizeMode.PARAMETER:\n\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_sizes:Dictionary = {\"normal\":0,\"bold\":0,\"italics\":0,\"bold_italics\":0,\"mono\":0}\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGRICHTEXTLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tvar any_modified = false\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\tvar cur_size = get_theme_font_size( style_size_name )\n\t\t\t\tif cur_size != _current_font_sizes[style_name]:\n\t\t\t\t\t_current_font_sizes[style_name] = cur_size\n\t\t\t\t\tany_modified = true\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\tif _current_font_sizes[style_name] != reference_font_sizes[style_name]:\n\t\t\t\t\t_current_font_sizes[style_name] = reference_font_sizes[style_name]\n\t\t\t\t\tany_modified = true\n\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\tany_modified = true\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\n\tif any_modified: request_layout()\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\tbbcode_enabled = true\n\t\tscroll_active = false\n\t\thorizontal_mode  = GGComponent.ScalingMode.EXPAND_TO_FILL\n\t\tvertical_mode = GGComponent.ScalingMode.FIXED\n\t\tif abs(layout_size.x) < 0.0001 and abs(layout_size.y) < 0.0001:\n\t\t\tlayout_size = Vector2( get_content_width(), get_content_height() )\n\t\tif text == \"\": text = \"[center]GGRichTextLabel\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t_current_font_sizes[style_name] = get_theme_font_size( style_size_name )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\t_current_font_sizes[style_name] = reference_font_sizes[style_name]\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of each font\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\tvar cur_size = reference_font_sizes[style_name] * cur_scale\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n\tlayout_size = Vector2( get_content_width(), get_content_height() )\n\tsize = layout_size\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGTextureRect.gd",
    "content": "@tool\n\n## Extends TextureRect and adapts it to work painlessly with the GameGUI layout system.\nclass_name GGTextureRect\nextends TextureRect\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## An optional node to use as a size reference for [b]Proportional[/b] scaling\n## mode. The reference node must be in a subtree higher in the scene tree than\n## this node. Often the size reference is an invisible root-level square-aspect\n## component; this allows same-size horizontal and vertical proportional spacers.\n@export var reference_node:Control = null :\n\tset(value):\n\t\tif reference_node != value:\n\t\t\treference_node = value\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that GameGUI-related properties have been set\n## for this node. Uncheck to automatically reconfigure those properties.\n@export var is_configured := false\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# CONFIGURATION METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\nfunc _configure():\n\tif not is_configured and texture:\n\t\tis_configured = true\n\t\thorizontal_mode  = GGComponent.ScalingMode.ASPECT_FIT\n\t\tvertical_mode = GGComponent.ScalingMode.ASPECT_FIT\n\t\tlayout_size   = texture.get_size()  # TextureRect-specific\n\n\t\t# Let the GG framework and wrapper handle the sizing (TextureRect-specific)\n\t\texpand_mode   = TextureRect.ExpandMode.EXPAND_IGNORE_SIZE\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API METHODS\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tpass\n\n# Called at the beginning of GGLayout. Adjust 'horizontal_mode',\n# 'vertical_mode', and/or 'layout_size'. Other nodes may not have their sizes set yet,\n# so defer relative size computations to _on_resolve_size(available_size:Vector2).\nfunc _on_update_size():\n\tpass\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/GGVBox.gd",
    "content": "@tool\n\n## A GameGUI layout that arranges its child elements in a vertical column.\nclass_name GGVBox\nextends GGComponent\n\nenum VerticalContentAlignment\n{\n\tTOP,     ## Top-align the content.\n\tCENTER,  ## Center the content.\n\tBOTTOM   ## Bottom-align the content.\n}\n\n## Specify the vertical alignment of the content as a whole.\n@export var content_alignment := VerticalContentAlignment.CENTER :\n\tset(value):\n\t\tcontent_alignment = value\n\t\trequest_layout()\n\nvar _min_heights:Array[int] = []\nvar _max_heights:Array[int] = []\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\t# Resolve for and collect min and max sizes\n\t_max_heights.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, available_size, true )\n\t\t\t_max_heights.push_back( int(child.size.y) )\n\t\telse:\n\t\t\t_max_heights.push_back( 0 )\n\n\t_min_heights.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, Vector2(available_size.x,0), true )\n\t\t\t_min_heights.push_back( int(child.size.y) )\n\t\telse:\n\t\t\t_min_heights.push_back( 0 )\n\n\tvar expand_count := 0\n\tvar total_stretch_ratio := 0.0\n\tvar fixed_height := 0\n\tvar min_height := 0\n\n\t# Leaving other children at their minimum height, set aspect-fit, proportional,\n\t# and shrink-to-fit height nodes to their maximum size.\n\tvar modes = [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.SHRINK_TO_FIT]\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\n\t\tvar has_mode = child is GGComponent or child.has_method(\"request_layout\")\n\t\tif has_mode and child.vertical_mode in modes:\n\t\t\t_resolve_child_size( child, available_size, limited )\n\t\t\tvar h = int(child.size.y)\n\t\t\tmin_height += h\n\t\t\tfixed_height += h\n\t\t\t_min_heights[i] = h\n\t\t\t_max_heights[i] = h\n\n\t\telse:\n\t\t\tvar h = _min_heights[i]\n\t\t\tmin_height += h\n\n\t\t\tif _min_heights[i] == _max_heights[i]:\n\t\t\t\tfixed_height += h\n\t\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\t\t\telse:\n\t\t\t\texpand_count += 1\n\t\t\t\ttotal_stretch_ratio += child.size_flags_stretch_ratio\n\n\tif expand_count == 0 or total_stretch_ratio == 0.0 or min_height >= available_size.y: return\n\n\tvar excess_height = int(available_size.y - fixed_height)\n\tvar remaining_height = excess_height\n\n\t# Find children with a min height larger than their portion. Let them keep their min height and adjust remaining.\n\tvar remaining_total_stretch_ratio = total_stretch_ratio\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif h < _min_heights[i]:\n\t\t\th = _min_heights[i]\n\t\t\tremaining_height -= h\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_heights[i] = _max_heights[i]  # skip this node in the next pass\n\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\n\texcess_height = remaining_height\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# Distribute remaining height next to children with a max height smaller than their portion.\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif h > _max_heights[i]:\n\t\t\th = _max_heights[i]\n\t\t\t_resolve_child_size( child, Vector2(available_size.x,h), limited )\n\t\t\tremaining_height -= h\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_heights[i] = _max_heights[i]  # skip this node in the next pass\n\n\texcess_height = remaining_height\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# If this GGVBox is shrink-to-fit height then we're done; don't add remaining space to\n\t# the children.\n\tif vertical_mode == GGComponent.ScalingMode.SHRINK_TO_FIT:\n\t\treturn\n\n\t# Distribute remaining height\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\t_resolve_child_size( child, Vector2(available_size.x,h), limited )\n\t\tremaining_height -= h\n\t\texpand_count -= 1\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\tsize.y = _get_sum_of_child_sizes().y\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\tsize.x = _get_largest_child_size().x\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\n\tvar inner_bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\tvar pos = inner_bounds.position\n\tvar sz = inner_bounds.size\n\n\tvar diff = sz.y - _get_sum_of_child_sizes().y\n\tmatch content_alignment:\n\t\tVerticalContentAlignment.TOP:    pass\n\t\tVerticalContentAlignment.CENTER: pos.y += int(diff/2.0)\n\t\tVerticalContentAlignment.BOTTOM: pos.y += diff\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:\n\t\t\t_perform_component_layout( child, Rect2(pos,Vector2(sz.x,child.size.y)) )\n\t\t\tpos += Vector2( 0, child.size.y )\n\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/plugin.cfg",
    "content": "[plugin]\n\nname=\"GameGUI\"\ndescription=\"A collection of dynamic layout nodes that provide a full-featured alternative to Container nodes.\"\nauthor=\"Brom Bresenham\"\nversion=\"1.5\"\nscript=\"plugin.gd\"\n"
  },
  {
    "path": "DemoProject/addons/GameGUI/plugin.gd",
    "content": "@tool\nextends EditorPlugin\n\nfunc _enter_tree():\n\t# Initialization of the plugin goes here.\n\tadd_custom_type( \"GGButton\",               \"Button\",        preload(\"GGButton.gd\"), preload(\"Icons/GGButton.svg\") )\n\tadd_custom_type( \"GGComponent\",            \"Container\",     preload(\"GGComponent.gd\"), preload(\"Icons/GGComponent.svg\") )\n\tadd_custom_type( \"GGFiller\",               \"GGComponent\",   preload(\"GGFiller.gd\"), preload(\"Icons/GGFiller.svg\") )\n\tadd_custom_type( \"GGHBox\",                 \"GGComponent\",   preload(\"GGHBox.gd\"), preload(\"Icons/GGHBox.svg\") )\n\tadd_custom_type( \"GGInitialWindowSize\",    \"GGComponent\",   preload(\"GGInitialWindowSize.gd\"), preload(\"Icons/GGInitialWindowSize.svg\") )\n\tadd_custom_type( \"GGLabel\",                \"Label\",         preload(\"GGLabel.gd\"), preload(\"Icons/GGLabel.svg\") )\n\tadd_custom_type( \"GGLayoutConfig\",         \"Node2D\",        preload(\"GGLayoutConfig.gd\"), preload(\"Icons/GGLayoutConfig.svg\") )\n\tadd_custom_type( \"GGLimitedSizeComponent\", \"GGComponent\",   preload(\"GGLimitedSizeComponent.gd\"), preload(\"Icons/GGLimitedSizeComponent.svg\") )\n\tadd_custom_type( \"GGMarginLayout\",         \"GGComponent\",   preload(\"GGMarginLayout.gd\"), preload(\"Icons/GGMarginLayout.svg\") )\n\tadd_custom_type( \"GGNinePatchRect\",        \"GGComponent\",   preload(\"GGNinePatchRect.gd\"), preload(\"Icons/GGNinePatchRect.svg\") )\n\tadd_custom_type( \"GGParameterSetter\",      \"GGComponent\",   preload(\"GGParameterSetter.gd\"), preload(\"Icons/GGParameterSetter.svg\") )\n\tadd_custom_type( \"GGOverlay\",              \"GGComponent\",   preload(\"GGOverlay.gd\"), preload(\"Icons/GGOverlay.svg\") )\n\tadd_custom_type( \"GGRichTextLabel\",        \"RichTextLabel\", preload(\"GGRichTextLabel.gd\"), preload(\"Icons/GGRichTextLabel.svg\") )\n\tadd_custom_type( \"GGTextureRect\",          \"TextureRect\",   preload(\"GGTextureRect.gd\"), preload(\"Icons/GGTextureRect.svg\") )\n\tadd_custom_type( \"GGVBox\",                 \"GGComponent\",   preload(\"GGVBox.gd\"), preload(\"Icons/GGVBox.svg\") )\n\nfunc _exit_tree():\n\t# Clean-up of the plugin goes here.\n\tremove_custom_type( \"GGButton\" )\n\tremove_custom_type( \"GGComponent\" )\n\tremove_custom_type( \"GGFiller\" )\n\tremove_custom_type( \"GGHBox\" )\n\tremove_custom_type( \"GGInitialWindowSize\" )\n\tremove_custom_type( \"GGLabel\" )\n\tremove_custom_type( \"GGLayoutConfig\" )\n\tremove_custom_type( \"GGLimitedSizeComponent\" )\n\tremove_custom_type( \"GGMarginLayout\" )\n\tremove_custom_type( \"GGNinePatchRect\" )\n\tremove_custom_type( \"GGParameterSetter\" )\n\tremove_custom_type( \"GGOverlay\" )\n\tremove_custom_type( \"GGRichTextLabel\" )\n\tremove_custom_type( \"GGTextureRect\" )\n\tremove_custom_type( \"GGVBox\" )\n"
  },
  {
    "path": "DemoProject/project.godot",
    "content": "; Engine configuration file.\n; It's best edited using the editor UI and not directly,\n; since the parameters that go here are not all obvious.\n;\n; Format:\n;   [section] ; section goes between []\n;   param=value ; assign values to parameters\n\nconfig_version=5\n\n[application]\n\nconfig/name=\"DemoProject\"\nconfig/features=PackedStringArray(\"4.2\", \"Mobile\")\nconfig/icon=\"res://icon.svg\"\n\n[editor_plugins]\n\nenabled=PackedStringArray(\"res://addons/GameGUI/plugin.cfg\")\n\n[rendering]\n\nrenderer/rendering_method=\"mobile\"\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Brom Bresenham\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": "Media/.gdignore",
    "content": "Godot will ignore this folder when installing the GameGUI addon.\n"
  },
  {
    "path": "README.md",
    "content": "# Godot-GameGUI\nA Godot 4.x plug-in that implements a rich and robust dynamic layout and sizing system for building user interfaces.\n\nAbout      | Current Release\n-----------|-----------------------\nVersion    | 1.5\nDate       | October 18, 2023\nChange Log | [Change Log](ChangeLog.md)\nPlatform   | Godot 4.x (tested on 4.2-dev3)\nLicense    | [MIT License](LICENSE)\nAuthor     | Brom Bresenham\n\n![DemoGif](Media/Images/Demo.gif)\n\n*DemoProject - included in this repo*\n\n\n![DemoGif](Media/Images/AxorUI.gif)\n\n*Axor the Mighty - see how this was built in the [Week 6 Devlog](https://youtu.be/Bq8M416Lcz4?si=9NVwZfn5KlI3fstB)*\n\n# About\nGameGUI is a set of Godot Control nodes that provide alternative layout capabilities to Godot's built-in Container classes. With Godot's built-in containers it is easy to make fixed-layout user interfaces that scale with the screen resolution *or* dynamic (responsive) layouts with fixed-size controls (such as labels), but it can be difficult to make dynamic layouts with scaling controls and text, and that's where GameGUI fits in.\n\n## Key Features\n- GameGUI layout nodes can be intermingled with standard Control nodes.\n- Layout of GameGUI subtrees is performed through recursive subdivision of available space via nested nodes.\n- Each GameGUI node can be configured to use various sizing modes that determine how it uses the space available to it: Expand-to-Fill, Aspect-Fit, etc.\n- Godot Control Container Sizing alignments (`Shrink Begin`, etc.) are respected.\n- GGComponent is the base node that implements the core functionality of the GameGUI system.\n- GGHBox and GGVbox are the layout workhorses. They are GameGUI variations of HBoxContainer and VBoxContainer that distribute available space to their children in an intuitive way.\n- Minimum and maximum node constraints can be established with a GGLimitedSizeComponent node.\n- Non-GameGUI Control nodes can be children of GameGUI nodes. They are stretched horizontally and vertically to fit the space their parent provides.\n- Any control node can be adapted to have the same scaling options as a GGComponent node by extending them with \"adapter code\" that adds certain properties and methods, making the control node a \"duck-typed\" fit. See GGTextureRect for a general example of adapter code that can be copy-pasted with minimal changes, or see GGLabel for code that auto-scales text size as well.\n- GGLabel, GGRichTextLabel, and GGButton are adapted versions of their namesakes that can scale their text size with the layout size.\n- GameGUI subtrees can define key-value \"parameters\" that components use for their size and/or for certain other properties. This allows complex sizing logic to be executed via GDScript and the results stored in the subtree for use by the components.\n- GGLayoutConfig provides a convenient way to set parameters by extending its script and overriding a single method.\n- [Built-in parameters](#Built-In-Parameters) `safe_area_[left|top|right|bottom]_margin` are automatically set by GameGUI.\n\n# Installation\n\n## Installing From Godot AssetLib\n\n1. Switch to the \"AssetLib\" tab in the Godot editor.\n2. Search for \"GameGUI\".\n3. Click and install the GameGUI addon.\n\n![Installing via AssetLib](Media/Images/InstallViaAssetLib.png)\n\n## Installing From GitHub Release\n\n1. Download the latest [release](https://github.com/brombres/Godot-GameGUI/releases).\n\n2. Drag the `addons` folder into a Godot project.\n\n3. Enable the plug-in under Project > Project Settings... > Plug-ins.\n\n![Enabling the Plugin](Media/Images/EnablePlugin.png)\n\n# Component Overview\n\n## Core Layout Components\n\n<table width=100%>\n  <tr>\n    <th>Icon</th>\n    <th>Node</th>\n    <th>Description</th>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGComponent.png\"></td>\n    <td>GGComponent</td>\n    <td>GameGUI base node type. Useful as container, sizer, filler, spacer. Lays out children in a layered stack.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGHBox.png\"></td>\n    <td>GGHBox</td>\n    <td>Lays out children in a horizontal row.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGVBox.png\"></td>\n    <td>GGVBox</td>\n    <td>Lays out children in a vertical column.</td>\n  </tr>\n</table>\n\n## Additional Layout Components\n\n<table width=100%>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGFiller.png\"></td>\n    <td>GGFiller</td>\n    <td>A GGComponent with an icon and default name that indicates its purpose is to fill up extra space.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGInitialWindowSize.png\"></td>\n    <td>GGInitialWindowSize</td>\n    <td>If it is the root node of a scene, sets the window size to its own size on launch. Useful for testing independent UI component scenes at typical aspect ratios. If it is not the root scene node the window size is unaffected.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGLimitedSizeComponent.png\"></td>\n    <td>GGLimitedSizeComponent</td>\n    <td>Applies minimum and/or maximum sizes to its child content.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGMarginLayout.png\"></td>\n    <td>GGMarginLayout</td>\n    <td>Adds inside margins to the layout of its child content.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGOverlay.png\"></td>\n    <td>GGOverlay</td>\n    <td>Positions its child content arbitrarily within its layout area in a sprite-like manner.</td>\n  </tr>\n</table>\n\n## Scaling Text Components\n\n<table width=100%>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGButton.png\"></td>\n    <td>GGButton</td>\n    <td>A Button that can auto-scale its text size.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGLabel.png\"></td>\n    <td>GGLabel</td>\n    <td>A Label that can auto-scale its text size.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGRichTextLabel.png\"></td>\n    <td>GGRichTextLabel</td>\n    <td>A RichTextLabel that can auto-scale its text size.</td>\n  </tr>\n</table>\n\n\n## Image Components\n\n<table width=100%>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGTextureRect.png\"></td>\n    <td>GGTextureRect</td>\n    <td>A TextureRect that uses the GameGUI layout system and automatically configures itself with appropriate defaults.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGNinePatchRect.png\"></td>\n    <td>GGNinePatchRect</td>\n    <td>Replicates NinePatchRect functionality and makes the following improvement: when the bounds of a GGNinePatchRect are smaller than its corners, the corners are proportionally shrunk to fit the available bounds.</td>\n  </tr>\n</table>\n\n\n## Parameter Components\n\n<table width=100%>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGLayoutConfig.png\"></td>\n    <td>GGLayoutConfig</td>\n    <td>Place this near the root of the scene, extend the script, make it a @tool, override <code>func _on_begin_layout(display_size:Vector2)</code>, and call <code>set_parameter(name,value)</code> with various computed values related to the current display size. Those parameters can be automatically used by other GameGUI nodes by setting their sizing mode to <i>Parameter</i> and supplying the desired parameter name.</td>\n  </tr>\n  <tr>\n    <td width=80px><img src=\"Media/Images/Icons/GGParameterSetter.png\"></td>\n    <td>GGParameterSetter</td>\n    <td>Sets specified subtree parameters to its own width and/or height, which can then be used to set the size of other components when they're in <i>Parameter</i> sizing mode.</td>\n  </tr>\n</table>\n\n# Scaling Modes\n\nEach GameGUI component supports the following seven scaling modes. Horizontal and vertical scaling modes are set separately.\n\n![Expand-to-Fill](Media/Images/ScalingModes.png)\n\nMode | Description\n-----|--------------\n**Expand-to-Fill** | The component stretches or compresses to fill the available area.<br><br>![Expand-to-Fill Demo](Media/Images/ScalingMode-ExpandToFill.gif)\n**Aspect-Fit** | The component maintains the specified aspect ratio and is sized as large as possible while still fitting in the available area.<br><br>![Aspect-Fit Demo](Media/Images/ScalingMode-AspectFit.gif)<br><br>The <b>Layout Size</b> property should be set to the desired aspect ratio. Note that 1680x840 has the same effect as 168x84, etc.<br><br>![Aspect-Layout-Size](Media/Images/ScalingMode-AspectFit-LayoutSize.png)\n**Aspect-Fill** | The component maintains the specified aspect ratio and is sized as small as possible while still completely filling the available area.<br><br>![Aspect-Fill Demo](Media/Images/ScalingMode-AspectFill.gif)<br><br>Note that Container Sizing options can be used to pin the content to a side or a corner.<br><br>![Aspect-Fill Demo 2](Media/Images/ScalingMode-AspectFill-2.gif)\n**Proportional** | Proportional mode can be used in one of two ways:<br><br>1. In the standard mode, the node size or other applicable property (such as GGMarginLayout margins) becomes a fraction of its available layout area, from 0.0 to 1.0.<br>![Proportional Demo](Media/Images/ScalingMode-Proportional-1.gif)<br><br>2. Alternatively, a component's `reference node` property can be set and the proportional value is now relative to the size of the reference node. The reference node should be in a higher subtree (closer to the root) than the node referencing it to ensure that the reference node size is established first. In the example below, a square-aspect component is used as the reference node for a GGMarginLayout so that the gap around the image has the same thickness on all sides.<br>![Proportional Demo](Media/Images/ScalingMode-Proportional-2.gif)\n**Shrink-to-Fit** | The component is sized as small as possible to enclose all of its children. For example, here is a GGVBox with fixed-size children set to **Shrink-to-Fit**.<br><br>![Shrink-to-Fit](Media/Images/ScalingMode-ShrinkToFit.png)\n**Fixed** | The component uses **Layout Size** as a fixed pixel size.<br><br>![Fixed](Media/Images/ScalingMode-Fixed.gif)\n**Parameter** | The component sets its pixel size to the subtree parameters named by the properties **Width Parameter** and/or **Height Parameter**. Create a `GGLayoutConfig` config as the first child of a GameGUI subtree, extend its script, add the `@tool` annotation, and override `func _on_begin_layout(display_size:Vector2)` to update parameters (via `set_parameter(name:String,value:Variant)`, `get_parameter(name:String)->Variant`, and/or `has_parameter(name:String)->bool`) whenever the layout is about to be updated.<br><br>![Fixed](Media/Images/ScalingMode-Parameter.gif)<br><br>Parameter values can be added, inspected, and removed in the editor by examining the **Parameters** property of a GameGUI subtree root.<br><br>![Parameter-2](Media/Images/ScalingMode-Parameter-2.png)\n\n## Aspect Mode Combinations\n\nHorizontal Mode | Vertical Mode | Effect\n----------------|---------------|-------\nAspect-Fit      | Aspect-Fit    | Component maintains the specified aspect ratio and is sized as large as possible while still fitting in the available area.\nAspect-Fill     | Aspect-Fill   | Component maintains the specified aspect ratio and is sized as small as possible while still completely filling the available area.\nAspect-Fill     | Aspect-Fit    | Component occupies all available width while maintaining the specified aspect ratio.\nAspect-Fit      | Aspect-Fill   | Component occupies all available height while maintaining the specified aspect ratio.\n\n## Built-In Parameters\n\nGameGUI automatically sets and maintains a small set of built-in parameters.\n\nAny component extending GGComponent can set its width and/or height to a parameter value by selecting the \"Fixed\" Horizontal and/or Vertical Mode and supplying the appropriate parameter name in the \"Width Parameter\" and/or \"Height Parameter\" field.\n\nIn scripts extending a GameGUI component these parameters can be retrieved with `get_parameter(\"parameter_name\",default_value)`.\n\n### Safe Area Margin Parameters\n\nEach margin parameter contains the number of pixels at a given edge of the display that cannot be safely drawn to due to a notch or rounded corners. For example, on iPhone 12 Pro, `safe_area_top_margin` will be set to `132` and `safe_area_bottom_margin` will be set to `102`.\n\nParameter Name            | Description\n--------------------------|--------------\n`safe_area_left_margin`   | The left pixel margin outside the safe area.\n`safe_area_top_margin`    | The top pixel margin outside the safe area.\n`safe_area_right_margin`  | The right pixel margin outside the safe area.\n`safe_area_bottom_margin` | The bottom pixel margin outside the safe area.\n\nHere is a simple GameGUI setup to include safe area margins in an app. A GGVbox contains top and bottom GGFiller components, their heights set to `Parameter` `safe_area_top_margin` and `Parameter` `safe_area_bottom_margin`, respectively. These parameters will be zero during desktop testing in windowed mode and non-zero on any modern iPhone, for example.\n\n![Safe Area Margin Parameters](Media/Images/SafeAreaMarginParameters.png)\n\n\n# Component Details\n\n## Core Layout Components\n\n### GGComponent\n\n![GGComponent](Media/Images/Icons/GGComponent.png)\n\nGameGUI base node type. Useful as container, sizer, filler, spacer. Lays out children in a layered stack.\nA common use case is to create multiple UI layers in a root GGComponent (or GGInitialWindowSize, etc.).\nThe first child might be an aspect-fill GGTextureRect background image, the second child a GGHBox or\nGGVBox for the main layout, and possibly finishing with a GGOverlay for a floating info panel or similar.\n\nThe following properties are available in all GameGUI nodes except for GGLayoutConfig.\n\n#### Properties\n\n![GGComponent](Media/Images/GGComponent.png)\n\n<table>\n  <tr>\n    <th>Property</th><th>Description</th>\n  </tr>\n  <tr>\n    <th>Horizontal Mode</th>\n    <td>Set to one of the <a href=\"#Scaling-Modes\">Scaling Modes</a>.</td>\n  </tr>\n  <tr>\n    <th>Vertical Mode</th>\n    <td>Set to one of the <a href=\"#Scaling-Modes\">Scaling Modes</a>. Can be set to a different mode than <b>Horizontal Mode</b>.</td>\n  </tr>\n  <tr>\n    <th>Layout Size</th>\n    <td>\n      <ul>\n        <li>Treated as an aspect ratio for scaling modes <b>Aspect-Fit</b> and <b>Aspect-Fill</b>.\n        <li>Treated as a fractional value between 0.0 and 1.0 for scaling mode <b>Proportional</b>. Note that Godot allows\n            values to be entered as e.g. <code>32.0/1024.0</code> for convenience.\n        <li>Treated as a pixel value for scaling mode <b>Fixed</b>.\n        <li>Treated as a default pixel value for scaling mode <b>Parameter</b>.\n            For example, if <b>Width Parameter</b> is undefined, <code>x</code> will be used as a pixel width.\n      </ul>\n    </td>\n  </tr>\n  <tr>\n    <th>Reference Node</th>\n    <td>\n      When a scaling mode is set to <b>Proportional</b>, by default the <b>Layout Size</b> is taken to be a fraction\n      of the layout size available to this node - 1.0 for full size, 0.5 for half size, and so on. If <b>Reference Node</b>\n      is set, <b>Layout Size</b> is a fraction of the reference node's size instead. The reference node should be\n      in a higher subtree (closer to the root) than the node referencing it.\n    </td>\n  </tr>\n  <tr>\n    <th>Width Parameter</th>\n    <td>\n      When <b>Horizontal Mode</b> is set to <b>Parameter</b>, <b>Width Parameter</b> is the name of the subtree\n      parameter to use as the pixel width for this node. If the name or the parameter is undefined, the <code>x</code>\n      component of <b>Layout Size</b> is used as the pixel width.\n    </td>\n  </tr>\n  <tr>\n    <th>Height Parameter</th>\n    <td>\n      when <b>Vertical Mode</b> is set to <b>Parameter</b>, <b>Height Parameter</b> is the name of the subtree\n      parameter to use as the pixel height for this node. If the name or the parameter is undefined, the <code>y</code>\n      component of <b>Layout Size</b> is used as the pixel height.\n    </td>\n  </tr>\n  <tr>\n    <th>Parameters</th>\n    <td>\n      If this node is the root of a GameGUI subtree (meaning its parent does not extend <code>GGComponent</code>),\n      any parameters defined using <code>set_parameter(name:String,value:Variant)</code> will be stored here.\n      Parameters can be added, inspected, or modified in the Editor as well.\n    </td>\n  </tr>\n  <tr>\n    <th>Clip Contents</th>\n    <td>\n      This inherited property is useful for clipping the overflow of child components that are larger than their parent.\n    </td>\n  </tr>\n  <tr>\n    <th>Custom Minimum Size [IGNORED]</th>\n    <td>\n      This inherited property is <b>ignored</b> by GameGUI. Instead wrap components in a GGLimitedSizeComponent with more flexible\n      minimum size options.\n    </td>\n  </tr>\n  <tr>\n    <th>Container Sizing: Horizontal and Vertical</th>\n    <td>\n      GGComponent respects the inherited <b>Horizontal and Vertical Container Sizing</b> properties for aligning child content that is smaller than the parent.\n      Note that <b>Fill</b> is treated as <b>Center</b> and <b>Expand</b> is ignored.\n    </td>\n  </tr>\n  <tr>\n    <th>Container Sizing: Stretch Ratio</th>\n    <td>\n      GGComponent respects the inherited <b>Stretch Ratio</b> property for setting the proportional expansion weights\n      of <b>Expand-to-Fill</b> components.\n    </td>\n  </tr>\n</table>\n\n### GGHBox\n\n![GGHBox](Media/Images/Icons/GGHBox.png)\n\nLays out children in a horizontal row. Here is the general algorithm:\n\n1. Fixed-width, aspect-width, and GGLimitedSizeComponent minimum-width children are given their fixed or minimum width, with aspect width being calculated using the height of the GGHBox.\n2. Remaining width is distributed among remaining expand-to-fill-width children according to their stretch ratio weights.\n\n### GGVBox\n\n![GGVBox](Media/Images/Icons/GGVBox.png)\n\nLays out children in a vertical row. Here is the general algorithm:\n\n1. Fixed-height, aspect-height, and GGLimitedSizeComponent minimum-height children are given their fixed or minimum height, with aspect height being calculated using the width of the GGVBox.\n2. Remaining height is distributed among remaining expand-to-fill-height children according to their stretch ratio weights.\n\n\n## Additional Layout Components\n\n### GGFiller\n\n![GGFiller](Media/Images/Icons/GGFiller.png)\n\nA GGComponent with an icon and default name that indicates its purpose is to fill up extra space. There is no technical difference between a GGFiller and a GGComponent.\n\n\n### GGInitialWindowSize\n\n![GGInitialWindowSize](Media/Images/Icons/GGInitialWindowSize.png)\n\nIF it is the root node of a scene, sets the window size to its own size on launch. Useful for testing independent UI component scenes at typical aspect ratios. If it is not the root scene node the window size is unaffected.\n\n#### Properties\n\n![GGInitialWindowSize](Media/Images/GGInitialWindowSize.png)\n\nProperty|Description\n--------|-----------\n**Initial Window Size** | Arbitrary pixel dimensions for the initial window size. This can be set by editing the numbers or by dragging the component's bounding box handles in the 2D view.\n\n### GGLimitedSizeComponent\n\n![GGLimitedSizeComponent](Media/Images/Icons/GGLimitedSizeComponent.png)\n\nApplies minimum and/or maximum sizes to its child content.\n\n#### Properties\n\n![GGLimitedSizeComponent](Media/Images/GGLimitedSizeComponent.png)\n\nProperty|Description\n--------|-----------\n**Min Width** | Selects the minimum width mode: Unlimited, Fixed, or Parameter.\n**Min Height** | Selects the minimum height mode: Unlimited, Fixed, or Parameter.\n**Min Size** | The minimum size pixel values in Fixed mode and the default pixel sizes in Parameter mode if the parameter is undefined.\n**Min Width Parameter** | The name of a subtree parameter to use as the minimum pixel width.\n**Min Height Parameter** | The name of a subtree parameter to use as the minimum pixel height.\n**Max Width** | Selects the maximum width mode: Unlimited, Fixed, or Parameter.\n**Max Height** | Selects the maximum height mode: Unlimited, Fixed, or Parameter.\n**Max Size** | The maximum size pixel values in Fixed mode and the default pixel sizes in Parameter mode if the parameter is undefined.\n**Max Width Parameter** | The name of a subtree parameter to use as the maximum pixel width.\n**Max Height Parameter** | The name of a subtree parameter to use as the maximum pixel height.\n\n### GGMarginLayout\n\n![GGMarginLayout](Media/Images/Icons/GGMarginLayout.png)\n\nAdds inside margins to the layout of its child content.\n\n#### Properties\n\n![GGMarginLayout](Media/Images/GGMarginLayout.png)\n\n##### Margin Type: Proportional (No Reference Node)\n\nProperty|Description\n--------|-----------\n**Left Margin<br>Top Margin** | The top and left margins as a proportion of the available area, `0.0`-`1.0`.\n**Right Margin<br>Bottom Margin** | The right and bottom margins as a proportion of the available area, `0.0`-`1.0`. Note that `1.0` means \"no margin\" and `0.9` means a 10% margin because it begins 90% of the way into the available area.\n\n##### Margin Type: Proportional (With Reference Node)\n\nProperty|Description\n--------|-----------\n**Left Margin<br>Top Margin** | The top and left margins as a proportion of the reference node size, `0.0`-`1.0`.\n**Right Margin<br>Bottom Margin** | The right and bottom margins as a proportion of the reference node size, 0.0-1.0. Note that `0.0` means \"no margin\" and `0.1` means a margin that is 10% of the size of the reference node.\n\n##### Margin Type: Fixed\nProperty|Description\n--------|-----------\n**Left Margin<br>Top Margin<br>Right Margin<br>Bottom Margin** | The pixel sizes of the four margins. `0` means \"no margin\".\n\n\n##### Margin Type: Parameter\nProperty|Description\n--------|-----------\n**Left Parameter<br>Top Parameter<br>Right Parameter<br>Bottom Parameter** | The names of subtree parameters that define the pixel sizes of the four margins. If Left Parameter is undefined then Left Margin is used as the pixel size, and so on.\n\n### GGOverlay\n\n![GGOverlay](Media/Images/Icons/GGOverlay.png)\n\nPositions its child content arbitrarily within its layout area in a sprite-like manner.\n\n![GGOverlay](Media/Images/GGOverlay.gif)\n\n#### Properties\n\nProperty|Description\n--------|-----------\n**Positioning Mode** | Selects the Positioning Mode: Proportional, Fixed, or Parameter.\n**Child X** | The horizontal child offset: `0.0`-`1.0` in Proportional mode, a pixel offset in Fixed mode, and a default pixel offset for an undefined parameter in Parameter mode.\n**Child Y** | The vertical child offset: `0.0`-`1.0` in Proportional mode, a pixel offset in Fixed mode, and a default pixel offset for an undefined parameter in Parameter mode.\n**Child X Parameter** | The name of the subtree parameter for the horizontal child offset in Parameter mode.\n**Child Y Parameter** | The name of the subtree parameter for the vertical child offset in Parameter mode.\n**H Scale Factor** | Selects the horizontal child layout area scaling mode: Constant or Parameter.\n**V Scale Factor** | Selects the vertical child layout area scaling mode: Constant or Parameter.\n**H Scale Constant** | The horizontal child layout area scaling factor in Constant mode and the default scaling factor for an undefined parameter in Parameter mode.\n**V Scale Constant** | The vertical child layout area scaling factor in Constant mode and the default scaling factor for an undefined parameter in Parameter mode.\n**H Scale Parameter** | The name of the subtree parameter for the horizontal child layout area scaling factor.\n**V Scale Parameter** | The name of the subtree parameter for the vertical child layout area scaling factor.\n\n## Scaling Text Components\n\n### GGButton\n\n![GGButton](Media/Images/Icons/GGButton.png)\n\nA Button that can auto-scale its text size.\n\n#### Text Size Modes\n\n![GGButton](Media/Images/GGButton.png)\n\nMode|Description\n--------|-----------\n**Default** | The size of the button text is fixed and is set via standard Button control properties. Extended GGButton properties have no effect.\n**Scale** | Set the button text size as desired for the current layout, assign a size Reference Node (which can be the GGButton itself), and then switch Text Size Mode to Scale. The GGButton's text size will then automatically scale with the size of the reference node.\n**Parameter** | The size of the button text is determined by the subtree parameter named by the Text Size Parameter property.\n\n### GGLabel\n\n![GGLabel](Media/Images/Icons/GGLabel.png)\n\nA Label that can auto-scale its text size.\n\n#### Text Size Modes\n\n![GGLabel](Media/Images/GGLabel.png)\n\nMode|Description\n--------|-----------\n**Default** | The size of the label text is fixed and is set via standard Label control properties. Extended GGLabel properties have no effect.\n**Scale** | Set the label text size as desired for the current layout, assign a size Reference Node (typically the GGLabel's parent, as the GGLabel's size is dependent on the text size), and then switch Text Size Mode to Scale. The GGLabel's text size will then automatically scale with the size of the reference node.\n**Parameter** | The size of the label text is determined by the subtree parameter named by the Text Size Parameter property.\n\n### GGRichTextLabel\n\n![GGRichTextLabel](Media/Images/Icons/GGRichTextLabel.png)\n\nA RichTextLabel that can auto-scale its text size.\n\n#### Text Size Modes\n\n![GGRichTextLabel](Media/Images/GGRichTextLabel.png)\n\nMode|Description\n--------|-----------\n**Default** | The size of the label text is fixed and is set via standard Label control properties. Extended GGRichTextLabel properties have no effect.\n**Scale** | Set the label text size as desired for the current layout, assign a size Reference Node (typically the GGRichTextLabel's parent, as the GGRichTextLabel's size is dependent on the text size), and then switch Text Size Mode to Scale. The GGRichTextLabel's text size will then automatically scale with the size of the reference node.\n**Parameter** | The size of the label text is determined by the subtree parameter named by the Text Size Parameter property.\n\n## Image Components\n\n### GGTextureRect\n\n![GGTextureRect](Media/Images/Icons/GGTextureRect.png)\n\nGGTextureRect is a TextureRect that supports GameGUI scaling modes and auto-configures appropriate properties.\n\nOnce a texture is assigned to the Texture property, the GGTextureRect auto-configures itself by setting the following properties:\n\n- Horizontal and Vertical Scaling Modes are set to Aspect Fit.\n- Layout Size is set to the original pixel dimensions of the texture.\n- Expand Mode is set to Ignore Size.\n- Is Configured is set to `true`.\n\nIf the configuration above becomes modified and you wish to restore the original configuration, uncheck Is Configured and the GGTextureRect will be reconfigured.\n\n![GGTextureRect](Media/Images/GGTextureRect.png)\n\nFor most purposes the images that provide the textures for GGTextureRect nodes should be imported with mipmaps enabled and the texture filter mode should be set to Linear Mipmap so that images look good when scaled down. To import with mipmaps, select one or more images in the FileSystem panel, switch from the Scene tab to the Import tab, tick the `Mipmaps > Generate` checkbox, and click `Reimport(*)`.\n\n![TextureImportSettings](Media/Images/TextureImportSettings.png)\n\nTo enable Linear Mipmap mode, inspect the properties of the scene's root node (or the CanvasItem parent node that's closest to the scene root) and change `Texture > Filter` to `Linear Mipmap`. As nodes use mode `Inherited` by default, all other nodes will now use Linear Mipmap mode.\n\n![TextureImportSettings](Media/Images/LinearMipmapMode.png)\n\n### GGNinePatchRect\n\n![GGNinePatchRect](Media/Images/Icons/GGNinePatchRect.png)\n\nGGNinePatchRect replicates NinePatchRect functionality and makes the following improvement: when the bounds of a GGNinePatchRect are smaller than its corners, the corners are proportionally shrunk to fit the available bounds.\n\n![GGLayoutConfig](Media/Images/GGNinePatchRect.gif)\n\n(The 9-patch image is from [pixy.org](https://pixy.org/474956/))\n\n#### Properties\n\n![GGNinePatchRect](Media/Images/GGNinePatchRect.png)\n\nProperty|Description\n--------|-----------\n**Texture** | The texture to use for the nine-patch rect.\n**Draw Center** | Check to draw the center patch of the nine-patch rect (default); uncheck to omit the center patch.\n**Patch Margin: Left** | The pixel width of the left side of the texture that comprises three patches: top-left, left, and bottom-left.\n**Patch Margin: Top** | The pixel height of the top side of the texture that comprises three patches: top-left, top, and top-right.\n**Patch Margin: Right** | The pixel width of the right side of the texture that comprises three patches: top-right, right, and bottom-right.\n**Patch Margin: Bottom** | The pixel height of the bottom side of the texture that comprises three patches: bottom-left, bottom, and bottom-right.\n**Fill Mode: Horizontal** | The horizontal fill mode for each patch.\n**Fill Mode: Vertical**   | Ther vertical fill mode for each patch.\n\nFill Mode | Description\n----------|------------\nStretch   | Stretch or compress each patch to cover the available space.\nTile      | Repeatedly tile each patch at its original pixel size to cover the available space.\nTile Fit  | Tile each patch, stretching slightly as necessary to ensure a whole number of tiles fit in the available space.\n\n\n## Parameter Components\n\n### GGLayoutConfig\n\n![GGLayoutConfig](Media/Images/Icons/GGLayoutConfig.png)\n\nPlace this node near the root of the GameGUI scene or subtree, extend the script, make it a `@tool`, override `func _on_begin_layout(display_size:Vector2)`, and call inherited method `set_parameter(name,value)` with various computed values related to the current display size. Those parameters can be automatically used by other GameGUI nodes by setting their sizing mode to *Parameter* and supplying the desired parameter name.\n\n![GGLayoutConfig](Media/Images/ScalingMode-Parameter.gif)\n\nAll GameGUI components define the following methods. A \"subtree root\" is the highest-level ancestor in an unbroken line of GameGUI component ancestors from the component that one of these methods is called on.\n\nMethod | Description\n-------|------------------\n`has_parameter(name:String)->bool` | Returns true if the subtree root's `parameters` dictionary defines a value with the specified name.\n`set_parameter(name:String,value:Variant)` | Sets a value in the subtree root's `parameters` dictionary.\n`get_parameter(name:String,default_result:Variant=0)->Variant` | Returns the specified value from the subtree root's `parameters` dictionary, if it exists, or else returns `default_result` if the value doesn't exist.\n\n### GGParameterSetter\n\n![GGLayoutConfig](Media/Images/Icons/GGParameterSetter.png)\n\nAfter a GGParameterSetter has been sized according to its settings, it sets specified subtree parameters to its own width and/or height, which can then be used to set the size of other components when they're in *Parameter* sizing mode.\n\n#### Properties\n\n![GGParameterSetter](Media/Images/GGParameterSetter.png)\n\nProperty|Description\n--------|-----------\n**Width Store** | The name of the subtree parameter to store this node's width in.\n**Height Store** | The name of the subtree parameter to store this node's height in.\n\n"
  },
  {
    "path": "addons/GameGUI/GGButton.gd",
    "content": "@tool\n\nclass_name GGButton\nextends Button\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n## Check to lock in the current font size and reference node height as reference\n## values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = floor(reference_node.size.y)\n\t\t\t\treference_font_size = get_theme_font_size( \"font_size\" )\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n\t\trequest_layout()\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [Button] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of the font.\n@export var reference_font_size := 0 :\n\tset(value):\n\t\tif reference_font_size == value: return\n\t\treference_font_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use when [member text_size_mode] is [b]Parameter[/b].\n@export var text_size_parameter:String = \"\" :\n\tset(value):\n\t\tif text_size_parameter == value: return\n\t\ttext_size_parameter = value\n\t\tif has_parameter( text_size_parameter ):\n\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_size := 0\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tvar cur_size = get_theme_font_size( \"font_size\" )\n\t\t\tif cur_size != _current_font_size:\n\t\t\t\t_current_font_size = cur_size\n\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif _current_font_size != reference_font_size:\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\trequest_layout()\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\tif text == \"\": text = \"Button\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\t_current_font_size = get_theme_font_size( \"font_size\" )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of the font to dynamically size it\n\t\t\t\tvar cur_size = reference_font_size * cur_scale\n\t\t\t\tif cur_size:\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n"
  },
  {
    "path": "addons/GameGUI/GGComponent.gd",
    "content": "@tool\n\n## General-purpose layout box and base class to other GameGUI layout components. Children are\n## stacked in layers.\n##\n## Nodes that do not extend [GGComponent] can still be children of GameGUI nodes in one of two\n## ways.[br][br]\n## First, GameGUI adapter code can be added to an extended class. See the [GGTextureRect] source\n## code for an example that can be copy-pasted into other extended classes with only small\n## modifications required.[br][br]\n## Second, any non-[GGComponent] Control node that does not contain GameGUI adapter code will be\n## treated as a component with [member horizontal_mode] and [member vertical_mode] both set to\n## [b]Expand To Fill[/b]. The size of the node can be further managed by making it a child of\n## a [GGComponent] that has other sizing modes.[br][br]\nclass_name GGComponent\nextends Container\n\n#-------------------------------------------------------------------------------\n# SIGNALS\n#-------------------------------------------------------------------------------\n\n## Signals the beginning of the layout process for a GGComponent subtree.\n## This signal is only emitted by the top-level root of a GGComponent subtree.\nsignal begin_layout\n\n## Signals the end of the layout process for a GGComponent subtree.\n## This signal is only emitted by the top-level root of a GGComponent subtree.\nsignal end_layout\n\n#-------------------------------------------------------------------------------\n# ENUMS\n#-------------------------------------------------------------------------------\n\n## The possible horizontal and vertical sizing modes for a GameGUI component.\nenum ScalingMode\n{\n\tEXPAND_TO_FILL, ## Fill all available space along this dimension.\n\tASPECT_FIT,     ## Dynamically adjusts size to maintain aspect ratio [member layout_size].x:[member layout_size].y, just small enough to entirely fit available space.\n\tASPECT_FILL,    ## Dynamically adjusts size to maintain aspect ratio [member layout_size].x:[member layout_size].y, just large enough to entirely fill available space.\n\tPROPORTIONAL,   ## The layout size represents a proportional fraction of 1) the available area or 2) the size of the [member reference_node] if defined.\n\tSHRINK_TO_FIT,  ## Make the size just large enough to contain all child nodes in their layout.\n\tFIXED,          ## Fixed pixel size along this dimension.\n\tPARAMETER       ## One of the subtree [member parameters] is used as the size.\n}\n\n## The text sizing mode for [GGLabel], [GGRichTextLabel], and [GGButton].\nenum TextSizeMode\n{\n\tDEFAULT,    ## Text size is whatever size you assign in the editor.\n\tSCALE,      ## Text scales with the size of a reference node.\n\tPARAMETER   ## Text size is set to the value of one of the defined [member parameters].\n}\n\n## The texture fill mode used by [method fill_texture].\nenum FillMode\n{\n\tSTRETCH,  # Stretch or compress each patch to cover the available space.\n\tTILE,     # Repeatedly tile each patch at its original pixel size to cover the available space.\n\tTILE_FIT  # Tile each patche, stretching slightly as necessary to ensure a whole number of tiles fit in the available space.\n}\n\n\n#-------------------------------------------------------------------------------\n# PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## An optional node to use as a size reference for [b]Proportional[/b] scaling\n## mode. The reference node must be in a subtree higher in the scene tree than\n## this node. Often the size reference is an invisible root-level square-aspect\n## component; this allows same-size horizontal and vertical proportional spacers.\n@export var reference_node:Control = null :\n\tset(value):\n\t\tif reference_node != value:\n\t\t\treference_node = value\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Parameter definitions for nodes that use scaling mode PARAMETER. Parameters are stored\n## the root of a GGComponent subtree. Use [method get_parameter], [method has_parameter], and\n## [method set_parameter] to access parameters from any subtree nodes.\n@export var parameters := {} :\n\tset(value):\n\t\tif parameters == value: return\n\t\tparameters = value\n\t\trequest_layout()\n\n# A top-level GGComponent is one that has no GGComponent parent.\n# It oversees the layout of its descendent nodes.\nvar _is_top_level := false\nvar _layout_stage := 0 # top-level component use. 0=layout finished, 1=layout requested, 2=performing layout\n\n#-------------------------------------------------------------------------------\n# EXTERNAL API\n#-------------------------------------------------------------------------------\n\n## Utility method that draws a texture with any combination of horizontal and vertical fill modes: Stretch, Tile, Tile Fit.\n## Used primarily by [GGComponent].\nfunc fill_texture( texture:Texture2D, dest_rect:Rect2, src_rect:Rect2, horizontal_fill_mode:FillMode=FillMode.STRETCH,\n\t\tvertical_fill_mode:FillMode=FillMode.STRETCH, modulate:Color=Color(1,1,1,1) ):\n\tif dest_rect.size.x <= 0 or dest_rect.size.y <= 0: return\n\n\tif horizontal_fill_mode == FillMode.TILE and src_rect.size.x > dest_rect.size.x:\n\t\thorizontal_fill_mode = FillMode.TILE_FIT\n\n\tif vertical_fill_mode == FillMode.TILE and src_rect.size.y > dest_rect.size.y:\n\t\tvertical_fill_mode = FillMode.TILE_FIT\n\n\tmatch horizontal_fill_mode:\n\t\tFillMode.TILE:\n\t\t\tvar tile_size = src_rect.size\n\t\t\tvar dest_pos  = dest_rect.position\n\t\t\tvar dest_w = dest_rect.size.x\n\t\t\tvar dest_h = dest_rect.size.y\n\t\t\twhile dest_w > 0:\n\t\t\t\tif tile_size.x <= dest_w:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(tile_size.x,dest_h) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\telse:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\tvar _src_rect = Rect2( src_rect.position, Vector2(dest_w,src_rect.size.y) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, _src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\treturn\n\n\t\t\t\tdest_pos += Vector2( tile_size.x, 0 )\n\t\t\t\tdest_w   -= tile_size.x\n\t\t\treturn\n\n\t\tFillMode.TILE_FIT:\n\t\t\tvar n = int( (dest_rect.size.x / src_rect.size.x) + 0.5 )\n\t\t\tif n == 0:\n\t\t\t\tfill_texture( texture, dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\telse:\n\t\t\t\tvar tile_size = Vector2( dest_rect.size.x / n, src_rect.size.y )\n\t\t\t\tvar dest_pos  = dest_rect.position\n\t\t\t\tvar dest_w = dest_rect.size.x\n\t\t\t\tvar dest_h = dest_rect.size.y\n\t\t\t\twhile dest_w > 0:\n\t\t\t\t\tif tile_size.x <= dest_w:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(tile_size.x,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\telse:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, FillMode.STRETCH, vertical_fill_mode, modulate )\n\t\t\t\t\t\treturn\n\n\t\t\t\t\tdest_pos += Vector2( tile_size.x, 0 )\n\t\t\t\t\tdest_w   -= tile_size.x\n\t\t\treturn\n\n\tmatch vertical_fill_mode:\n\t\tFillMode.TILE:\n\t\t\tvar tile_size = src_rect.size\n\t\t\tvar dest_pos  = dest_rect.position\n\t\t\tvar dest_w = dest_rect.size.x\n\t\t\tvar dest_h = dest_rect.size.y\n\t\t\twhile dest_h > 0:\n\t\t\t\tif tile_size.y <= dest_h:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w, tile_size.y) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\telse:\n\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\tvar _src_rect = Rect2( src_rect.position, Vector2(src_rect.size.x,dest_h) )\n\t\t\t\t\tfill_texture( texture, _dest_rect, _src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\treturn\n\n\t\t\t\tdest_pos += Vector2( 0, tile_size.y )\n\t\t\t\tdest_h   -= tile_size.y\n\t\t\treturn\n\n\t\tFillMode.TILE_FIT:\n\t\t\tvar n = int( (dest_rect.size.y / src_rect.size.y) + 0.5 )\n\t\t\tif n == 0:\n\t\t\t\tfill_texture( texture, dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\telse:\n\t\t\t\tvar tile_size = Vector2( src_rect.size.x, dest_rect.size.y / n )\n\t\t\t\tvar dest_pos  = dest_rect.position\n\t\t\t\tvar dest_w = dest_rect.size.x\n\t\t\t\tvar dest_h = dest_rect.size.y\n\t\t\t\twhile dest_h > 0:\n\t\t\t\t\tif tile_size.y <= dest_h:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w, tile_size.y) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\telse:\n\t\t\t\t\t\tvar _dest_rect = Rect2( dest_pos, Vector2(dest_w,dest_h) )\n\t\t\t\t\t\tfill_texture( texture, _dest_rect, src_rect, horizontal_fill_mode, FillMode.STRETCH, modulate )\n\t\t\t\t\t\treturn\n\n\t\t\t\t\tdest_pos += Vector2( 0, tile_size.y )\n\t\t\t\t\tdest_h   -= tile_size.y\n\t\t\treturn\n\n\t# Horizontal and vertical fill are both STRETCH\n\tdraw_texture_rect_region( texture, dest_rect, src_rect, modulate )\n\n## Returns the specified parameter's value if it exists in the [member parameters]\n## of this node or a [GGComponent] ancestor. If it doesn't exist, returns\n## [code]0[/code] or a specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this GGComponent subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in the\n## [member parameters] of this node or one of its ancestors.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tif _is_top_level:\n\t\tif _layout_stage == 0:\n\t\t\t_layout_stage = 1\n\t\t\tqueue_sort()\n\telse:\n\t\tvar top = get_top_level_component()\n\t\tif top: top.request_layout()\n\n#-------------------------------------------------------------------------------\n# KEY OVERRIDES\n#-------------------------------------------------------------------------------\nfunc _on_resolve_size( available_size:Vector2 ):\n\t# Overrideable.\n\t# Called just before this component's size is resolved.\n\t# Override and adjust this component's size if desired.\n\tpass\n\nfunc _on_update_size():\n\t# Overrideable.\n\t# Called at the beginning of layout.\n\t# Override and adjust this GGComponent's size if desired.\n\tpass\n\nfunc _perform_child_layout( available_bounds:Rect2 ):\n\tfor i in range(get_child_count()):\n\t\t_perform_component_layout( get_child(i), available_bounds )\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\tfor i in range(get_child_count()):\n\t\t_resolve_child_size( get_child(i), available_size, limited )\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\t# Override in extended classes.\n\tsize.y = _get_largest_child_size().y\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\t# Override in extended classes.\n\tsize.x = _get_largest_child_size().x\n\nfunc _with_margins( rect:Rect2 )->Rect2:\n\treturn rect\n\n#-------------------------------------------------------------------------------\n# INTERNAL GAMEGUI API\n#-------------------------------------------------------------------------------\n\nfunc _get_largest_child_size()->Vector2:\n\t# 'x' and 'y' will possibly come from different children.\n\tvar max_w := 0.0\n\tvar max_h := 0.0\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:  # includes GGComponent\n\t\t\tmax_w = max( max_w, child.size.x )\n\t\t\tmax_h = max( max_h, child.size.y )\n\treturn Vector2(max_w,max_h)\n\nfunc _get_sum_of_child_sizes()->Vector2:\n\tvar sum := Vector2(0,0)\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tsum += child.size\n\treturn sum\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\tvar bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\t_perform_child_layout( bounds )\n\nfunc _place_component( component:Control, available_bounds:Rect2 ):\n\tcomponent.position = _rect_position_within_parent_bounds( component, component.size, available_bounds )\n\nfunc _rect_position_within_parent_bounds( component:Control, rect_size:Vector2, available_bounds:Rect2 )->Vector2:\n\tvar pos = available_bounds.position\n\n\tif component is Control:  # includes GGComponent\n\t\tif component.size_flags_horizontal & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tpos.x += floor( (available_bounds.size.x - rect_size.x) / 2 )\n\t\telif component.size_flags_horizontal & SizeFlags.SIZE_SHRINK_END:\n\t\t\tpos.x += available_bounds.size.x - rect_size.x\n\n\t\tif component.size_flags_vertical & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tpos.y += floor( (available_bounds.size.y - rect_size.y) / 2 )\n\t\telif component.size_flags_vertical & SizeFlags.SIZE_SHRINK_END:\n\t\t\tpos.y += available_bounds.size.y - rect_size.y\n\n\treturn pos\n\nfunc _resolve_child_size( child:Node, available_size:Vector2, limited:bool=false ):\n\tif not child.visible or not child is Control: return\n\n\tif child is GGComponent:\n\t\tchild._resolve_size( available_size, limited )\n\telse:\n\t\t_resolve_component_size( child, available_size )\n\t\t_resolve_shrink_to_fit_size( child, available_size )\n\nfunc _resolve_component_size( component:Node, available_size:Vector2 )->Vector2:\n\tvar component_size := available_size\n\n\tvar is_gg = component is GGComponent\n\n\tif is_gg or component.has_method(\"_on_resolve_size\"):\n\t\tcomponent._on_resolve_size( available_size )\n\n\tvar has_mode = is_gg or component.has_method(\"request_layout\")\n\tvar h_mode = component.horizontal_mode if has_mode else ScalingMode.EXPAND_TO_FILL\n\tvar v_mode = component.vertical_mode if has_mode else ScalingMode.EXPAND_TO_FILL\n\n\tmatch h_mode:\n\t\tScalingMode.EXPAND_TO_FILL:\n\t\t\tpass # use available width\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif not component is GGComponent: component_size.x = component.size.x\n\t\tScalingMode.ASPECT_FIT:\n\t\t\tif v_mode == ScalingMode.ASPECT_FILL:\n\t\t\t\tcomponent_size.x = floor( (available_size.y / component.layout_size.y) * component.layout_size.x )\n\t\t\telse:\n\t\t\t\tvar fit_x = floor( (available_size.y / component.layout_size.y) * component.layout_size.x )\n\t\t\t\tif fit_x <= available_size.x: component_size.x = fit_x\n\t\tScalingMode.ASPECT_FILL:\n\t\t\tif v_mode != ScalingMode.ASPECT_FIT:\n\t\t\t\tvar scale_x = (available_size.x / component.layout_size.x)\n\t\t\t\tvar scale_y = (available_size.y / component.layout_size.y)\n\t\t\t\tcomponent_size.x = floor( max(scale_x,scale_y) * component.layout_size.x )\n\t\tScalingMode.FIXED:\n\t\t\tcomponent_size.x = component.layout_size.x\n\t\tScalingMode.PARAMETER:\n\t\t\tcomponent_size.x = get_parameter( component.width_parameter, component.layout_size.x )\n\t\tScalingMode.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tcomponent_size.x = int(component.layout_size.x * reference_node.size.x)\n\t\t\telse:\n\t\t\t\tcomponent_size.x = int(component.layout_size.x * available_size.x)\n\n\tmatch v_mode:\n\t\tScalingMode.EXPAND_TO_FILL:\n\t\t\tpass # use available height\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif not component is GGComponent: component_size.y = component.size.y\n\t\tScalingMode.ASPECT_FIT:\n\t\t\tif h_mode == ScalingMode.ASPECT_FILL:\n\t\t\t\tcomponent_size.y = floor( (available_size.x / component.layout_size.x) * component.layout_size.y )\n\t\t\telse:\n\t\t\t\tvar fit_y = floor( (available_size.x / component.layout_size.x) * component.layout_size.y )\n\t\t\t\tif fit_y <= available_size.y: component_size.y = fit_y\n\t\tScalingMode.ASPECT_FILL:\n\t\t\tif h_mode != ScalingMode.ASPECT_FIT:\n\t\t\t\tvar scale_x = (available_size.x / component.layout_size.x)\n\t\t\t\tvar scale_y = (available_size.y / component.layout_size.y)\n\t\t\t\tcomponent_size.y = floor( max(scale_x,scale_y) * component.layout_size.y )\n\t\tScalingMode.FIXED:\n\t\t\tcomponent_size.y = component.layout_size.y\n\t\tScalingMode.PARAMETER:\n\t\t\tcomponent_size.y = get_parameter( component.height_parameter, component.layout_size.y )\n\t\tScalingMode.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tcomponent_size.y = int(component.layout_size.y * reference_node.size.y)\n\t\t\telse:\n\t\t\t\tcomponent_size.y = int(component.layout_size.y * available_size.y)\n\n\tif not component is GGComponent or not component._is_top_level: component.size = component_size\n\n\treturn component_size\n\nfunc _perform_component_layout( component:Node, available_bounds:Rect2 ):\n\tif component is GGComponent:\n\t\tcomponent._perform_layout(available_bounds)\n\n\telif component is Control:\n\t\t_place_component( component, available_bounds )\n\nfunc _resolve_shrink_to_fit_size( component:Node, available_size:Vector2 )->Vector2:\n\tif not (component is GGComponent or component.has_method(\"request_layout\")): return available_size\n\n\tvar component_size := available_size\n\n\tmatch component.horizontal_mode:\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif component is GGComponent:\n\t\t\t\t_resolve_shrink_to_fit_width( available_size )\n\t\t\telse:\n\t\t\t\tcomponent_size.x = component.size.x\n\n\tmatch component.vertical_mode:\n\t\tScalingMode.SHRINK_TO_FIT:\n\t\t\tif component is GGComponent:\n\t\t\t\t_resolve_shrink_to_fit_height( available_size )\n\t\t\telse:\n\t\t\t\tcomponent_size.y = component.size.y\n\n\treturn component_size\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\t# limited\n\t#   Don't recurse to children unless necessary (Shrink to Fit mode). limited==true indicates\n\t#   that several sizing options are being checked by GGHBox/GGVBox/etc., so we only need\n\t#   to figure out the size of this node.\n\tavailable_size = _resolve_component_size( self, available_size )\n\tvar inner_size = _with_margins( Rect2(Vector2(0,0), available_size) ).size\n\tif not limited or ScalingMode.SHRINK_TO_FIT in [horizontal_mode,vertical_mode]:\n\t\t_resolve_child_sizes( inner_size, limited )\n\t\t_resolve_shrink_to_fit_size( self, available_size )\n\nfunc _update_layout():\n\tif not _is_top_level or _layout_stage == 2: return\n\t_layout_stage = 2\n\n\t_update_safe_area()\n\n\tbegin_layout.emit()\n\n\t_update_size()\n\t_resolve_size( size )\n\t_perform_layout( _with_margins(Rect2(Vector2(0,0),size)) )\n\n\tend_layout.emit()\n\t_layout_stage = 0\n\nfunc _update_size():\n\t_on_update_size()\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is GGComponent:\n\t\t\tchild._update_size()\n\t\telif child.has_method(\"_on_update_size\"):\n\t\t\tchild._on_update_size()\n\n#-------------------------------------------------------------------------------\n# INTERNAL NODE API\n#-------------------------------------------------------------------------------\nfunc _disconnect( sig:Signal, callback:Callable ):\n\tif sig.is_connected(callback):\n\t\tsig.disconnect( callback )\n\nfunc _enter_tree():\n\t_is_top_level = not (get_parent() is GGComponent)\n\tif _is_top_level:\n\t\tresized.connect( request_layout )\n\t\tsort_children.connect( _on_sort_children )\n\t\t_update_safe_area()\n\n\tchild_entered_tree.connect( _on_child_entered_tree )\n\tchild_exiting_tree.connect( _on_child_exiting_tree )\n\tchild_order_changed.connect( request_layout )\n\n\trequest_layout()\n\nfunc _exit_tree():\n\tif _is_top_level:\n\t\t_disconnect( resized, request_layout )\n\t\t_disconnect( sort_children, _on_sort_children )\n\n\t_disconnect( child_entered_tree, _on_child_entered_tree )\n\t_disconnect( child_exiting_tree, _on_child_exiting_tree )\n\t_disconnect( child_order_changed, request_layout )\n\n\trequest_layout()\n\nfunc _on_child_entered_tree( child:Node ):\n\tif child is Control: child.visibility_changed.connect( request_layout )\n\tif Engine.is_editor_hint() and child is Control:\n\t\tchild.minimum_size_changed.connect( request_layout )\n\t\tchild.resized.connect( request_layout )\n\t\tchild.size_flags_changed.connect( request_layout )\n\n\trequest_layout()\n\nfunc _on_child_exiting_tree( child:Node ):\n\tif child is Control: _disconnect( child.visibility_changed, request_layout )\n\tif Engine.is_editor_hint() and child is Control:\n\t\t_disconnect( child.minimum_size_changed, request_layout )\n\t\t_disconnect( child.resized, request_layout )\n\t\t_disconnect( child.size_flags_changed, request_layout )\n\n\trequest_layout()\n\nfunc _on_child_visibility_changed():\n\trequest_layout()\n\nfunc _on_sort_children():\n\t_update_layout()\n\nfunc _update_safe_area():\n\tvar viewport = get_viewport()\n\tif viewport:\n\t\tvar display_size = viewport.get_visible_rect().size\n\t\tvar safe_area = Rect2( Vector2(0,0), display_size )\n\n\t\tmatch DisplayServer.window_get_mode():\n\t\t\tDisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN, \\\n\t\t\tDisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN:\n\t\t\t\tsafe_area = DisplayServer.get_display_safe_area()\n\n\t\tset_parameter( \"safe_area_left_margin\", safe_area.position.x )\n\t\tset_parameter( \"safe_area_top_margin\", safe_area.position.y )\n\t\tset_parameter( \"safe_area_right_margin\", display_size.x - safe_area.end.x )\n\t\tset_parameter( \"safe_area_bottom_margin\", display_size.y - safe_area.end.y )\n\n"
  },
  {
    "path": "addons/GameGUI/GGFiller.gd",
    "content": "@tool\n\n## A GameGUI node that by default will expand to fill any extra space within a layout.\n## There is no functional difference between [GGFiller] and [GGComponent], but the name [GGFiller]\n## can make a node tree more readable.\nclass_name GGFiller\nextends GGComponent\n"
  },
  {
    "path": "addons/GameGUI/GGHBox.gd",
    "content": "@tool\n\n## A GameGUI layout that arranges its child elements in a horizontal row.\nclass_name GGHBox\nextends GGComponent\n\nenum HorizontalContentAlignment\n{\n\tLEFT,    ## Left-align the content.\n\tCENTER,  ## Center the content.\n\tRIGHT    ## Right-align the content.\n}\n\n## Specify the horizontal alignment of the content as a whole.\n@export var content_alignment := HorizontalContentAlignment.CENTER :\n\tset(value):\n\t\tcontent_alignment = value\n\t\trequest_layout()\n\nvar _min_widths:Array[int] = []\nvar _max_widths:Array[int] = []\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\t# Resolve for and collect min and max sizes\n\t_max_widths.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, available_size, true )\n\t\t\t_max_widths.push_back( int(child.size.x) )\n\t\telse:\n\t\t\t_max_widths.push_back( 0 )\n\n\t_min_widths.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, Vector2(0,available_size.y), true )\n\t\t\t_min_widths.push_back( int(child.size.x) )\n\t\telse:\n\t\t\t_min_widths.push_back( 0 )\n\n\tvar expand_count := 0\n\tvar total_stretch_ratio := 0.0\n\tvar fixed_width := 0\n\tvar min_width := 0\n\n\t# Leaving other children at their minimum width, set aspect-fit, proportional,\n\t# and shrink-to-fit width nodes to their maximum size.\n\tvar modes = [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.SHRINK_TO_FIT]\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\n\t\tvar has_mode = child is GGComponent or child.has_method(\"request_layout\")\n\t\tif has_mode and child.horizontal_mode in modes:\n\t\t\t_resolve_child_size( child, available_size, limited )\n\t\t\tvar w = int(child.size.x)\n\t\t\tmin_width += w\n\t\t\tfixed_width += w\n\t\t\t_min_widths[i] = w\n\t\t\t_max_widths[i] = w\n\n\t\telse:\n\t\t\tvar w = _min_widths[i]\n\t\t\tmin_width += w\n\n\t\t\tif _min_widths[i] == _max_widths[i]:\n\t\t\t\tfixed_width += w\n\t\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\t\t\telse:\n\t\t\t\texpand_count += 1\n\t\t\t\ttotal_stretch_ratio += child.size_flags_stretch_ratio\n\n\tif expand_count == 0 or total_stretch_ratio == 0.0 or min_width >= available_size.x: return\n\n\tvar excess_width = int(available_size.x - fixed_width)\n\tvar remaining_width = excess_width\n\n\t# Find children with a min width larger than their portion. Let them keep their min width and adjust remaining.\n\tvar remaining_total_stretch_ratio = total_stretch_ratio\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif w < _min_widths[i]:\n\t\t\tw = _min_widths[i]\n\t\t\tremaining_width -= w\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_widths[i] = _max_widths[i]  # skip this node in the next pass\n\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\n\texcess_width = remaining_width\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# Distribute remaining width next to children with a max width smaller than their portion.\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif w > _max_widths[i]:\n\t\t\tw = _max_widths[i]\n\t\t\t_resolve_child_size( child, Vector2(w,available_size.y), limited )\n\t\t\tremaining_width -= w\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_widths[i] = _max_widths[i]  # skip this node in the next pass\n\n\texcess_width = remaining_width\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# If this GGHBox is shrink-to-fit width then we're done; don't add remaining space to\n\t# the children.\n\tif horizontal_mode == GGComponent.ScalingMode.SHRINK_TO_FIT:\n\t\treturn\n\n\t# Distribute remaining width\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_widths[i] == _max_widths[i]: continue\n\n\t\tvar w := 0\n\t\tif expand_count == 1: w = remaining_width\n\t\telse:                 w = int( excess_width * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\t_resolve_child_size( child, Vector2(w,available_size.y), limited )\n\t\tremaining_width -= w\n\t\texpand_count -= 1\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\tsize.x = _get_sum_of_child_sizes().x\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\tsize.y = _get_largest_child_size().y\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\n\tvar inner_bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\tvar pos = inner_bounds.position\n\tvar sz = inner_bounds.size\n\n\tvar diff = sz.x - _get_sum_of_child_sizes().x\n\tmatch content_alignment:\n\t\tHorizontalContentAlignment.LEFT:   pass\n\t\tHorizontalContentAlignment.CENTER: pos.x += int(diff/2.0)\n\t\tHorizontalContentAlignment.RIGHT:  pos.x += diff\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:\n\t\t\t_perform_component_layout( child, Rect2(pos,Vector2(child.size.x,sz.y)) )\n\t\t\tpos += Vector2( child.size.x, 0 )\n"
  },
  {
    "path": "addons/GameGUI/GGInitialWindowSize.gd",
    "content": "@tool\n\n## A control that sets the initial window size to be its own size if it is the base\n## node of the scene. Facilitates testing child scenes individually with a customized aspect\n## ratio similar to what their bounds will be when placed in the parent scene.\nclass_name GGInitialWindowSize\nextends GGComponent\n\n## The desired initial size of the window. Only takes effect when this node is the base node\n## of a scene at runtime.\n@export var initial_window_size := Vector2(1280,720):\n\tset(value):\n\t\tif initial_window_size == value: return\n\t\tinitial_window_size = value\n\n\t\tvar viewport = get_viewport()\n\t\tif not viewport: return  # engine is setting the initial value of this property\n\t\tif not Engine.is_editor_hint() or viewport != get_parent(): return\n\n\t\tif value != size:\n\t\t\tsize = value\n\t\t\trequest_layout()\n\nfunc _ready():\n\tif get_parent() == get_viewport() and not Engine.is_editor_hint():\n\t\tDisplayServer.window_set_size( initial_window_size )\n\n\t\tvar screen_size = Vector2(DisplayServer.screen_get_size())\n\t\tvar pos = Vector2i( (screen_size-initial_window_size)/2.0 )\n\t\tDisplayServer.window_set_position( pos )\n\n\t\tset_anchors_and_offsets_preset( LayoutPreset.PRESET_FULL_RECT )\n\telse:\n\t\tinitial_window_size = Vector2i(size)\n\nfunc _enter_tree():\n\tsuper()\n\tif get_parent() == get_viewport():\n\t\tresized.connect( _on_resized )\n\nfunc _exit_tree():\n\tsuper()\n\tif resized.is_connected( _on_resized ):\n\t\tresized.disconnect( _on_resized )\n\nfunc _on_resized():\n\tif Engine.is_editor_hint(): initial_window_size = size\n"
  },
  {
    "path": "addons/GameGUI/GGLabel.gd",
    "content": "@tool\n\n## A Label with expanded layout and scaling capabilities. Make this a child of an extended\n## GGComponent, not a standard Control or Container.\nclass_name GGLabel\nextends Label\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n## Check to lock in the current font size and reference node height as reference\n## values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = int(reference_node.size.y)\n\t\t\t\treference_font_size = get_theme_font_size( \"font_size\" )\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\treference_font_size = 0\n\t\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [Label] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n## Setting [member text_size_mode] to [b]SCALE[/b] will automatically set this property's value.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of the font. Setting [member text_size_mode] to [b]SCALE[/b] will\n## automatically set this property's value.\n@export var reference_font_size := 0 :\n\tset(value):\n\t\tif reference_font_size == value: return\n\t\treference_font_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use when [member text_size_mode] is [b]Parameter[/b].\n@export var text_size_parameter:String = \"\" :\n\tset(value):\n\t\tif text_size_parameter == value: return\n\t\ttext_size_parameter = value\n\t\tif has_parameter( text_size_parameter ):\n\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_size := 0\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tvar cur_size = get_theme_font_size( \"font_size\" )\n\t\t\tif cur_size != _current_font_size:\n\t\t\t\t_current_font_size = cur_size\n\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif _current_font_size != reference_font_size:\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\trequest_layout()\n\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\trequest_layout()\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\thorizontal_mode  = GGComponent.ScalingMode.EXPAND_TO_FILL\n\t\tvertical_mode = GGComponent.ScalingMode.FIXED\n\t\tif abs(layout_size.x) < 0.0001 and abs(layout_size.y) < 0.0001:\n\t\t\tlayout_size = Vector2( 0, get_line_count()*get_line_height() )\n\t\tif text == \"\": text = \"Label\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\t_current_font_size = get_theme_font_size( \"font_size\" )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\t_current_font_size = reference_font_size\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of the font to dynamically size it\n\t\t\t\tvar cur_size = reference_font_size * cur_scale\n\t\t\t\tif cur_size:\n\t\t\t\t\tadd_theme_font_size_override( \"font_size\", cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tif has_parameter( text_size_parameter ):\n\t\t\t\tadd_theme_font_size_override( \"font_size\", int(get_parameter(text_size_parameter)) )\n\n\tlayout_size = Vector2( 0, get_line_count()*get_line_height() )\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n\n"
  },
  {
    "path": "addons/GameGUI/GGLayoutConfig.gd",
    "content": "@tool\n\n## Extend this node and override [method _on_begin_layout] to set GameGUI parameters\n## prior to each layout via [method set_parameter]. Ensure the extended node\n## begins with the [code]@tool[/code] annotation.\nclass_name GGLayoutConfig\nextends Node2D\n\nfunc _enter_tree():\n\tvar top = get_top_level_component()\n\tif top:\n\t\ttop.begin_layout.connect( _dispatch_on_begin_layout )\n\nfunc _exit_tree():\n\tvar top = get_top_level_component()\n\tif top:\n\t\ttop._disconnect( top.begin_layout, _dispatch_on_begin_layout )\n\nfunc _dispatch_on_begin_layout():\n\tvar viewport = get_viewport()\n\tif not viewport: return\n\n\tvar display_size = viewport.get_visible_rect().size\n\tvar top = get_top_level_component()\n\tif Engine.is_editor_hint() and top: display_size = top.size\n\n\t_on_begin_layout( display_size )\n\n## Layout for the [GGComponent] subtree this node is attached to is about to begin.\n## Override this method and set parameters or other configuration needs.\nfunc _on_begin_layout( display_size:Vector2 ):\n\tpass\n\n## Returns the specified parameter's value if it exists in the [member parameters]\n## of the [GGComponent] subtree this node is attached to. If it doesn't exist, returns\n## [code]0[/code] or a specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant )->Variant:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.get_parameter( parameter_name, default_result )\n\telse:   return default_result\n\n## Returns the root of the [GGComponent] subtree this node is attached to.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.get_top_level_component()\n\telse:   return null\n\n## Returns [code]true[/code] if the specified parameter exists in the\n## [member parameters] of the [GGComponent] subtree this node is attached to.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: return cur.has_parameter( parameter_name )\n\telse:   return false\n\n## Sets the named parameter's value in the top-level root of the [GGComponent]\n## subtree this node is attached to.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar cur = get_parent()\n\twhile cur and not cur is GGComponent:\n\t\tcur = cur.get_parent()\n\tif cur: cur.set_parameter( parameter_name, value )\n"
  },
  {
    "path": "addons/GameGUI/GGLimitedSizeComponent.gd",
    "content": "@tool\n\n## A component that can limit its size to arbitrary minimum and/or maximum values.\nclass_name GGLimitedSizeComponent\nextends GGComponent\n\nenum LimitType\n{\n\tUNLIMITED,  ## No size limit.\n  FIXED,      ## A limited size in pixels.\n\tPARAMETER   ## A parameter defines the size limit in pixels.\n}\n\n@export_group(\"Minimum Size\")\n\n@export var min_width  := LimitType.UNLIMITED :\n\tset(value):\n\t\tif min_width == value: return\n\t\tmin_width = value\n\t\trequest_layout()\n\n@export var min_height := LimitType.UNLIMITED :\n\tset(value):\n\t\tif min_height == value: return\n\t\tmin_height = value\n\t\trequest_layout()\n\n@export var min_size := Vector2(0,0) :\n\tset(value):\n\t\tif min_size == value: return\n\t\tmin_size = value\n\t\trequest_layout()\n\n@export var min_width_parameter := \"\" :\n\tset(value):\n\t\tif min_width_parameter == value: return\n\t\tmin_width_parameter = value\n\t\trequest_layout()\n\n@export var min_height_parameter := \"\" :\n\tset(value):\n\t\tif min_height_parameter == value: return\n\t\tmin_height_parameter = value\n\t\trequest_layout()\n\n@export_group(\"Maximum Size\")\n\n@export var max_width  := LimitType.UNLIMITED :\n\tset(value):\n\t\tif max_width == value: return\n\t\tmax_width = value\n\t\trequest_layout()\n\n@export var max_height := LimitType.UNLIMITED :\n\tset(value):\n\t\tif max_height == value: return\n\t\tmax_height = value\n\t\trequest_layout()\n\n@export var max_size := Vector2(0,0) :\n\tset(value):\n\t\tif max_size == value: return\n\t\tmax_size = value\n\t\trequest_layout()\n\n@export var max_width_parameter := \"\" :\n\tset(value):\n\t\tif max_width_parameter == value: return\n\t\tmax_width_parameter = value\n\t\trequest_layout()\n\n@export var max_height_parameter := \"\" :\n\tset(value):\n\t\tif max_height_parameter == value: return\n\t\tmax_height_parameter = value\n\t\trequest_layout()\n\nfunc _effective_min_height()->int:\n\tmatch min_height:\n\t\tLimitType.FIXED:\n\t\t\treturn int(min_size.y)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( min_height_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_min_width()->int:\n\tmatch min_width:\n\t\tLimitType.FIXED:\n\t\t\treturn int(min_size.x)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( min_width_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_max_height()->int:\n\tmatch max_height:\n\t\tLimitType.FIXED:\n\t\t\treturn int(max_size.y)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( max_height_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _effective_max_width()->int:\n\tmatch max_width:\n\t\tLimitType.FIXED:\n\t\t\treturn int(max_size.x)\n\t\tLimitType.PARAMETER:\n\t\t\treturn get_parameter( max_width_parameter, 0 )\n\t\t_:\n\t\t\treturn 0\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\tif min_width != LimitType.UNLIMITED:\n\t\tavailable_size.x = max( available_size.x, _effective_min_width() )\n\tif min_height != LimitType.UNLIMITED:\n\t\tavailable_size.y = max( available_size.y, _effective_min_height() )\n\n\tif max_width != LimitType.UNLIMITED:\n\t\tavailable_size.x = min( available_size.x, _effective_max_width() )\n\tif max_height != LimitType.UNLIMITED:\n\t\tavailable_size.y = min( available_size.y, _effective_max_height() )\n\n\tsuper( available_size, limited )\n"
  },
  {
    "path": "addons/GameGUI/GGMarginLayout.gd",
    "content": "@tool\n\n## Lays out its children in a layered stack with pixel or proportional margins.\nclass_name GGMarginLayout\nextends GGComponent\n\nenum MarginType\n{\n\tPROPORTIONAL,  ## Specify margins between 0.0 and 1.0, similar to anchors.\n\tFIXED,         ## Margins have fixed pixel sizes.\n\tPARAMETER      ## Margins use parameters as their pixel sizes.\n}\n\n@export_group(\"Margins\")\n\n## Specifies the type of margin that will surround the child content area.\n@export var margin_type := MarginType.PROPORTIONAL :\n\tset(value):\n\t\tif margin_type == value: return\n\n\t\tif reference_node:\n\t\t\tvar ref_size = reference_node.size\n\t\t\tif value == MarginType.PROPORTIONAL:\n\t\t\t\tif margin_type == MarginType.FIXED and ref_size.x and ref_size.y:\n\t\t\t\t\tleft_margin /= ref_size.x\n\t\t\t\t\ttop_margin /= ref_size.y\n\t\t\t\t\tright_margin /= ref_size.x\n\t\t\t\t\tbottom_margin /= ref_size.y\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0.0\n\t\t\t\t\ttop_margin = 0.0\n\t\t\t\t\tright_margin = 0.0\n\t\t\t\t\tbottom_margin = 0.0\n\t\t\telif value == MarginType.FIXED:\n\t\t\t\tif margin_type == MarginType.PROPORTIONAL and ref_size.x and ref_size.y:\n\t\t\t\t\tleft_margin = int( left_margin * ref_size.x )\n\t\t\t\t\ttop_margin = int( top_margin * ref_size.y )\n\t\t\t\t\tright_margin = int( right_margin * ref_size.x )\n\t\t\t\t\tbottom_margin = int( bottom_margin * ref_size.y )\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0\n\t\t\t\t\ttop_margin = 0\n\t\t\t\t\tright_margin = 0\n\t\t\t\t\tbottom_margin = 0\n\t\telse:\n\t\t\tif value == MarginType.PROPORTIONAL:\n\t\t\t\tif margin_type == MarginType.FIXED and size.x and size.y:\n\t\t\t\t\tleft_margin /= size.x\n\t\t\t\t\ttop_margin /= size.y\n\t\t\t\t\tright_margin = 1.0 - (right_margin/size.x)\n\t\t\t\t\tbottom_margin = 1.0 - (bottom_margin/size.y)\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0.0\n\t\t\t\t\ttop_margin = 0.0\n\t\t\t\t\tright_margin = 1.0\n\t\t\t\t\tbottom_margin = 1.0\n\t\t\telif value == MarginType.FIXED:\n\t\t\t\tif margin_type == MarginType.PROPORTIONAL and size.x and size.y:\n\t\t\t\t\tleft_margin = int( left_margin * size.x )\n\t\t\t\t\ttop_margin = int( top_margin * size.y )\n\t\t\t\t\tright_margin = int( (1.0 - right_margin) * size.x )\n\t\t\t\t\tbottom_margin = int( (1.0 - bottom_margin) * size.y )\n\t\t\t\telse:\n\t\t\t\t\tleft_margin = 0\n\t\t\t\t\ttop_margin = 0\n\t\t\t\t\tright_margin = 0\n\t\t\t\t\tbottom_margin = 0\n\n\t\tmargin_type = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.25: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var left_margin:float = 0.0 :\n\tset(value):\n\t\tif left_margin == value: return\n\t\tleft_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.25: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var top_margin:float = 0.0 :\n\tset(value):\n\t\tif top_margin == value: return\n\t\ttop_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.75: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var right_margin:float = 1.0 :\n\tset(value):\n\t\tif right_margin == value: return\n\t\tright_margin = value\n\t\trequest_layout()\n\n## [b]Proportional[/b] Margin Type, no [member reference_node][br]  0.0: no margin[br]  0.75: 25% margin, etc.[br]\n## [b]Proportional[/b] Margin Type, [member reference_node] set[br]  0.0: no margin[br]  0.25: margin is 25% size of ref node, etc.[br]\n## [b]Fixed[/b] Margin Type[br]  0: no margin[br]  25: 25 pixel margin, etc.[br]\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var bottom_margin:float = 1.0 :\n\tset(value):\n\t\tif bottom_margin == value: return\n\t\tbottom_margin = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no left margin[br]  \"abc\": left margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var left_parameter:String = \"\" :\n\tset(value):\n\t\tif left_parameter == value: return\n\t\tleft_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no top margin[br]  \"abc\": top margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var top_parameter:String = \"\" :\n\tset(value):\n\t\tif top_parameter == value: return\n\t\ttop_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no right margin[br]  \"abc\": right margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var right_parameter:String = \"\" :\n\tset(value):\n\t\tif right_parameter == value: return\n\t\tright_parameter = value\n\t\trequest_layout()\n\n## [b]Parameter[/b] Margin Type[br]  \"\": no bottom margin[br]  \"abc\": bottom margin is [code]get_parameter(\"abc\")[/code] pixels, etc.\n@export var bottom_parameter:String = \"\" :\n\tset(value):\n\t\tif bottom_parameter == value: return\n\t\tbottom_parameter = value\n\t\trequest_layout()\n\nfunc _resolve_shrink_to_fit_height( available_size:Vector2 ):\n\tsuper( available_size )\n\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tsize.y += int( top_margin * reference_node.size.y )\n\t\t\t\tsize.y += int( bottom_margin * reference_node.size.y )\n\t\t\telse:\n\t\t\t\tsize.y += int( available_size.y * top_margin )\n\t\t\t\tsize.y += int( available_size.y * bottom_margin )\n\t\tMarginType.FIXED:\n\t\t\tsize.y += top_margin\n\t\t\tsize.y += bottom_margin\n\t\tMarginType.PARAMETER:\n\t\t\tsize.y += get_parameter(top_parameter,0)\n\t\t\tsize.y += get_parameter(bottom_parameter,0)\n\nfunc _resolve_shrink_to_fit_width( available_size:Vector2 ):\n\tsuper( available_size )\n\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tsize.x += int( left_margin * reference_node.size.x )\n\t\t\t\tsize.x += int( right_margin * reference_node.size.x )\n\t\t\telse:\n\t\t\t\tsize.x += int( available_size.x * left_margin )\n\t\t\t\tsize.x += int( available_size.x * right_margin )\n\t\tMarginType.FIXED:\n\t\t\tsize.x += left_margin\n\t\t\tsize.x += right_margin\n\t\tMarginType.PARAMETER:\n\t\t\tsize.x += get_parameter(left_parameter,0)\n\t\t\tsize.x += get_parameter(right_parameter,0)\n\nfunc _with_margins( rect:Rect2 )->Rect2:\n\tmatch margin_type:\n\t\tMarginType.PROPORTIONAL:\n\t\t\tif reference_node:\n\t\t\t\tvar left = int( left_margin * reference_node.size.x )\n\t\t\t\tvar right = int( right_margin * reference_node.size.x )\n\t\t\t\tvar top = int( top_margin * reference_node.size.y )\n\t\t\t\tvar bottom = int( bottom_margin * reference_node.size.y )\n\t\t\t\tvar x = rect.position.x + left\n\t\t\t\tvar y = rect.position.y + top\n\t\t\t\tvar x2 = rect.position.x + (rect.size.x - right)\n\t\t\t\tvar y2 = rect.position.y + (rect.size.y - bottom)\n\t\t\t\tvar w = x2 - x\n\t\t\t\tvar h = y2 - y\n\t\t\t\tif w < 0: w = 0\n\t\t\t\tif h < 0: h = 0\n\t\t\t\treturn Rect2( x, y, w, h )\n\t\t\telse:\n\t\t\t\tvar x = rect.position.x + floor( rect.size.x * left_margin )\n\t\t\t\tvar y = rect.position.y + floor( rect.size.y * top_margin )\n\t\t\t\tvar x2 = rect.position.x + floor( rect.size.x * right_margin )\n\t\t\t\tvar y2 = rect.position.y + floor( rect.size.y * bottom_margin )\n\t\t\t\tvar w = x2 - x\n\t\t\t\tvar h = y2 - y\n\t\t\t\tif w < 0: w = 0\n\t\t\t\tif h < 0: h = 0\n\t\t\t\treturn Rect2( x, y, w, h )\n\t\tMarginType.FIXED:\n\t\t\tvar x = rect.position.x + left_margin\n\t\t\tvar y = rect.position.y + top_margin\n\t\t\tvar x2 = rect.position.x + (rect.size.x - right_margin)\n\t\t\tvar y2 = rect.position.y + (rect.size.y - bottom_margin)\n\t\t\tvar w = x2 - x\n\t\t\tvar h = y2 - y\n\t\t\tif w < 0: w = 0\n\t\t\tif h < 0: h = 0\n\t\t\treturn Rect2( x, y, w, h )\n\t\tMarginType.PARAMETER:\n\t\t\tvar x = rect.position.x + get_parameter(left_parameter,0)\n\t\t\tvar y = rect.position.y + get_parameter(top_parameter,0)\n\t\t\tvar x2 = rect.position.x + (rect.size.x - get_parameter(right_parameter,0))\n\t\t\tvar y2 = rect.position.y + (rect.size.y - get_parameter(bottom_parameter,0))\n\t\t\tvar w = x2 - x\n\t\t\tvar h = y2 - y\n\t\t\tif w < 0: w = 0\n\t\t\tif h < 0: h = 0\n\t\t\treturn Rect2( x, y, w, h )\n\t\t_: return rect\n\n"
  },
  {
    "path": "addons/GameGUI/GGNinePatchRect.gd",
    "content": "@tool\n\nclass_name GGNinePatchRect\nextends GGComponent\n\n@export var texture:Texture2D :\n\tset(value):\n\t\ttexture = value\n\t\tif not texture: return\n\n\t\t_texture_region = Rect2( 0, 0, texture.get_width(), texture.get_height() )\n\n\t\t_update_piece_rects()\n\n@export var draw_center := true :\n\tset(value):\n\t\tdraw_center = value\n\t\tqueue_redraw()\n\n@export_group(\"Patch Margin\")\n\n@export var left   := 0:\n\tset(value):\n\t\tleft = clamp( value, 0, _texture_region.size.x )\n\t\t_update_piece_rects()\n\n@export var top    := 0:\n\tset(value):\n\t\ttop = clamp( value, 0, _texture_region.size.y )\n\t\t_update_piece_rects()\n\n@export var right  := 0:\n\tset(value):\n\t\tright = clamp( value, 0, _texture_region.size.x )\n\t\t_update_piece_rects()\n\n@export var bottom := 0:\n\tset(value):\n\t\tbottom = clamp( value, 0, _texture_region.size.y )\n\t\t_update_piece_rects()\n\n@export_group(\"Fill Mode\")\n\n@export var horizontal_fill := FillMode.STRETCH :\n\tset(value):\n\t\thorizontal_fill = value\n\t\tqueue_redraw()\n\n@export var vertical_fill   := FillMode.STRETCH :\n\tset(value):\n\t\tvertical_fill = value\n\t\tqueue_redraw()\n\nvar _texture_region:Rect2\nvar _piece_rects:Array[Rect2] = []\n\nfunc _draw():\n\tif size.x == 0 or size.y == 0 or not texture: return\n\n\tvar _left = left\n\tvar _right = right\n\tvar _top = top\n\tvar _bottom = bottom\n\n\tif left + right > size.x or top + bottom > size.y:\n\t\tvar scale_x = size.x / (_left + _right)\n\t\tvar scale_y = size.y / (_top + _bottom)\n\t\tvar scale = min( scale_x, scale_y )\n\n\t\t_left = floor( _left * scale )\n\t\t_right = ceil( _right * scale )\n\t\t_top = floor( _top * scale )\n\t\t_bottom = ceil( _bottom * scale )\n\n\tvar mid_w = max( size.x - (_left+_right), 0 )\n\tvar mid_h = max( size.y - (_top+_bottom), 0 )\n\n\tvar pos = position\n\tif _top > 0:\n\t\tif _left > 0:   draw_texture_rect_region( texture, Rect2(pos,Vector2(_left,_top)), _piece_rects[0], modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(mid_w,_top)), _piece_rects[1], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tif _right > 0:  draw_texture_rect_region( texture, Rect2(pos,Vector2(_right,_top)), _piece_rects[2], modulate )\n\n\tpos = Vector2( position.x, pos.y + _top )\n\tif mid_h > 0:\n\t\tfill_texture( texture, Rect2(pos,Vector2(_left,mid_h)), _piece_rects[3], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tif draw_center and mid_w > 0:  fill_texture( texture, Rect2(pos,Vector2(mid_w,mid_h)), _piece_rects[4], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(_right,mid_h)), _piece_rects[5], horizontal_fill, vertical_fill, modulate )\n\n\tpos = Vector2( position.x, pos.y + mid_h )\n\tif _bottom > 0:\n\t\tif _left > 0:   draw_texture_rect_region( texture, Rect2(pos,Vector2(_left,_bottom)), _piece_rects[6], modulate )\n\t\tpos += Vector2( _left, 0 )\n\t\tfill_texture( texture, Rect2(pos,Vector2(mid_w,_bottom)), _piece_rects[7], horizontal_fill, vertical_fill, modulate )\n\t\tpos += Vector2( mid_w, 0 )\n\t\tif _right > 0:  draw_texture_rect_region( texture, Rect2(pos,Vector2(_right,_bottom)), _piece_rects[8], modulate )\n\nfunc _update_piece_rects():\n\tvar x = _texture_region.position.x\n\tvar y = _texture_region.position.y\n\tvar w = _texture_region.size.x\n\tvar h = _texture_region.size.y\n\tvar mid_w = max( w - (left+right), 0 )\n\tvar mid_h = max( h - (top+bottom), 0 )\n\n\t_piece_rects = []\n\n\t_piece_rects.push_back( Rect2     (      x, y, left,  top) )  # TL\n\t_piece_rects.push_back( Rect2(      x+left, y, mid_w, top) )  # T\n\t_piece_rects.push_back( Rect2( x+(w-right), y, right, top) )  # TR\n\n\t_piece_rects.push_back( Rect2(           x, y+top, left,  mid_h) )  # L\n\t_piece_rects.push_back( Rect2(      x+left, y+top, mid_w, mid_h) )  # M\n\t_piece_rects.push_back( Rect2( x+(w-right), y+top, right, mid_h) )  # R\n\n\t_piece_rects.push_back( Rect2(           x, y+(h-bottom), left,  bottom) )  # BL\n\t_piece_rects.push_back( Rect2(      x+left, y+(h-bottom), mid_w, bottom) )  # B\n\t_piece_rects.push_back( Rect2( x+(w-right), y+(h-bottom), right, bottom) )  # BR\n\n\tqueue_redraw()\n"
  },
  {
    "path": "addons/GameGUI/GGOverlay.gd",
    "content": "@tool\n\n## Positions its children at arbitrary coordinates within its own bounds, similiar to a sprite.\n## Not intended for use with an actual Sprite2D; use GGTextureRect or other Control types as\n## children. Typically used with a single child node.\nclass_name GGOverlay\nextends GGComponent\n\nenum PositioningMode\n{\n\tPROPORTIONAL,  ## Specify child position as a fraction between 0.0 and 1.0.\n\tFIXED,         ## Child position is a fixed pixel offset.\n\tPARAMETER      ## Use a parameter as the child's relative pixel offset.\n}\n\nenum ScaleFactor\n{\n\tCONSTANT,      ## Scale using a fixed scale factor.\n\tPARAMETER      ## Scale using a subtree parameter.\n}\n\n@export_group(\"Child Position and Scale\")\n\n## The child positioning mode.\n@export var positioning_mode := PositioningMode.PROPORTIONAL :\n\tset(value):\n\t\tif positioning_mode == value: return\n\t\tmatch value:\n\t\t\tPositioningMode.PROPORTIONAL:\n\t\t\t\tif positioning_mode == PositioningMode.FIXED:\n\t\t\t\t\tchild_x /= size.x\n\t\t\t\t\tchild_y /= size.y\n\t\t\t\telse:\n\t\t\t\t\tchild_x = 0.5\n\t\t\t\t\tchild_y = 0.5\n\t\t\tPositioningMode.FIXED:\n\t\t\t\tif positioning_mode == PositioningMode.PROPORTIONAL:\n\t\t\t\t\tchild_x = int( child_x * size.x )\n\t\t\t\t\tchild_y = int( child_y * size.y )\n\t\t\t\telse:\n\t\t\t\t\tchild_x = int(size.x / 2.0)\n\t\t\t\t\tchild_y = int(size.y / 2.0)\n\t\tpositioning_mode = value\n\t\trequest_layout()\n\n## The child 'x' offset within this component. Use 0.0-1.0 for positioning mode [b]Proportional[/b] and integer values for [b]Fixed[/b].\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var child_x:float = 0.5 :\n\tset(value):\n\t\tif child_x == value: return\n\t\tchild_x = value\n\t\trequest_layout()\n\n## The child 'y' offset within this component. Use 0.0-1.0 for positioning mode [b]Proportional[/b] and integer values for [b]Fixed[/b].\n@export_range(0.0,1.0,0.0001,\"or_less\",\"or_greater\") var child_y:float = 0.5 :\n\tset(value):\n\t\tif child_y == value: return\n\t\tchild_y = value\n\t\trequest_layout()\n\n## The parameter name to use for the child 'x' offset.\n@export var child_x_parameter := \"\" :\n\tset(value):\n\t\tif child_x_parameter == value: return\n\t\tchild_x_parameter = value\n\t\trequest_layout()\n\n## The parameter name to use for the child 'y' offset.\n@export var child_y_parameter := \"\" :\n\tset(value):\n\t\tif child_y_parameter == value: return\n\t\tchild_y_parameter = value\n\t\trequest_layout()\n\n## The horizontal scale mode.\n@export var h_scale_factor := ScaleFactor.CONSTANT :\n\tset(value):\n\t\tif h_scale_factor == value: return\n\t\th_scale_factor = value\n\t\trequest_layout()\n\n## The vertical scale mode.\n@export var v_scale_factor := ScaleFactor.CONSTANT :\n\tset(value):\n\t\tif v_scale_factor == value: return\n\t\tv_scale_factor = value\n\t\trequest_layout()\n\n## The horizontal scale factor to use when [member h_scale_factor] is [b]Constant[/b].\n@export_range(0.0,1.0,0.0001,\"or_greater\") var h_scale_constant:float = 1.0 :\n\tset(value):\n\t\tif h_scale_constant == value: return\n\t\th_scale_constant = value\n\t\trequest_layout()\n\n## The vertical scale factor to use when [member v_scale_factor] is [b]Constant[/b].\n@export_range(0.0,1.0,0.0001,\"or_greater\") var v_scale_constant:float = 1.0 :\n\tset(value):\n\t\tif v_scale_constant == value: return\n\t\tv_scale_constant = value\n\t\trequest_layout()\n\n## The horizontal scale factor to use when [member h_scale_factor] is [b]Parameter[/b].\n@export var h_scale_parameter:String = \"\" :\n\tset(value):\n\t\tif h_scale_parameter == value: return\n\t\th_scale_parameter = value\n\t\trequest_layout()\n\n## The vertical scale factor to use when [member v_scale_factor] is [b]Parameter[/b].\n@export var v_scale_parameter:String = \"\" :\n\tset(value):\n\t\tif v_scale_parameter == value: return\n\t\tv_scale_parameter = value\n\t\trequest_layout()\n\nfunc _get_scale()->Vector2:\n\tvar sx := 0.0\n\tvar sy := 0.0\n\n\tmatch h_scale_factor:\n\t\tScaleFactor.CONSTANT:\n\t\t\tsx = h_scale_constant\n\t\tScaleFactor.PARAMETER:\n\t\t\tsx = get_parameter( h_scale_parameter )\n\n\tmatch v_scale_factor:\n\t\tScaleFactor.CONSTANT:\n\t\t\tsy = v_scale_constant\n\t\tScaleFactor.PARAMETER:\n\t\t\tsy = get_parameter( v_scale_parameter )\n\n\treturn Vector2(sx,sy)\n\nfunc _perform_child_layout( available_bounds:Rect2 ):\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child is Control or not child.visible: continue\n\n\t\tvar x_pos := 0\n\t\tvar y_pos := 0\n\t\tmatch positioning_mode:\n\t\t\tPositioningMode.PROPORTIONAL:\n\t\t\t\tx_pos = available_bounds.size.x * child_x\n\t\t\t\ty_pos = available_bounds.size.y * child_y\n\t\t\tPositioningMode.FIXED:\n\t\t\t\tx_pos = child_x\n\t\t\t\ty_pos = child_y\n\t\t\tPositioningMode.PARAMETER:\n\t\t\t\tx_pos = get_parameter( child_x_parameter, child_x )\n\t\t\t\ty_pos = get_parameter( child_y_parameter, child_y )\n\n\t\t# Adjust x_pos and y_pos for SIZE_SHRINK_X.\n\t\tif child.size_flags_horizontal & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\tx_pos -= int(child.size.x / 2.0)\n\t\telif child.size_flags_horizontal & SizeFlags.SIZE_SHRINK_END:\n\t\t\tx_pos -= int(child.size.x)\n\n\t\tif child.size_flags_vertical & (SizeFlags.SIZE_SHRINK_CENTER | SizeFlags.SIZE_FILL):\n\t\t\ty_pos -= int(child.size.y / 2.0)\n\t\telif child.size_flags_vertical & SizeFlags.SIZE_SHRINK_END:\n\t\t\ty_pos -= int(child.size.y)\n\n\t\t_perform_component_layout( child, Rect2(Vector2(x_pos,y_pos),child.size) )\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\tvar scale = _get_scale()\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child is Control or not child.visible: continue\n\n\t\t# Resolve once at full size to get the child's full size\n\t\t_resolve_child_size( child, available_size, limited )\n\n\t\t# Apply the scale factor to the child\n\t\t_resolve_child_size( child, floor( child.size * scale ), limited )\n\n"
  },
  {
    "path": "addons/GameGUI/GGParameterSetter.gd",
    "content": "@tool\n\n## A component that sizes normally and then sets subtree parameters to its own width and/or height.\nclass_name GGParameterSetter\nextends GGComponent\n\n@export_group(\"Parameter Names\")\n\n## Optional name of a parameter to save this node's width in.\n@export var width_store := \"\" :\n\tset(value):\n\t\tif width_store == value: return\n\t\twidth_store = value\n\t\tif value != \"\":\n\t\t\tif value != _cur_width_parameter:\n\t\t\t\tvar top = get_top_level_component()\n\t\t\t\tif top: top.parameters.erase( _cur_width_parameter )\n\t\t\t\t_cur_width_parameter = value\n\t\t\tset_parameter( width_store, size.x )\n\n## Optional name of a parameter to save this node's height in.\n@export var height_store := \"\" :\n\tset(value):\n\t\tif height_store == value: return\n\t\theight_store = value\n\t\tif value != \"\":\n\t\t\tif value != _cur_height_parameter:\n\t\t\t\tvar top = get_top_level_component()\n\t\t\t\tif top: top.parameters.erase( _cur_height_parameter )\n\t\t\t\t_cur_height_parameter = value\n\t\t\tset_parameter( height_store, size.y )\n\nvar _cur_width_parameter := \"\"\nvar _cur_height_parameter := \"\"\n\nfunc _resolve_size( available_size:Vector2, limited:bool=false ):\n\tsuper( available_size, limited )\n\tif width_store != \"\": set_parameter( width_store, size.x )\n\tif height_store != \"\": set_parameter( height_store, size.y )\n\n"
  },
  {
    "path": "addons/GameGUI/GGRichTextLabel.gd",
    "content": "@tool\n\nclass_name GGRichTextLabel\nextends RichTextLabel\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Text Size\")\n\n# Check to lock in the current font size and reference node height as reference\n# values that will be used to scale the font size.\n@export var text_size_mode:GGComponent.TextSizeMode:\n\tset(value):\n\t\tif text_size_mode == value: return\n\t\ttext_size_mode = value\n\t\tif not get_parent(): return  # resource loading is setting properties\n\n\t\tmatch value:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\treference_node_height = 0\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\treference_font_sizes[style_name] = 0\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\tif reference_node: reference_node_height = floor(reference_node.size.y)\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\treference_font_sizes[style_name] = get_theme_font_size( style_size_name )\n\n\t\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\t\treference_node_height = 0\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\treference_font_sizes[style_name] = 0\n\t\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\t\tif cur_size:\n\t\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n## A node that will be used as a height reference for scaling this node's text.\n@export var reference_node:Control :\n\tset(value):\n\t\tif reference_node == value: return\n\t\treference_node = value\n\t\tif reference_node and reference_node_height == 0:\n\t\t\treference_node_height = int(value.size.y)\n\t\t\trequest_layout()\n\n## The height of the [RichTextLabel] node that the [member reference_font_size] was designed for.\n## This is used to scale the font based on the current height of the reference node.\n@export var reference_node_height := 0 :\n\tset(value):\n\t\tif reference_node_height == value: return\n\t\treference_node_height = value\n\t\trequest_layout()\n\n## The original size of each font style.\n@export var reference_font_sizes:Dictionary = {\"normal\":0,\"bold\":0,\"italics\":0,\"bold_italics\":0,\"mono\":0}\n\n## The names of the parameters to use when [member text_size_mode] is [b]Parameter[/b].\n## The parameter for style \"normal\" will be the default for any other style that does not specify a\n## parameter.\n@export var text_size_parameters:Dictionary = {\"normal\":\"\",\"bold\":\"\",\"italics\":\"\",\"bold_italics\":\"\",\"mono\":\"\"} :\n\tset(value):\n\t\tif text_size_parameters == value: return\n\t\ttext_size_parameters = value\n\n\t\tif text_size_mode == GGComponent.TextSizeMode.PARAMETER:\n\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that default properties have been set\n## for this node. Uncheck to reset those defaults.\n@export var is_configured := false\n\n# Internal editor use to detect font size changes and request an updated layout.\nvar _current_node_height := 0\nvar _current_font_sizes:Dictionary = {\"normal\":0,\"bold\":0,\"italics\":0,\"bold_italics\":0,\"mono\":0}\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# GGRICHTEXTLABEL METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\t_check_for_modified_font_size()\n\nfunc _check_for_modified_font_size():\n\tif not Engine.is_editor_hint(): return\n\n\tvar any_modified = false\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\tvar cur_size = get_theme_font_size( style_size_name )\n\t\t\t\tif cur_size != _current_font_sizes[style_name]:\n\t\t\t\t\t_current_font_sizes[style_name] = cur_size\n\t\t\t\t\tany_modified = true\n\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\tif _current_font_sizes[style_name] != reference_font_sizes[style_name]:\n\t\t\t\t\t_current_font_sizes[style_name] = reference_font_sizes[style_name]\n\t\t\t\t\tany_modified = true\n\n\t\t\tif reference_node:\n\t\t\t\tvar h = int(reference_node.size.y)\n\t\t\t\tif _current_node_height != h:\n\t\t\t\t\t_current_node_height = h\n\t\t\t\t\tany_modified = true\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tpass\n\n\tif any_modified: request_layout()\n\nfunc _configure():\n\tif not is_configured and size.y > 0:\n\t\tis_configured = true\n\t\tbbcode_enabled = true\n\t\tscroll_active = false\n\t\thorizontal_mode  = GGComponent.ScalingMode.EXPAND_TO_FILL\n\t\tvertical_mode = GGComponent.ScalingMode.FIXED\n\t\tif abs(layout_size.x) < 0.0001 and abs(layout_size.y) < 0.0001:\n\t\t\tlayout_size = Vector2( get_content_width(), get_content_height() )\n\t\tif text == \"\": text = \"[center]GGRichTextLabel\"\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tif Engine.is_editor_hint():\n\t\tmatch text_size_mode:\n\t\t\tGGComponent.TextSizeMode.DEFAULT:\n\t\t\t\t# Save current font size theme override size to check for editor changes\n\t\t\t\tfor style_name in _current_font_sizes.keys():\n\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t_current_font_sizes[style_name] = get_theme_font_size( style_size_name )\n\n\t\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\t\t# Save current font reference size to check for editor changes\n\t\t\t\tif reference_node: _current_node_height = int( reference_node.size.y )\n\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\t_current_font_sizes[style_name] = reference_font_sizes[style_name]\n\n\tmatch text_size_mode:\n\t\tGGComponent.TextSizeMode.SCALE:\n\t\t\tif reference_node and reference_node_height:\n\t\t\t\tvar cur_scale = floor(reference_node.size.y) / reference_node_height\n\n\t\t\t\t# Override the size of each font\n\t\t\t\tfor style_name in reference_font_sizes.keys():\n\t\t\t\t\tvar cur_size = reference_font_sizes[style_name] * cur_scale\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n\t\tGGComponent.TextSizeMode.PARAMETER:\n\t\t\tfor style_name in text_size_parameters.keys():\n\t\t\t\tvar var_name = text_size_parameters[style_name]\n\t\t\t\tif not has_parameter(var_name): var_name = text_size_parameters[\"normal\"]\n\t\t\t\tif has_parameter(var_name):\n\t\t\t\t\tvar cur_size = int(get_parameter(var_name))\n\t\t\t\t\tif cur_size:\n\t\t\t\t\t\tvar style_size_name = style_name + \"_font_size\"\n\t\t\t\t\t\tadd_theme_font_size_override( style_size_name, cur_size )\n\n\tlayout_size = Vector2( get_content_width(), get_content_height() )\n\tsize = layout_size\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n"
  },
  {
    "path": "addons/GameGUI/GGTextureRect.gd",
    "content": "@tool\n\n## Extends TextureRect and adapts it to work painlessly with the GameGUI layout system.\nclass_name GGTextureRect\nextends TextureRect\n\n#-------------------------------------------------------------------------------\n# GAMEGUI PROPERTIES\n#-------------------------------------------------------------------------------\n\n@export_group(\"Component Layout\")\n\n## The horizontal scaling mode for this node.\n@export var horizontal_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif horizontal_mode == value: return\n\t\thorizontal_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif vertical_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: vertical_mode = value\n\t\t\tif layout_size.x  < 0.0001: layout_size.x = 1\n\t\t\tif layout_size.y  < 0.0001: layout_size.y = 1\n\t\telif vertical_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): vertical_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## The vertical scaling mode for this node.\n@export var vertical_mode := GGComponent.ScalingMode.EXPAND_TO_FILL:\n\tset(value):\n\t\tif vertical_mode == value: return\n\t\tvertical_mode = value\n\t\tif value in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif horizontal_mode in [GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.FIXED,GGComponent.ScalingMode.PARAMETER]: horizontal_mode = value\n\t\t\tif abs(layout_size.x)  < 0.0001: layout_size.x = 1\n\t\t\tif abs(layout_size.y)  < 0.0001: layout_size.y = 1\n\t\telif horizontal_mode in [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.ASPECT_FILL]:\n\t\t\tif not (value in [GGComponent.ScalingMode.EXPAND_TO_FILL,GGComponent.ScalingMode.SHRINK_TO_FIT,GGComponent.ScalingMode.PARAMETER]): horizontal_mode = value\n\t\tif value == GGComponent.ScalingMode.PROPORTIONAL:\n\t\t\tif layout_size.x < 0.0001 or layout_size.x > 1: layout_size.x = 1\n\t\t\tif layout_size.y < 0.0001 or layout_size.x > 1: layout_size.y = 1\n\t\trequest_layout()\n\n## Pixel values for scaling mode [b]Fixed[/b], fractional values for [b]Proportional[/b], and aspect ratio values for [b]Aspect Fit[/b] and [b]Aspect Fill[/b].\n@export var layout_size := Vector2(0,0):\n\tset(value):\n\t\tif layout_size == value: return\n\t\t# The initial Vector2(0,0) may come in as e.g. 0.00000000000208 for x and y\n\t\tif abs(value.x) < 0.00001: value.x = 0\n\t\tif abs(value.y) < 0.00001: value.y = 0\n\t\tlayout_size = value\n\t\trequest_layout()\n\n## An optional node to use as a size reference for [b]Proportional[/b] scaling\n## mode. The reference node must be in a subtree higher in the scene tree than\n## this node. Often the size reference is an invisible root-level square-aspect\n## component; this allows same-size horizontal and vertical proportional spacers.\n@export var reference_node:Control = null :\n\tset(value):\n\t\tif reference_node != value:\n\t\t\treference_node = value\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] horizontal scaling mode.\n@export var width_parameter := \"\" :\n\tset(value):\n\t\tif width_parameter == value: return\n\t\twidth_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## The name of the parameter to use for the [b]Parameter[/b] vertical_mode scaling mode.\n@export var height_parameter := \"\" :\n\tset(value):\n\t\tif height_parameter == value: return\n\t\theight_parameter = value\n\t\tif value != \"\" and has_parameter(value):\n\t\t\trequest_layout()\n\n## Automatically set to indicate that GameGUI-related properties have been set\n## for this node. Uncheck to automatically reconfigure those properties.\n@export var is_configured := false\n\n# GameGUI framework use\nvar is_width_resolved  := false  ## Internal GameGUI use.\nvar is_height_resolved := false  ## Internal GameGUI use.\n\n#-------------------------------------------------------------------------------\n# CONFIGURATION METHODS\n#-------------------------------------------------------------------------------\nfunc _ready():\n\t_configure()\n\nfunc _process(_delta):\n\t_configure()\n\nfunc _configure():\n\tif not is_configured and texture:\n\t\tis_configured = true\n\t\thorizontal_mode  = GGComponent.ScalingMode.ASPECT_FIT\n\t\tvertical_mode = GGComponent.ScalingMode.ASPECT_FIT\n\t\tlayout_size   = texture.get_size()  # TextureRect-specific\n\n\t\t# Let the GG framework and wrapper handle the sizing (TextureRect-specific)\n\t\texpand_mode   = TextureRect.ExpandMode.EXPAND_IGNORE_SIZE\n\n#-------------------------------------------------------------------------------\n# GAMEGUI API METHODS\n#-------------------------------------------------------------------------------\n\n## Returns the specified parameter's value if it exists in a [GGComponent]\n## parent or ancestor. If it doesn't exist, returns [code]0[/code] or a\n## specified default result.\nfunc get_parameter( parameter_name:String, default_result:Variant=0 )->Variant:\n\tvar top = get_top_level_component()\n\tif top and top.parameters.has(parameter_name):\n\t\treturn top.parameters[parameter_name]\n\telse:\n\t\treturn default_result\n\n## Returns the root of this [GGComponent] subtree.\nfunc get_top_level_component()->GGComponent:\n\tvar cur = self\n\twhile cur and (not cur is GGComponent or not cur._is_top_level):\n\t\tcur = cur.get_parent()\n\treturn cur\n\n## Returns [code]true[/code] if the specified parameter exists in a\n## [GGComponent] parent or ancestor.\nfunc has_parameter( parameter_name:String )->bool:\n\tvar top = get_top_level_component()\n\tif top:\n\t\treturn top.parameters.has(parameter_name)\n\telse:\n\t\treturn false\n\n## Sets the named parameter's value in the top-level [GGComponent] root of this subtree.\nfunc set_parameter( parameter_name:String, value:Variant ):\n\tvar top = get_top_level_component()\n\tif top: top.parameters[parameter_name] = value\n\n# Called when this component is about to compute its size. Any size computations\n# relative to reference nodes higher in the tree should be performed here.\nfunc _on_resolve_size( available_size:Vector2 ):\n\tpass\n\n# Called at the beginning of GGLayout. Adjust 'horizontal_mode',\n# 'vertical_mode', and/or 'layout_size'. Other nodes may not have their sizes set yet,\n# so defer relative size computations to _on_resolve_size(available_size:Vector2).\nfunc _on_update_size():\n\tpass\n\n## Layout is performed automatically in most cases, but request_layout() can be\n## called for edge cases.\nfunc request_layout():\n\tvar top = get_top_level_component()\n\tif top: top.request_layout()\n\n"
  },
  {
    "path": "addons/GameGUI/GGVBox.gd",
    "content": "@tool\n\n## A GameGUI layout that arranges its child elements in a vertical column.\nclass_name GGVBox\nextends GGComponent\n\nenum VerticalContentAlignment\n{\n\tTOP,     ## Top-align the content.\n\tCENTER,  ## Center the content.\n\tBOTTOM   ## Bottom-align the content.\n}\n\n## Specify the vertical alignment of the content as a whole.\n@export var content_alignment := VerticalContentAlignment.CENTER :\n\tset(value):\n\t\tcontent_alignment = value\n\t\trequest_layout()\n\nvar _min_heights:Array[int] = []\nvar _max_heights:Array[int] = []\n\nfunc _resolve_child_sizes( available_size:Vector2, limited:bool=false ):\n\t# Resolve for and collect min and max sizes\n\t_max_heights.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, available_size, true )\n\t\t\t_max_heights.push_back( int(child.size.y) )\n\t\telse:\n\t\t\t_max_heights.push_back( 0 )\n\n\t_min_heights.clear()\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif child is Control and child.visible:\n\t\t\t_resolve_child_size( child, Vector2(available_size.x,0), true )\n\t\t\t_min_heights.push_back( int(child.size.y) )\n\t\telse:\n\t\t\t_min_heights.push_back( 0 )\n\n\tvar expand_count := 0\n\tvar total_stretch_ratio := 0.0\n\tvar fixed_height := 0\n\tvar min_height := 0\n\n\t# Leaving other children at their minimum height, set aspect-fit, proportional,\n\t# and shrink-to-fit height nodes to their maximum size.\n\tvar modes = [GGComponent.ScalingMode.ASPECT_FIT,GGComponent.ScalingMode.PROPORTIONAL,GGComponent.ScalingMode.SHRINK_TO_FIT]\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\n\t\tvar has_mode = child is GGComponent or child.has_method(\"request_layout\")\n\t\tif has_mode and child.vertical_mode in modes:\n\t\t\t_resolve_child_size( child, available_size, limited )\n\t\t\tvar h = int(child.size.y)\n\t\t\tmin_height += h\n\t\t\tfixed_height += h\n\t\t\t_min_heights[i] = h\n\t\t\t_max_heights[i] = h\n\n\t\telse:\n\t\t\tvar h = _min_heights[i]\n\t\t\tmin_height += h\n\n\t\t\tif _min_heights[i] == _max_heights[i]:\n\t\t\t\tfixed_height += h\n\t\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\t\t\telse:\n\t\t\t\texpand_count += 1\n\t\t\t\ttotal_stretch_ratio += child.size_flags_stretch_ratio\n\n\tif expand_count == 0 or total_stretch_ratio == 0.0 or min_height >= available_size.y: return\n\n\tvar excess_height = int(available_size.y - fixed_height)\n\tvar remaining_height = excess_height\n\n\t# Find children with a min height larger than their portion. Let them keep their min height and adjust remaining.\n\tvar remaining_total_stretch_ratio = total_stretch_ratio\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif h < _min_heights[i]:\n\t\t\th = _min_heights[i]\n\t\t\tremaining_height -= h\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_heights[i] = _max_heights[i]  # skip this node in the next pass\n\t\t\t_resolve_child_size( child, child.size, limited )  # final resolve\n\n\texcess_height = remaining_height\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# Distribute remaining height next to children with a max height smaller than their portion.\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\tif h > _max_heights[i]:\n\t\t\th = _max_heights[i]\n\t\t\t_resolve_child_size( child, Vector2(available_size.x,h), limited )\n\t\t\tremaining_height -= h\n\t\t\texpand_count -= 1\n\t\t\tremaining_total_stretch_ratio -= child.size_flags_stretch_ratio\n\t\t\t_min_heights[i] = _max_heights[i]  # skip this node in the next pass\n\n\texcess_height = remaining_height\n\ttotal_stretch_ratio = remaining_total_stretch_ratio\n\tif expand_count == 0 or abs(total_stretch_ratio) < 0.0001: return\n\n\t# If this GGVBox is shrink-to-fit height then we're done; don't add remaining space to\n\t# the children.\n\tif vertical_mode == GGComponent.ScalingMode.SHRINK_TO_FIT:\n\t\treturn\n\n\t# Distribute remaining height\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not child.visible or not child is Control: continue\n\t\tif _min_heights[i] == _max_heights[i]: continue\n\n\t\tvar h := 0\n\t\tif expand_count == 1: h = remaining_height\n\t\telse:                 h = int( excess_height * child.size_flags_stretch_ratio / total_stretch_ratio )\n\n\t\t_resolve_child_size( child, Vector2(available_size.x,h), limited )\n\t\tremaining_height -= h\n\t\texpand_count -= 1\n\nfunc _resolve_shrink_to_fit_height( _available_size:Vector2 ):\n\tsize.y = _get_sum_of_child_sizes().y\n\nfunc _resolve_shrink_to_fit_width( _available_size:Vector2 ):\n\tsize.x = _get_largest_child_size().x\n\nfunc _perform_layout( available_bounds:Rect2 ):\n\t_place_component( self, available_bounds )\n\n\tvar inner_bounds = _with_margins( Rect2(Vector2(0,0),size) )\n\tvar pos = inner_bounds.position\n\tvar sz = inner_bounds.size\n\n\tvar diff = sz.y - _get_sum_of_child_sizes().y\n\tmatch content_alignment:\n\t\tVerticalContentAlignment.TOP:    pass\n\t\tVerticalContentAlignment.CENTER: pos.y += int(diff/2.0)\n\t\tVerticalContentAlignment.BOTTOM: pos.y += diff\n\n\tfor i in range(get_child_count()):\n\t\tvar child = get_child(i)\n\t\tif not (child is Control) or not child.visible: continue\n\t\tif child is Control:\n\t\t\t_perform_component_layout( child, Rect2(pos,Vector2(sz.x,child.size.y)) )\n\t\t\tpos += Vector2( 0, child.size.y )\n\n"
  },
  {
    "path": "addons/GameGUI/plugin.cfg",
    "content": "[plugin]\n\nname=\"GameGUI\"\ndescription=\"A collection of dynamic layout nodes that provide a full-featured alternative to Container nodes.\"\nauthor=\"Brom Bresenham\"\nversion=\"1.5\"\nscript=\"plugin.gd\"\n"
  },
  {
    "path": "addons/GameGUI/plugin.gd",
    "content": "@tool\nextends EditorPlugin\n\nfunc _enter_tree():\n\t# Initialization of the plugin goes here.\n\tadd_custom_type( \"GGButton\",               \"Button\",        preload(\"GGButton.gd\"), preload(\"Icons/GGButton.svg\") )\n\tadd_custom_type( \"GGComponent\",            \"Container\",     preload(\"GGComponent.gd\"), preload(\"Icons/GGComponent.svg\") )\n\tadd_custom_type( \"GGFiller\",               \"GGComponent\",   preload(\"GGFiller.gd\"), preload(\"Icons/GGFiller.svg\") )\n\tadd_custom_type( \"GGHBox\",                 \"GGComponent\",   preload(\"GGHBox.gd\"), preload(\"Icons/GGHBox.svg\") )\n\tadd_custom_type( \"GGInitialWindowSize\",    \"GGComponent\",   preload(\"GGInitialWindowSize.gd\"), preload(\"Icons/GGInitialWindowSize.svg\") )\n\tadd_custom_type( \"GGLabel\",                \"Label\",         preload(\"GGLabel.gd\"), preload(\"Icons/GGLabel.svg\") )\n\tadd_custom_type( \"GGLayoutConfig\",         \"Node2D\",        preload(\"GGLayoutConfig.gd\"), preload(\"Icons/GGLayoutConfig.svg\") )\n\tadd_custom_type( \"GGLimitedSizeComponent\", \"GGComponent\",   preload(\"GGLimitedSizeComponent.gd\"), preload(\"Icons/GGLimitedSizeComponent.svg\") )\n\tadd_custom_type( \"GGMarginLayout\",         \"GGComponent\",   preload(\"GGMarginLayout.gd\"), preload(\"Icons/GGMarginLayout.svg\") )\n\tadd_custom_type( \"GGNinePatchRect\",        \"GGComponent\",   preload(\"GGNinePatchRect.gd\"), preload(\"Icons/GGNinePatchRect.svg\") )\n\tadd_custom_type( \"GGParameterSetter\",      \"GGComponent\",   preload(\"GGParameterSetter.gd\"), preload(\"Icons/GGParameterSetter.svg\") )\n\tadd_custom_type( \"GGOverlay\",              \"GGComponent\",   preload(\"GGOverlay.gd\"), preload(\"Icons/GGOverlay.svg\") )\n\tadd_custom_type( \"GGRichTextLabel\",        \"RichTextLabel\", preload(\"GGRichTextLabel.gd\"), preload(\"Icons/GGRichTextLabel.svg\") )\n\tadd_custom_type( \"GGTextureRect\",          \"TextureRect\",   preload(\"GGTextureRect.gd\"), preload(\"Icons/GGTextureRect.svg\") )\n\tadd_custom_type( \"GGVBox\",                 \"GGComponent\",   preload(\"GGVBox.gd\"), preload(\"Icons/GGVBox.svg\") )\n\nfunc _exit_tree():\n\t# Clean-up of the plugin goes here.\n\tremove_custom_type( \"GGButton\" )\n\tremove_custom_type( \"GGComponent\" )\n\tremove_custom_type( \"GGFiller\" )\n\tremove_custom_type( \"GGHBox\" )\n\tremove_custom_type( \"GGInitialWindowSize\" )\n\tremove_custom_type( \"GGLabel\" )\n\tremove_custom_type( \"GGLayoutConfig\" )\n\tremove_custom_type( \"GGLimitedSizeComponent\" )\n\tremove_custom_type( \"GGMarginLayout\" )\n\tremove_custom_type( \"GGNinePatchRect\" )\n\tremove_custom_type( \"GGParameterSetter\" )\n\tremove_custom_type( \"GGOverlay\" )\n\tremove_custom_type( \"GGRichTextLabel\" )\n\tremove_custom_type( \"GGTextureRect\" )\n\tremove_custom_type( \"GGVBox\" )\n"
  }
]