[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [LudiKha] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Khaya Ludidi - CupBearer Interactive\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/LudiKha/Graphene/master/docs/images/graphene-logo-full.png\" />\n</p>\n\n&nbsp;\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ![last-commit](https://img.shields.io/github/last-commit/LudiKha/Graphene) ![open-issues](https://img.shields.io/github/issues/LudiKha/Graphene) [![](https://img.shields.io/twitter/follow/LudiKha.svg?label=Follow&style=social)](https://twitter.com/intent/follow?screen_name=LudiKha)\n\nGraphene is a lightweight and modular framework for building runtime user interfaces with Unity's [UI Toolkit][0f273cb2].\n\n[0f273cb2]: https://docs.unity3d.com/2020.1/Documentation/Manual/UIElements.html \"UI Toolkit\"\n\n&nbsp;\n\n# Intro\n\nGraphene **superconducts** your creativity for efficiently building modern interactive UI for games. It takes care of the heavy lifting by providing a framework inspired by Web standards, right into Unity.\n\nIt's **lightweight** and modular - you get to pick and choose which parts you need for your projects.\n\n- **Declarative Hierarchy**: Graphene makes it painless to design interactive UI. Use the familiar GameObjects hierarchy to design simple `Views` as sections of the screen or nested states.\n- **Reduce Boilerplate**: Focus on your custom logic and design of building UI instead of repeating low-level tasks for each unique screen you are building. Graphene comes with a number of `Controls` that greatly enhance the speed of creating interactive UI, whilst reducing the need of custom view controllers in C# by exposing vital functionality in Uxml.\n- **Attribute-Based**: Instruct your UI to both draw _and_ bind templates using any data-container with a `[Bind]` attribute. Primitives, objects, collections, one-way, two-way binding, specific control selection: the parts you'll be most frequently developing with in C# are  exposed via attributes\n- **State-Based Routing**: Use the GameObject hierarchy dynamically construct your router's states. Its functionality mimics url-based addresses: `index/settings/video`.\n- **Template Composition**: Reuse your static assets by writing atomic templates, and dynamically compose them in runtime.\n\nIt comes with a **[component-kit][e593c071]** library, **[sample project][05f1d8f6]** several VisualElement extensions and an **[online demo][84ed822d]** to get you started.\n\n  [e593c071]: https://github.com/LudiKha/Graphene-Components \"Graphene Components\"\n  [05f1d8f6]: https://github.com/LudiKha/Graphene-Sample-Project \"Graphene-Sample-Project\"\n  [84ed822d]: https://ludikha.github.io/Graphene-WebGL-Demo/ \"Graphene-WebGL-Demo\"\n\n\n## Online Demo\n### [Check out the WebGL demo ][f45eaa31]\n\n  [f45eaa31]: https://ludikha.github.io/Graphene-WebGL-Demo/ \"Graphene WebGL demo\"\n\n\n## Installation\n### Using Unity Package Manager (For Unity 2018.3 or later)\n\n<details>\n  <summary>You can install the package via UPM by adding the line below to your project's `Packages/manifest.json` file.</summary>\n\n&nbsp;\n\n  >You can find this file by opening your project's *Packages* folder in a file browser, it is not displayed in the editor.\n\n</details>\n\n```\n{\n  \"dependencies\": {\n    \"com.graphene.core\": \"https://github.com/LudiKha/Graphene.git?path=/src\",\n    \"com.graphene.components\": \"https://github.com/LudiKha/Graphene-Components.git?path=/src\",\n    ...\n  },\n}\n```\n\n>Do note that although both components and demo are optional packages, it is recommended you use them to kickstart your own Graphene-based development environment.\n\n#### Staying updated\nUpdating the package can be done via `Window/Graphene/Check for updates`. Unity currently does not support updating Git packages via the Package Manager automatically.\n\n\n### Using UPM Git Extension\nThe best way to install Graphene and stay up to date with the latest versions, is to use [UPM Git Extension][49fed258].\n   1. Follow the [installation instructions][2ddc031d]\n   2. In the Package Manager, click the ![Git button](docs/images/installation/git.png) button, and add `https://github.com/LudiKha/Graphene.git` under subdirectory `src`, with the latest version.\n   3. Voilá! As an added bonus you are now able to update the package via the package manager.\n\n  [49fed258]: https://github.com/mob-sakai/UpmGitExtension \"Upm Git Extension\"\n  [2ddc031d]: https://github.com/mob-sakai/UpmGitExtension#installation \"UP Git Extension Installation Instructions\"\n\n\n&nbsp;\n\n---\n\n&nbsp;\n\n# Quickstart\n\nFor a quick start, Graphene comes with a component library and [sample project][52a86b14] - it is **highly** recommended to start your new project using the demo scene and resources provided within this project.\n\n  [52a86b14]: https://github.com/LudiKha/Graphene-Sample-Project \"Graphene Sample Project\"\n\n#### 1: Constructing the hierarchy\n- Construct the high-level UI hierarchy, where each unique state is represented by a GameObject\n- Add a [`Plate`][0fb2479e] component to each GameObject in the tree, with a `Graphene` component at the root.\n- For each Plate in the tree, assign a static asset to its UIDocument. Root states will typically need a Layout-style [`template`](https://github.com/LudiKha/Graphene#template) for their children to be fitted in.\n\nPress play - Graphene will now dynamically construct the VisualTree based on your GameObject hierarchy. You've completed the required part of Graphene - however, we are still getting started.\n\nLet's draw and bind some data onto our UI.\n\n#### 2: Rendering & binding a model\n- Add a [`Theme`][a617f693] to the root Graphene component.\n- Add one or more [`Renderer`][b39c255d] components to each `Plate` that has dynamic (instantiated) content\n- Assign a [`Model`][19f2ae47] to the Renderer - this is a data container that serves as the model for the data-binding.\n- In the type(s) assigned as model, select the members you wish to expose for binding by adding [`BindAttribute`][b3387189]s. Add an additional `DrawAttribute` to dynamically instantiate controls in runtime using [`Templates`][fe269940].\n\n  [a617f693]: https://github.com/LudiKha/Graphene#theming \"Theming\"\n\nPress play - Graphene will draw templates, and bind them to the model. If a static asset contained a control with a binding-path (e.g. a label with [`Model.Title`][04efb446]), this will be bound to the model too.\n\n  [04efb446]: https://github.com/LudiKha/Graphene#scopes \"Scopes\"\n\nThe hierarchy is created and detail fields are rendered dynamically - now all that remains is to switch states.\n\n#### 3: Routing\n- Add a [`StringRouter`][1015cb88] to the root GameObject.\n- Add a `StringStateHandle` to each `Plate` GameObject that needs to be activated or deactivated based on states. Children are automatically deactivated with their parents. Give the StateHandle `StateId` unique names (e.g. \"start\", \"load\", \"exit\").\n- For each `Plate` that has one or more children using states, select which child state is enabled by default by ticking `enableWithParent`\n- In order to navigate, we can instantiate controls with a `RouteAttribute` or statically type them in UXML. Make sure to set the route member to a value that corresponds the available states.\n\n\n> Note: It is also possible to encapsulate a button within a Route element.\n> ```html\n><gr:Route route=\"/settings\">\n>  <ui:Button text=\"Clicking me will change state\"/>\n></gr:Route>\n>\n><gr:Route binding-path=\"~Model/ExitState\">\n>```\n\n\nPress play - The router constructs its state tree from the `Plate` hierarchy. When clicking a route element (or child button), the router will attempt to change states and the view will display this state change accordingly.\n\n  [0fb2479e]: https://github.com/LudiKha/Graphene#plates \"Plates\"\n  [b39c255d]: https://github.com/LudiKha/Graphene#rendering \"Renderer\"\n  [19f2ae47]: https://github.com/LudiKha/Graphene#model \"Model\"\n  [1015cb88]: https://github.com/LudiKha/Graphene#routing \"Router\"\n  [b3387189]: https://github.com/LudiKha/Graphene#binding \"Binding\"  \n  [fe269940]: https://github.com/LudiKha/Graphene#templating \"Templating\"\n\nCongrats! You're now done with the Quickstart and ready to tackle your first project using Graphene.\n\n&nbsp;\n\n---\n\n&nbsp;\n\n# Core Concepts\n\nGraphene decouples fine-grained authoring from high-level logic, and in doing so aims to leverage UI Toolkit's innovations to the fullest.\n\n## Plates\nA `Plate` represents a view controller in the VisualTree, and is used by Graphene to display the hierarchy, its states and views.\n\nA Graphene hierarchy consists of nested components called `Plates`, with a `Graphene` component at the root. `Plate`s are the core of Graphene, are analogous for a general-purpose UI controller that can be switched on or off. Other, optional MonoBehaviour components may hook into a plate, and have their functionality based on whether a plate is active or not.\n\nThe following components and logic depends on plates:\n- View\n\nThese can be authored in the familiar GameObject hierarchy. Graphene then constructs the VisualElement tree at runtime into a nested view.\n\n## Views\nA `View` represents a section of the screen, and is defined as a VisualElement within the `Plate`'s UXML asset. It contains an 'id', which serves as a unique identifier within the plate. Examples of views include a sidebar, a content area, a header or footer.\n\nViews can be used to compose static content (i.e. child Plates composing into parent Views), or to render dynamic content into (i.e. a renderer drawing a list of items into a View).\n\nBy default, Graphene components assume these two types of views will be present on a `Plate`:\n- **Content View**: A view that is used to render dynamic content into, using a `Renderer` component.\n- **Children View**: A view that is used to compose child plates into, based on the GameObject hierarchy.\n\n## Rendering & Binding\nRendering refers to the process of drawing and binding a model to the view. This is done using a `Renderer` component. It requires a `Model` to be assigned, which serves as the data container for the binding.\n\nIt consists of two passes:\n1. A static binding pass, where elements that are part of the static UXML asset assigned to the `Plate`'s `VisualTreeAsset` are bound to the model.\n2. A dynamic rendering and binding pass, where templates are rendered and bound from the model.\n\nA manager class called `Binder` takes care of the binding process and lifecycle management, and supports one-way and two-way binding modes.\n\n## Model\nA `Model` is a data container that serves as the model for the data-binding. Any class or struct can be used as a model, as long as it has been decorated with the `[Bind]` attribute on the members you wish to expose for binding.\n\n## Templating\nTemplating refers to the process of dynamically instantiating static UXML assets at runtime, based on a predetermined type of `Control` (i.e. a button, a toggle, a slider etc). UXML templates are mapped to a `ControlType` enum in a `TemplateAsset`, and DrawAttributes are used to instruct the `Renderer` which templates to instantiate.\n\n### Template\nA `Template` is a semantic name for static asset that represents a chunk of UXML of varying granularity and complexity, which are used as building blocks to build and render the application. Moreover, templates can be declared directly in UXML based, and will be rendered at runtime based on the `Renderer` template configuration. Templates are wrapped in a `TemplateAsset` ScriptableObject, where additional variants can be created without needing to create and maintain copies of the base template.\n\n#### Why use templates\n\nWhen creating a simple element, such as a button, it may quickly end up consisting of several carefully configured elements and bindings:\n\n```html\n<ui:Button text=\"Button\" name=\"ButtonFramed\" focusable=\"true\" tooltip=\"This is a button\" binding-path=\"Value\" class=\"button button-framed light\">\n  <ui:Style src=\"ButtonFramed.uss\" />\n  <gr:Route binding-path=\"Route\" class=\"button__route\" />\n  <gr:Tooltip binding-path=\"Description\" class=\"button__tooltip\" />\n  <ui:VisualElement class=\"button__background\" />\n  <ui:Label text=\"Label\" binding-path=\"Label\" />\n  <ui:VisualElement class=\"button__hover\" />\n  <ui:VisualElement class=\"button__frame\" />\n</ui:Button>\n```\n\nMaintaining multiple versions and instances of the same chunk of UXML throughout multiple files can be both error prone and time intensive. Graphene allows you to reuse the same template, and instantiate them at runtime when required.\n\n#### Creating a template\n\nCreate a `TemplateAsset` via the following menu command:\n>  `Assets/Create/Graphene/Templating/TemplateAsset`\n\nAssign a static UXML template, and give it an appropriate name.\n\n#### Instantiating a template via CSharp\nTemplates can be instantiated directly in C# via a reference of the `TemplateAsset`.\n```csharp\nvar clone = myTemplateAsset.Instantiate();\n...\n\n```\n#### Instantiating a template via UXML\n Graphene allows you to statically type control types using the following syntax:\n\n```html\n<gr:Button text=\"Button\" name=\"ButtonFramed\" focusable=\"true\" tooltip=\"This is a button\" binding-path=\"Value\" class=\"button button-framed light\" />\n```\nAt runtime, the button will be rendered to the full syntax of the first snippet, using the `Template` configuration of the `Renderer` component that initiates the binding.\n\n## Binding\n\n### Binding Modes\nGraphene supports 3 modes of binding a model to the view. These can either be specified in the BindAttribute on the model, or using the Binder API directly.\n- **OneTime**: Instructs the `Binder` to only \"print\" the model once onto the view. No continuous binding will be attempted. Useful for immutable data, such as titles, labels or button callbacks.\n>Note: Prefixing the binding-path with the `::` syntax instructs the binder to use a one-time binding.\n>```html\n><ui:Label binding-path=\"::Title\" />\n>```\n\n- **OneWay**: Creates a continuous binding from the model to the view. Updates are polled continuously but only for bindings that are currently visible (based on `Plate` state). Polling rate can be configured in the `Graphene` component.\n- **TwoWay**: Creates a two-directional binding (from model to view, and view to model) for controls that support two-way binding. View to model binding is based on `INotifyPropertyChange` callbacks.\n\n### Scopes\n- Scope drilldown `.` `Renderer.Model.ChildObject.Title`\n- Scope transferral `~` `~Renderer.Model` > `ChildObject.Title`\n\n### Binding Passes\n1. Static\n2. Dynamic\n\n### Static Binding\nStatic binding refers to binding elements that are part of the static UXML asset assigned to the `Plate`'s `VisualTreeAsset`. It allows you to bind elements that are always present in the view, such as titles, labels, buttons or other controls that may have a binding-path assigned. \n\nCommon use-cases include binding a title label to `Model.Title`, or binding a button's `Route` to a member in the model.\n\n### Dynamic Binding\nDynamic binding refers to binding elements that have been instantiated dynamically via the `Renderer`'s `Template` configuration, or viewmodel's `Instantiate` method. \n\nCommon use-cases include rendering a list of items from a collection, or rendering detail fields from a complex object, such as a Settings ScriptableObject.\n\n## Routing\nGraphene supports url-like state routing via the `Router` component. This allows you to construct a state hierarchy using the GameObject hierarchy, and navigate between states using `Route` elements.\n\nThe routing system is inspired by web standards, and mimics url-based addresses: `index/settings/video`. \n\n## Localization\nTBD\n\n## Step-by-step process\n1. Static view composition\n2. Static binding pass\n3. Render templates from dynamic model\n4. Dynamic binding pass\n5. Runtime one-way/two-way binding\n\n\n&nbsp;\n\n---\n\n&nbsp;\n\n# Inspector Extensions\nGraphene supports two third party inspector extensions out of the box: [Sirenix Odin Inspector][5855fee6] (paid) and [NaughtyAttributes][06602d8c] (free). Graphene relies on these to expose optional enhanced functionality to the inspector windows.\n\n[5855fee6]: https://odininspector.com/ \"Sirenix Odin Inspector\"\n[06602d8c]: https://github.com/dbrizov/NaughtyAttributes \"Naughty Attributes\"\n\n## Installation\n### Odin Inspector\nThis asset is automatically setup when it is included in the project.\n > Define symbol: `ODIN_INSPECTOR`\n\n### Naughty Attributes\nThis package requires you to follow the following steps:\n1. Add the package to the project `Packages/manifest.json` file:\n\n   >`\"com.dbrizov.naughtyattributes\": \"https://github.com/dbrizov/NaughtyAttributes.git#upm\"`\n2. In Project Settings/Player/Scripting define symbols, add the following entry: `NAUGHTY_ATTRIBUTES;`\n\nAfter recompilation, your project will now have enhanced inspector functionality for Graphene components.\n"
  },
  {
    "path": "docs/About.md",
    "content": "layout: page\ntitle: \"About Graphene\"\npermalink: /about/\n\n![Graphene](images/graphene-logo-full.png)\n\n# Graphene\n## UI Framework for unity\n\n## WebGL Demo\n"
  },
  {
    "path": "docs/_config.yml",
    "content": "theme: jekyll-theme-cayman"
  },
  {
    "path": "docs/howto.md",
    "content": ""
  },
  {
    "path": "src/Core/Binding/Binder.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n  using global::Graphene.ViewModel;\n  using Kinstrife.Core.ReflectionHelpers;\n  using System.Collections;\n  using System.Linq;\n\n  public interface IRoute\n  {\n  }\n\n  /// <summary>\n  /// Utility class to bind bindable data objects to VisualElements, using the <see cref=\"BindAttribute\"/> to match members to <see cref=\"BindableElement\"/>s.\n  /// </summary>\n  /// <remarks>Common use cases include buttons, labels, toggles, sliders etc. Supports recursive binding and drilling down into sub-scopes defined in UXML.</remarks>\n  public static class Binder\n  {\n\tinternal static VisualElement InternalInstantiate(VisualTreeAsset template, Plate plate)\n\t{\n\t  var clone = template.Instantiate();\n\t  return clone.Children().First();\n\t}\n\n\t/// <summary>\n\t/// Binds the tree recursively\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"element\"></param>\n\t/// <param name=\"context\"></param>\n\tpublic static VisualElement Instantiate(in object context, VisualTreeAsset template, Plate plate)\n\t{\n\t  var clone = InternalInstantiate(template, plate);\n\t  var t = context.GetType();\n\t  if (RenderUtils.IsPrimitiveContext(t))\n\t  {\n\t  }\n\t  // Bind class with its own context\n\t  else\n\t  {\n\t\tif (context is ICustomAddClasses customAddClasses)\n\t\t  clone.AddMultipleToClassList(customAddClasses.ClassesToAdd);\n\t\tif (context is ICustomName customName && !string.IsNullOrWhiteSpace(customName.CustomName))\n\t\t  clone.name = customName.CustomName;\n\n\t\t// Get members\n\t\tList<ValueWithAttribute<BindAttribute>> members = new List<ValueWithAttribute<BindAttribute>>();\n\t\tTypeInfoCache.GetMemberValuesWithAttribute<BindAttribute>(context, members);\n\t\tBinder.BindRecursive(clone, context, members, plate, false);\n\t  }\n\t  return clone;\n\t}\n\n\t/// <summary>\n\t/// Binds the tree recursively\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"element\"></param>\n\t/// <param name=\"fieldValue\"></param>\n\tpublic static VisualElement InstantiatePrimitive(in object context, ref ValueWithAttribute<BindAttribute> bindableMember, VisualTreeAsset template, Plate plate)\n\t{\n\t  var clone = InternalInstantiate(template, plate);\n\n\t  if (bindableMember.Attribute == null)\n\t  {\n\t\tDebug.LogError($\"Drawing {template.name} for primitive on {context} without Bind Attribute\", template);\n\t\treturn clone;\n\t  }\n\n\t  // Get members\n\t  List<ValueWithAttribute<BindAttribute>> members = new List<ValueWithAttribute<BindAttribute>>();\n\t  members.Add(bindableMember);\n\n\t  // Bind without scope drilldown\n\t  Binder.BindRecursive(clone, context, members, plate, false);\n\n\t  return clone;\n\t}\n\n\t/// <summary>\n\t/// Binds the tree recursively\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"element\"></param>\n\t/// <param name=\"context\"></param>\n\tpublic static void BindRecursive(VisualElement element, object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate, bool notFullyDrilledDown)\n\t{\n\t  if (members == null)\n\t  {\n\t\t// Get members\n\t\tmembers = new List<ValueWithAttribute<BindAttribute>>();\n\t\tTypeInfoCache.GetMemberValuesWithAttribute<BindAttribute>(context, members);\n\t  }\n\n\t  // Is bindable with binding-path in uxml\n\t  if (element is BindableElement el && !string.IsNullOrWhiteSpace(el.bindingPath))\n\t  {\n\t\t// Should drill down to a child's scope (based on binding-path '.', and scope ovveride '~')\n\t\tbool branched = notFullyDrilledDown && TryBranch(el, context, plate);\n\t\tif (branched) // Started branch via drilled down scope branch\n\t\t  return;\n\n\t\tBindElementValues(el, ref context, members, plate);\n\n\t\t// Context potentially has routing binding (TODO remove interface check)\n\t\tif (context is IRoute && plate.Router)\n\t\t  plate.Router.BindRouteToContext(el, context);\n\t  }\n\t  // Image (not bindable)\n\t  else if (element is Image image)\n\t  {\n\t\tBindImage(image, ref context, members, plate);\n\t  }\n\t  // Rout el special case\n\t  else if (element is Route route)\n\t  {\n\t\tBindRoute(route, ref context, plate);\n\t  }\n\n\t  BindChildren(element, context, members, plate, notFullyDrilledDown);\n\t}\n\n\tstatic void BindChildren(VisualElement element, object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate, bool scopeDrillDown)\n\t{\n\t  //element.BindValues(data);\n\t  if (element.childCount == 0)\n\t  {\n\t\treturn;\n\t  }\n\n\t  // Loop through children and bind data to them\n\t  foreach (var child in element.Children())\n\t  {\n\t\tBindRecursive(child, context, members, plate, scopeDrillDown);\n\t  }\n\t}\n\n\t/// <summary>\n\t/// Binds values of a particular VisualElement to an IBindable\n\t/// </summary>\n\t/// <typeparam name=\"T\"></typeparam>\n\t/// <param name=\"el\"></param>\n\t/// <param name=\"data\"></param>\n\tprivate static void BindElementValues<V>(V el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate) where V : BindableElement\n\t{\n\t  if (el.binding != null/* || el.userData != null*/)\n\t  {\n\t\tDebug.LogError($\"Binding twice! {el}\");\n\t\treturn;\n\t  }\n\t  else\n\t  {\n\t\tel.userData = context;\n\t\tplate.Graphene.BroadcastBindCallback(el, context, plate);\n\t  }\n\n\t  // Pass in list of properties for possible custom logic spanning multiple properties\n\t  if (el is Label)\n\t\tBindLabel(el as Label, ref context, members, plate);\n\t  else if (el is Button)\n\t\tBindButton(el as Button, ref context, members, plate);\n\t  else if (el is If)\n\t\tBindIf(el as If, ref context, members, plate);\n\t  else if (el is Image image)\n\t\tBindImage(image, ref context, members, plate);\n\t  else if (el is CycleField)\n\t\tBindCycleField(el as CycleField, ref context, members, plate);\n\t  else if (el is DropdownField)\n\t\tBindDropdownField(el as DropdownField, ref context, members, plate);\n\t  else if (el is ListView)\n\t\tBindListView(el as ListView, ref context, members, plate);\n\t  else if (el is SelectField)\n\t\tBindSelectField(el as SelectField, ref context, members, plate);\n\t  else if (el is Toggle)\n\t\tBindBaseField<bool>(el as Toggle, ref context, members, plate);\n\t  else if (el is Slider)\n\t\tBindSlider(el as Slider, ref context, members, plate);\n\t  else if (el is SliderInt sliderInt)\n\t\tBindSlider(sliderInt, ref context, members, plate);\n\t  else if (el is MinMaxSlider minMax)\n\t\tBindMinMaxSlider(minMax, ref context, members, plate);\n\t  else if (el is AbstractProgressBar progress)\n\t\tBindProgress(progress, ref context, members, plate);\n\t  else if (el is Foldout foldout)\n\t\tBindFoldout(foldout, ref context, members, plate);\n\t  else if (el is ButtonGroup buttonGroup)\n\t\tBindButtonGroup(buttonGroup, ref context, members, plate);\n\t  else if (el is TextField)\n\t\tBindTextField(el as TextField, ref context, members, plate);\n\t  else if (el is TextElement)\n\t\tBindTextElement(el as TextElement, ref context, members, plate);\n\t}\n\n\tprivate static void BindTextElement(TextElement el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var item in members)\n\t  {\n\t\tif (BindingPathOrTypeMatch<string>(el, in item))\n\t\t{\n\t\t  BindText(el, ref context, in item, plate);\n\t\t  break;\n\t\t}\n\t  }\n\t}\n\tprivate static void BindLabel(Label el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var item in members)\n\t  {\n\t\tif (BindingPathOrTypeMatch<string>(el, in item))\n\t\t{\n\t\t  BindText(el, ref context, in item, plate);\n\t\t  break;\n\t\t}\n\t  }\n\t}\n\n\n\tprivate static void BindButton(Button el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var item in members)\n\t  {\n\t\tif (BindingPathAndTypeMatch<BindableObject>(el, in item))\n\t\t{\n\t\t  //Debug.Log($\"Binding BindableObject click {el.bindingPath} {context}\", context as UnityEngine.Object);\n\n\t\t  //var data = item.Value as BindableObject;\n\t\t  BindRecursive(el, item.Value, null, plate, false);\n\t\t  var bindable = (BindableObject)item.Value;\n\t\t  el.tooltip = bindable.Tooltip;\n\t\t  BindCallbacks(el, context);\n\t\t  //Debug.Log(bindable);\n\t\t  break;\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<ActionButton>(el, in item))\n\t\t{\n\t\t  //Debug.Log($\"Binding ActionButton click {el.bindingPath} {context}\", context as UnityEngine.Object);\n\t\t  BindRecursive(el, item.Value, null, plate, false);\n\t\t  el.tooltip = ((ActionButton)item.Value).Tooltip;\n\t\t  break;\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<string>(el, in item))\n\t\t{\n\t\t  //Debug.Log($\"Binding string click {el.bindingPath} {item.Value} {context}\", context as UnityEngine.Object);\n\t\t  BindText(el, ref context, in item, plate);\n\t\t  //break;\n\t\t}\n\t\telse if (BindingPathOrTypeMatch<Action>(el, in item))\n\t\t{\n\t\t  //Debug.Log($\"Binding click {el.GetType().Name} {el.bindingPath} ({item.MemberInfo.Name} {item.Type.Name} in {context})\", context as UnityEngine.Object);\n\t\t  BindClick(el, (Action)item.Value, context, plate);\n\t\t  break;\n\t\t}\n\t\telse if (BindingPathOrTypeMatch<UnityEngine.Events.UnityEvent>(el, in item))\n\t\t{\n\t\t  //Debug.Log($\"Binding UnityEvent click {el.GetType().Name} {el.bindingPath} ({item.MemberInfo.Name} {item.Type.Name} in {context})\", context as UnityEngine.Object);\n\t\t  BindClick(el, (UnityEngine.Events.UnityEvent)item.Value, context, plate);\n\t\t  break;\n\t\t}\n\t  }\n\t}\n\n\tinternal static void BindRoute(Route el, ref object context, Plate plate)\n\t{\n\t  // Check if parent is a button -> propagate click\n\t  if (el.parent is Button button)\n\t  {\n\t\tBindClick(button, el.clicked, context, plate);\n\t  }\n\t  else\n\t  {\n\t\tforeach (var item in el.Children())\n\t\t{\n\t\t  if (item is Button btn)\n\t\t  {\n\t\t\tBindClick(btn, el.clicked, context, plate);\n\t\t  }\n\t\t  else if (item is ButtonGroup btnGroup)\n\t\t  {\n\t\t\tbtnGroup.clicked += (int index, string route) => { el.route = route; el.clicked?.Invoke(); }; // A bit hacky perhaps\n\t\t  }\n\t\t}\n\t  }\n\n\t  el.SetRouter(plate.Router);\n\n\t  // Let the (generic) router handle the way it binds routes\n\t  plate.Router.BindRoute(el, context);\n\t}\n\n\tprivate static void BindSlider(Slider el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  el.Q(\"unity-dragger\").pickingMode = PickingMode.Ignore;\n\n\t  // Slider specifics\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathOrTypeMatch<float>(el, in item))\n\t\t{\n\t\t  if (item.Attribute is BindFloatAttribute floatAttribute)\n\t\t  {\n\t\t\tel.value = floatAttribute.startingValue;\n\t\t\tel.lowValue = floatAttribute.lowValue;\n\t\t\tel.highValue = floatAttribute.highValue;\n\t\t\tel.showInputField = floatAttribute.showInputField;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<float>(\"Min\", item))\n\t\t  el.lowValue = (float)item.Value;\n\t\telse if (BindingPathAndTypeMatch<float>(\"Max\", item))\n\t\t  el.highValue = (float)item.Value;\n\t  }\n\n\t  // Bind base field value & callback\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindSlider(SliderInt el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  el.Q(\"unity-dragger\").pickingMode = PickingMode.Ignore;\n\n\t  // Slider specifics\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathOrTypeMatch<int>(el, item))\n\t\t{\n\t\t  if (item.Attribute is BindIntAttribute att)\n\t\t  {\n\t\t\tel.value = att.startingValue;\n\t\t\tel.lowValue = att.lowValue;\n\t\t\tel.highValue = att.highValue;\n\t\t\tel.showInputField = att.showInputField;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<int>(\"Min\", item))\n\t\t  el.lowValue = (int)item.Value;\n\t\telse if (BindingPathAndTypeMatch<int>(\"Max\", item))\n\t\t  el.highValue = (int)item.Value;\n\t  }\n\n\t  //el./*showMixedValue*/ = true;\n\t  //el.showInputField = true;\n\n\t  // Bind base field value & callback\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindMinMaxSlider(MinMaxSlider el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  el.Q(\"unity-dragger\").pickingMode = PickingMode.Ignore;\n\t  // Slider specifics\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathOrTypeMatch<Vector2>(el, in item))\n\t\t{\n\t\t  if (item.Attribute is BindRangeAttribute floatAttribute)\n\t\t  {\n\t\t\tel.value = floatAttribute.startingValue;\n\t\t\tel.lowLimit = floatAttribute.lowLimit;\n\t\t\tel.highLimit = floatAttribute.highLimit;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<float>(\"Min\", item))\n\t\t  el.lowLimit = (float)item.Value;\n\t\telse if (BindingPathAndTypeMatch<float>(\"Max\", item))\n\t\t  el.highLimit = (float)item.Value;\n\t  }\n\n\t  // Bind base field value & callback\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindProgress(AbstractProgressBar el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Progress Bar specifics\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathOrTypeMatch<float>(el, item))\n\t\t{\n\t\t  if (item.Attribute is BindFloatAttribute att)\n\t\t  {\n\t\t\tel.value = att.startingValue;\n\t\t\tel.lowValue = att.lowValue;\n\t\t\tel.highValue = att.highValue;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<float>(\"Min\", item))\n\t\t  el.lowValue = (float)item.Value;\n\t\telse if (BindingPathAndTypeMatch<float>(\"Max\", item))\n\t\t  el.highValue = (float)item.Value;\n\t  }\n\t  var results = BindNotifyValueChange<AbstractProgressBar, float>(el, ref context, members, plate);\n\t  el.value = results.value;\n\t  el.title = results.label;\n\t}\n\n\tprivate static void BindTextField(TextField el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathOrTypeMatch<string>(el, in item))\n\t\t{\n\t\t  if (item.Attribute is BindStringAttribute stringAttribute)\n\t\t  {\n\t\t\tel.value = stringAttribute.startingValue;\n\t\t\tel.isPasswordField = stringAttribute.password;\n\t\t\tel.isReadOnly = stringAttribute.readOnly;\n\t\t\tel.multiline = stringAttribute.multiLine;\n\n\t\t\tif (stringAttribute.maxLength >= 0)\n\t\t\t  el.maxLength = stringAttribute.maxLength;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t  }\n\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static (TValueType value, string label) BindNotifyValueChange<TElementType, TValueType>(TElementType el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate) where TElementType : BindableElement, INotifyValueChanged<TValueType>\n\t{\n\t  bool labelFromAttribute = false;\n\t  string label = null;\n\t  foreach (var item in members)\n\t  {\n\t\t// Primary (value)\n\t\tif (BindingPathOrTypeMatch<TValueType>(el, in item))\n\t\t{\n\t\t  if (item.Value is TValueType value)\n\t\t  {\n\t\t\tel.SetValueWithoutNotify(value);\n\t\t\tplate.BindingsManager.TryCreate<TValueType>(el, in context, in item, plate);\n\t\t  }\n\t\t  else if (item.Value is BindableBaseField<TValueType> baseField)\n\t\t  {\n\t\t\tel.SetValueWithoutNotify(baseField.value);\n\t\t\tif (!string.IsNullOrWhiteSpace(baseField.Label))\n\t\t\t  label = baseField.Label;\n\t\t\tplate.BindingsManager.TryCreate<TValueType>(el, in item.Value, in item, plate);\n\t\t  }\n\n\t\t  // Set label from attribute\n\t\t  if (item.Attribute is BindBaseFieldAttribute att)\n\t\t  {\n\t\t\tif (!string.IsNullOrWhiteSpace(att.label))\n\t\t\t{\n\t\t\t  //el.text = att.label;\n\t\t\t  labelFromAttribute = true;\n\t\t\t  label = att.label;\n\t\t\t}\n\t\t  }\n\t\t}\n\t\t// Set register callback event\n\t\telse if (item.Attribute is BindValueChangeCallbackAttribute callbackAttribute)\n\t\t{\n\t\t  var target = item.Value as EventCallback<ChangeEvent<TValueType>>;\n#if UNITY_ASSERTIONS\n\t\t  UnityEngine.Assertions.Assert.IsNotNull(target, \"Bindable item Invalid callback - ValueType mismatch.\");\n#endif\n\t\t  el.RegisterValueChangedCallback(target);\n\t\t}\n\t\t// Set label from field, if not from attribute\n\t\telse if (!labelFromAttribute && item.Attribute.Path == \"Label\" && item.Value is string labelText)\n\t\t  label = labelText;\n\t\t//BindText(el.labelElement, ref context, labelText, in item, plate);\n\t\telse if (item.Attribute is BindTooltip)\n\t\t  el.tooltip = ObjectToString(item.Value, item.Type);\n\t  }\n\n\t  // Returning value & label tuple because each element implements text differently. (E.g. Foldout vs. Basefield)\n\t  return (el.value, label);\n\t}\n\n\tprivate static void BindBaseField<TValueType>(BaseField<TValueType> el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  var results = BindNotifyValueChange<BaseField<TValueType>, TValueType>(el, ref context, members, plate);\n\t  el.label = results.label;\n\t}\n\n\tprivate static void BindDropdownField(DropdownField el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathMatch(item.Attribute.Path, SelectField.itemsPath))\n\t\t{\n\t\t  el.choices = item.Value as List<string>;\n\t\t  break;\n\t\t}\n\t\telse if (item.Type.IsEnum) // Primitive -> Can only do two way\n\t\t{\n\t\t  el.choices = Enum.GetNames(item.Type).ToList();\n\t\t  el.SetValueWithoutNotify(ObjectToString(item.Value, item.Type) ?? el.value);\n\n\t\t  var t = item.Type;\n\t\t  var memberName = item.MemberInfo.Name;\n\t\t  var accessor = TypeInfoCache.GetExtendedTypeInfo(context.GetType()).Accessor;\n\t\t  var ctx = context;\n\n\t\t  // Filthy hax\n\t\t  el.RegisterValueChangedCallback<string>((evt) =>\n\t\t  {\n\t\t\tvar val = Enum.Parse(t, evt.newValue);\n\t\t\taccessor[ctx, memberName] = val;\n\t\t  });\n\t\t}\n\t  }\n\n\t  // First bind base field (string)\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindSelectField(SelectField el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathMatch(item.Attribute.Path, SelectField.itemsPath))\n\t\t{\n\t\t  el.items = item.Value as List<string>;\n\t\t  break;\n\t\t}\n\t  }\n\n\t  // First bind base field (int)\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindListView(ListView el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var bindMember in members)\n\t  {\n\t\t// Primary\n\t\tif (BindingPathMatch(bindMember.Attribute.Path, el.bindingPath))\n\t\t{\n\t\t  ControlType controlType = ControlType.ListItem;\n\t\t  if (context is IListViewBindable listViewBindable)\n\t\t  {\n\t\t\tcontrolType = listViewBindable.ItemControlType;\n\t\t  }\n\n\t\t  IList list = bindMember.Value as IList;\n\n\t\t  RenderUtils.templatesDefault.TryGetTemplateAsset(controlType, out VisualTreeAsset template);\n\t\t  InternalBindListView(el, in context, list, template, plate);\n\n\t\t  plate.BindingsManager.TryCreate<IList>(el, in context, in bindMember, plate);\n\t\t  break;\n\t\t}\n\t  }\n\n\t  // Fallback\n\t  if (context is IListViewBindable listViewBindable2)\n\t  {\n\t\tRenderUtils.templatesDefault.TryGetTemplateAsset(listViewBindable2.ItemControlType, out VisualTreeAsset template);\n\t\tInternalBindListView(el, in context, listViewBindable2.ItemsSource, template, plate);\n\t  }\n\t}\n\n\tinternal static void BindListView(ListView el, in object context, Plate plate, VisualTreeAsset templateAsset, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  IList list = member.Value as IList;\n\t  InternalBindListView(el, in context, list, templateAsset, plate);\n\t  plate.BindingsManager.TryCreate<IList>(el, in context, in member, plate);\n\t}\n\n\n\tinternal static void InternalBindListView(ListView el, in object context, IList itemsSource, VisualTreeAsset templateAsset, Plate plate)\n\t{\n\t  bool elementsPickable = true;\n\n\t  if (context is IListViewBindable bindable)\n\t  {\n\t\tbindable.Apply(el);\n\t\tBindCallbacks(el, context);\n\t\telementsPickable = bindable.ElementsPickable;\n\t  }\n\n\t  Func<VisualElement> makeItem = () =>\n\t  {\n\t\tvar e = InternalInstantiate(templateAsset, plate);\n\t\te.pickingMode = elementsPickable ? PickingMode.Position : PickingMode.Ignore;\n\t\treturn e;\n\t  };\n\t  Action<VisualElement, int> bindItem = (e, i) =>\n\t  {\n\t\tvar src = itemsSource[i];\n\t\te.userData = src;\n\t\tBinder.BindRecursive(e, src, null, plate, false);\n\t\tif (!(e is BindableElement))\n\t\t{\n\t\t  BindCallbacks(e, src);\n\t\t}\n\t  };\n\n\t  el.makeItem = makeItem;\n\t  el.bindItem = bindItem;\n\t  el.itemsSource = itemsSource;\n\t}\n\n\tprivate static void BindCycleField(CycleField el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathMatch(item.Attribute.Path, CycleField.itemsPath))\n\t\t{\n\t\t  el.items = item.Value as List<string>;\n\t\t  break;\n\t\t}\n\t  }\n\n\t  // First bind base field (int)\n\t  BindBaseField(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindIf(If el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathMatch(el, in item))\n\t\t{\n\t\t  plate.BindingsManager.TryCreate<object>(el, in context, in item, plate); // This shows/hides the bindable\n\t\t  el.OnModelChange(item.Value);\n\t\t  return;\n\t\t}\n\t  }\n\t}\n\n\tprivate static void BindImage(Image el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathOrTypeMatch<Texture>(\"Image\", item))\n\t\t{\n\t\t  el.image = item.Value as Texture;\n\t\t  if (item.Attribute.hideIfEmpty)\n\t\t\tel.SetShowHide(el.image != null);\n\t\t  return;\n\t\t}\n\t\telse if (BindingPathOrTypeMatch<Sprite>(\"Image\", item))\n\t\t{\n\t\t  el.sprite = item.Value as Sprite;\n\t\t  if (item.Attribute.hideIfEmpty)\n\t\t\tel.SetShowHide(el.sprite != null);\n\t\t  return;\n\t\t}\n\t  }\n\t}\n\n\tprivate static void BindFoldout(Foldout el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  foreach (var member in members)\n\t  {\n\t\tif (BindingPathMatch(el, in member) && member.Value is BindableBaseField baseField)\n\t\t{\n\t\t  BindRecursive(el, baseField, null, plate, false);\n\t\t  return;\n\t\t}\n\t  }\n\n\t  if (context is BindableBaseField<bool> baseFieldContext)\n\t  {\n\t\tel.SetValueWithoutNotify(baseFieldContext.value);\n\t\tif (!string.IsNullOrWhiteSpace(baseFieldContext.Label))\n\t\t  el.text = baseFieldContext.Label;\n\t  }\n\n\t  foreach (var member in members)\n\t  {\n\t\tif (member.Value is bool)\n\t\t  plate.BindingsManager.TryCreate<bool>(el, in context, in member, plate);\n\t\telse if (member.Value is string)\n\t\t  plate.BindingsManager.TryCreate<string>(el, in context, in member, plate);\n\t  }\n\n\t  //var results = BindNotifyValueChange<Foldout, bool>(el, ref context, members, plate);\n\t}\n\n\tprivate static void BindButtonGroup(ButtonGroup el, ref object context, List<ValueWithAttribute<BindAttribute>> members, Plate plate)\n\t{\n\t  // Then bind the items\n\t  foreach (var item in members)\n\t  {\n\t\t// Model items\n\t\tif (BindingPathAndTypeMatch<ICollection<string>>(el, item))\n\t\t{\n\t\t  el.items = item.Value as List<string>;\n\t\t  break;\n\t\t}\n\t\telse if (BindingPathAndTypeMatch<ICollection<ActionButton>>(el, item))\n\t\t{\n\t\t  el.items.Clear();\n\t\t  var items = item.Value as List<ActionButton>;\n\t\t  if (items == null || items.Count == 0)\n\t\t\tcontinue;\n\n\t\t  el.ClearItems();\n\t\t  foreach (var action in items)\n\t\t\tel.AddItem(action.Label, action.Tooltip);\n\n\t\t  el.SourceData = items;\n\n\t\t  //el.ClearCallback();\n\t\t  //el.clicked += (int i, string name) => items[i].OnClick?.Invoke();\n\t\t  break;\n\t\t}\n\t  }\n\t}\n\n\tprivate static void BindText(TextElement el, ref object context, in ValueWithAttribute<BindAttribute> member, Plate plate)\n\t{\n\t  // Add translation here\n\t  el.text = ObjectToString(in member.Value, member.Type);\n\n\t  plate.BindingsManager.TryCreate(el, ref context, in member, plate);\n\t}\n\n\tprivate static void BindClick(Button el, System.Action action, in object context, Plate plate)\n\t{\n\t  el.clicked += action;\n\t  BindCallbacks(el, context);\n\t}\n\n\tprivate static void BindClick(Button el, UnityEngine.Events.UnityEvent unityEvent, in object context, Plate plate)\n\t{\n\t  el.clicked += delegate { unityEvent?.Invoke(); };\n\t  BindCallbacks(el, context);\n\t}\n\n\t[System.Obsolete(\"Use IBindableToVisualElement and apply in bindable object\")]\n\tstatic void BindCallbacks(VisualElement el, in object context)\n\t{\n\t  if (context is IHasTooltip tooltip)\n\t  {\n\t\tel.tooltip = tooltip.Tooltip;\n\t\tif (context is IBindableToVisualElement bindable)\n\t\t{\n\t\t  el.SetEnabled(bindable.isEnabled);\n\t\t  el.SetActive(bindable.isActive2);\n\t\t  el.SetShowHide(bindable.isShown);\n\t\t  bindable.SetBinding(el);\n\n\n\t\t  bindable.onSetEnabled += el.SetEnabled;\n\t\t  bindable.onShowHide += el.SetShowHide;\n\t\t  bindable.onSetActive += el.SetActive;\n\t\t  //Debug.Log($\"On bind element {el} to context <b>{context}</b> \");\n\t\t}\n\t  }\n\t}\n\n\tpublic static string[] stringSplitOptions = new string[] { \".\", \"~\", \"::\", \"_\" };\n\n\tpublic const char nestedScopeChar = '.';\n\tpublic const char relativeScopeChar = '_';\n\tpublic const string oneTimeBindingChar = \"::\";\n\n\tprivate static bool TryBranch(BindableElement el, object data, Plate owner)\n\t{\n\t  var scopes = el.bindingPath.Split(nestedScopeChar);\n\t  if (scopes.Length == 1)\n\t\treturn false;\n\n\t  // Create sub scope '~'\n\t  bool createSubScope = false;\n\t  string bindingPath = el.bindingPath;\n\t  if (el.bindingPath.IndexOf(relativeScopeChar) == 0)\n\t  {\n\t\tcreateSubScope = true;\n\t\tbindingPath = bindingPath.Remove(0, 1);\n\t  }\n\n\t  return DrillDownToChildScopeRecursive(el, data, owner, bindingPath, createSubScope);\n\t}\n\n\n\tprivate static bool DrillDownToChildScopeRecursive(BindableElement el, object data, Plate owner, string currentScope, bool createSubScope)\n\t{\n\t  if (data == null)\n\t  {\n\t\tDebug.LogError($\"Data was null for scope {currentScope} {owner}\", owner);\n\t\treturn false;\n\t  }\n\n\t  //Debug.Log($\"Drilling down to child scope {currentScope} {data} ({el})\", data as UnityEngine.Object);\n\n\t  // Get binding members info\n\t  List<ValueWithAttribute<BindAttribute>> members = new List<ValueWithAttribute<BindAttribute>>();\n\t  TypeInfoCache.GetMemberValuesWithAttribute<BindAttribute>(data, members);\n\t  // Context doesn't have any bindable members\n\t  if (members.Count == 0)\n\t\treturn false;\n\n\t  // Split it & remove '~' and '::'\n\t  var scopes = currentScope.Split(stringSplitOptions, StringSplitOptions.RemoveEmptyEntries);\n\t  // We're at the leaf scope - bind\n\t  if (scopes.Length == 1)\n\t  {\n\t\t// Override the element's path now we found the scope\n\t\tel.bindingPath = currentScope;\n\n\t\t// Start a new binding branch here and terminate the one we came from\n\t\tif (createSubScope)\n\t\t{\n\t\t  BindRecursive(el, data, members, owner, createSubScope);\n\t\t  return true;\n\t\t}\n\t\t// Only bind the element values, and carry on with the child binding as usual\n\t\telse\n\t\t{\n\t\t  BindElementValues(el, ref data, members, owner);\n\t\t  return false;\n\t\t}\n\t  }\n\n\t  // Select the topmost scope\n\t  string targetScope = scopes[0];\n\n\t  ValueWithAttribute<BindAttribute>[] matchingMembers = members.Where(x => x.Attribute.Path?.ToLower() == targetScope?.ToLower()).ToArray();\n\t  // Might need/want to throw an error here\n\t  if (matchingMembers.Length == 0)\n\t\treturn false;\n\n\t  bool startedBranch = false;\n\t  string newPath = currentScope.Substring(currentScope.IndexOf(nestedScopeChar) + 1);\n\t  foreach (var member in matchingMembers)\n\t  {\n\t\tif (DrillDownToChildScopeRecursive(el, member.Value, owner, newPath, createSubScope))\n\t\t  startedBranch = true;\n\t  }\n\t  return startedBranch;\n\t}\n\n\t#region Internals\n\tinternal static bool BindingPathMatch(in string a, in string b)\n\t{\n\t  return string.CompareOrdinal(a, b) == 0;\n\t}\n\tinternal static bool BindingPathMatch(BindableElement el, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  return string.CompareOrdinal(el.bindingPath, member.Attribute.Path) == 0;\n\t}\n\tinternal static bool BindingPathOrTypeMatch<T>(BindableElement el, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  return string.CompareOrdinal(el.bindingPath, member.Attribute.Path) == 0 || (string.IsNullOrEmpty(member.Attribute.Path) && typeof(T).IsAssignableFrom(member.Type));\n\t}\n\tinternal static bool BindingPathOrTypeMatch<T>(in string path, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  return string.CompareOrdinal(path, member.Attribute.Path) == 0 || (string.IsNullOrEmpty(member.Attribute.Path) && typeof(T).IsAssignableFrom(member.Type));\n\t}\n\tinternal static bool BindingPathAndTypeMatch<T>(in BindableElement el, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  return string.CompareOrdinal(el.bindingPath, member.Attribute.Path) == 0 && typeof(T).IsAssignableFrom(member.Type);\n\t}\n\tinternal static bool BindingPathAndTypeMatch<T>(in string a, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  return string.CompareOrdinal(a, member.Attribute.Path) == 0 && typeof(T).IsAssignableFrom(member.Type);\n\t}\n\tinternal static string ObjectToString(in object obj, in Type t)\n\t{\n\t  // Add translation here\n\t  if (obj is string str)\n\t\treturn str;\n\t  else if (t.IsEnum)\n\t\treturn Enum.GetName(t, obj);\n\t  else if (obj != null)\n\t\treturn obj.ToString();\n\n\t  return default;\n\t}\n\t#endregion\n  }\n}"
  },
  {
    "path": "src/Core/Binding/Binder.cs.meta",
    "content": "fileFormatVersion: 2\nguid: f25cd91944aa2e546b1708c66852751c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding/Binding.cs",
    "content": "﻿\n\nusing System;\nusing System.ComponentModel;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n  using Kinstrife.Core.ReflectionHelpers;\n\n  /// <summary>\n  /// Base class for runtime Bindings between a context object and a VisualElement\n  /// </summary>\n  public abstract class Binding : IDisposable, IBinding\n  {\n\tpublic bool scheduleDispose;\n\tpublic virtual void Dispose()\n\t{\n\t}\n\n\tpublic abstract void PreUpdate();\n\n\tpublic abstract void Release();\n\n\tpublic abstract void Update();\n  }\n  public abstract class Binding<T> : Binding\n  {\n\tprotected object context;\n\t[SerializeField] protected T lastValue;\n\t[SerializeField] protected T newValue;\n\n\tprotected BindableElement element;\n\t[SerializeField] BindAttribute attribute;\n\n\t// The target field\n\tprotected string memberName;\n\tprotected ExtendedTypeInfo extendedTypeInfo;\n\n\tpublic Binding(BindableElement el, in object context, in ValueWithAttribute<BindAttribute> member)\n\t{\n\t  this.element = el;\n\t  this.context = context;\n\t  this.extendedTypeInfo = TypeInfoCache.GetExtendedTypeInfo(context.GetType()); // K: 28-10-2020 -> Could be optimized with member.MemberInfo.DeclaringType;\n\n\t  this.attribute = member.Attribute;\n\t  this.memberName = member.MemberInfo.Name;\n\n\t  //el.binding = this;\n\n\t  DetermineBindingMode();\n\t  RegisterEvents();\n\t}\n\n\tvoid DetermineBindingMode()\n\t{\n\t  if (context is INotifyPropertyChanged notifyPropertyChanged)\n\t  {\n\t\tnotifyPropertyChanged.PropertyChanged += Model_PropertyChanged;\n\t  }\n\t  // Specifically set to not have two-way binding\n\t  if (attribute.bindingMode.HasValue)\n\t  {\n\t\tif (attribute.bindingMode == BindingMode.TwoWay)\n\t\t  RegisterTwoWayValueChangeCallback();\n\t  }\n\t  // No value set - Determine based on control type\n\t  else\n\t  {\n\t\t// Can't two-way bind a label\n\t\tif (this.element is Label || element is If)\n\t\t  return;\n\t\telse if (this.element is INotifyValueChanged<T>)\n\t\t  RegisterTwoWayValueChangeCallback();\n\t  }\n\t}\n\n\tvoid RegisterEvents()\n\t{\n\t  if (context is IHasTooltip hasTooltip)\n\t  {\n\t\telement.tooltip = hasTooltip.Tooltip;\n\t\tif (element is BaseField<T> baseField)\n\t\t  baseField.labelElement.tooltip = hasTooltip.Tooltip;\n\n\t\tif (context is IBindableToVisualElement bindable)\n\t\t{\n\t\t  element.SetEnabled(bindable.isEnabled);\n\t\t  element.SetActive(bindable.isActive2);\n\t\t  element.SetShowHide(bindable.isShown);\n\t\t  bindable.SetBinding(element);\n\n\n\t\t  bindable.onSetEnabled += element.SetEnabled;\n\t\t  bindable.onShowHide += element.SetShowHide;\n\t\t  bindable.onSetActive += element.SetActive;\n\n\t\t  element.RegisterCallback<DetachFromPanelEvent>(OnDetach);\n\t\t}\n\t  }\n\t}\n\n\tvoid OnDetach(DetachFromPanelEvent evt) => UnregisterEvents();\n\n\tvoid UnregisterEvents()\n\t{\n\t  if (context is IBindableToVisualElement bindable)\n\t  {\n\t\tbindable.onSetEnabled -= element.SetEnabled;\n\t\tbindable.onShowHide -= element.SetShowHide;\n\t\tbindable.onSetActive -= element.SetActive;\n\t  }\n\t}\n\n\tvoid SyncVisualElementToModel()\n\t{\n\n\t}\n\n\tprotected virtual void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)\n\t{\n\t}\n\n\tpublic override void Dispose()\n\t{\n\t  base.Dispose();\n\t  UnregisterEvents();\n\t}\n\tpublic override void PreUpdate()\n\t{\n\t  throw new NotImplementedException();\n\t}\n\n\tpublic override void Release()\n\t{\n\t  throw new NotImplementedException();\n\t}\n\n\tpublic override void Update()\n\t{\n\t  // Needs to be disposed because the context ceased to exist\n\t  if (context == null || !IsValidBinding())\n\t  {\n\t\tscheduleDispose = true;\n\t\treturn;\n\t  }\n\n\t  newValue = GetValueFromMemberInfo();\n\n\t  UpdateFromModel(in newValue);\n\t}\n\n\tprotected virtual void UpdateFromModel(in T newValue)\n\t{\n\t  // Model changed -> Update view\n\t  if (this.lastValue != null && !this.lastValue.Equals(newValue))\n\t  {\n\t\tif (newValue is T && element is INotifyValueChanged<T> notifyValueChanged)\n\t\t  notifyValueChanged.SetValueWithoutNotify(newValue);\n\t\telse if (newValue is string text && element is TextElement textEl)\n\t\t  textEl.text = text;\n\t\telse if (newValue is string foldoutText && element is Foldout foldout)\n\t\t  foldout.text = foldoutText;\n\t\telse if (element is IBindableElement<object> bindableEl)\n\t\t  bindableEl.OnModelChange(newValue);\n\t\telse\n\t\t  Debug.LogError($\"No binding found for {element?.bindingPath} {context}\", context as UnityEngine.Object);\n\t  }\n\n\t  lastValue = newValue;\n\t}\n\n\tvoid RegisterTwoWayValueChangeCallback()\n\t{\n\t  if (element is INotifyValueChanged<T> notifyValChangedEl)\n\t  {\n\t\tnotifyValChangedEl.RegisterValueChangedCallback((evt) =>\n\t\t{\n\t\t  if (context is INotifyValueChanged<T> ctx)\n\t\t\tctx.value = notifyValChangedEl.value;\n\t\t  else\n\t\t\tSetValueFromMemberInfo(evt.newValue);\n\t\t});\n\t  }\n\t}\n\n\tprotected abstract bool IsValidBinding();\n\tprotected abstract T GetValueFromMemberInfo();\n\tprotected abstract void SetValueFromMemberInfo(T value);\n\n  }\n}"
  },
  {
    "path": "src/Core/Binding/Binding.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4c859ae80f4d89648b8c3b6dc1839149\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding/BindingAttribute.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing System.Runtime.CompilerServices;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  public class UIAttribute : System.Attribute\n  {\n\n  }\n\n  public enum BindingMode\n  {\n    /// <summary>\n    /// For immutable data. Use this for things like menu (buttons/labels) and static text.\n    /// </summary>\n    OneTime,\n    /// <summary>\n    /// For dynamic data. Use this for mutable data that isn't changable via UI.\n    /// </summary>\n    OneWay,\n    /// <summary>\n    /// For dynamic data which is changable through the UI.\n    /// </summary>\n    TwoWay\n  }\n\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindAttribute : UIAttribute\n  {\n    string path = \"\"; public string Path => path;\n    /// <summary>\n    /// The binding mode for this binding. When left null, the system will pick a binding mode based on the control type (recommended).\n    /// </summary>\n    public BindingMode? bindingMode = null; // By default don't override binding mode\n\n    public bool hideIfEmpty;\n\n    public BindAttribute()\n    {\n    }\n\n    public BindAttribute(string path)\n    {\n      this.path = path;\n    }\n    public BindAttribute(BindingMode bindingMode)\n    {\n      this.bindingMode = bindingMode;\n    }\n    public BindAttribute(string path, BindingMode bindingMode)\n    {\n      this.path = path;\n      this.bindingMode = bindingMode;\n\t}\n\tpublic BindAttribute(string path, BindingMode bindingMode, bool hideIfEmpty)\n\t{\n\t  this.path = path;\n\t  this.bindingMode = bindingMode;\n      this.hideIfEmpty = hideIfEmpty;\n\t}\n  }\n\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindBaseFieldAttribute : BindAttribute\n  {\n    public string label;\n    public bool showInputField;\n\n    public BindBaseFieldAttribute(string path, string label = null, bool showInput = false) : base(path)\n    {\n      this.label = label;\n      this.showInputField = showInput;\n    }\n\n    public BindBaseFieldAttribute(string path, BindingMode bindingMode, string label = null, bool showInput = false) : base(path, bindingMode)\n    {\n      this.label = label;\n      this.showInputField = showInput;\n    }\n  }\n\n  // For fields & properties\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindFloatAttribute : BindBaseFieldAttribute\n  {\n    public float startingValue;\n    public float lowValue;\n    public float highValue;\n\n    public BindFloatAttribute(string path, float startingValue, float min, float max, string label = null, bool showInput = false) : base(path, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.lowValue = min;\n      this.highValue = max;\n    }\n    public BindFloatAttribute(string path, BindingMode bindingMode, float startingValue, float min, float max, string label = null, bool showInput = false) : base(path, bindingMode, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.lowValue = min;\n      this.highValue = max;\n    }\n  }\n\n\n  // For fields & properties\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindIntAttribute : BindBaseFieldAttribute\n  {\n    public int startingValue;\n    public int lowValue;\n    public int highValue;\n\n    public BindIntAttribute(string path, int startingValue, int min, int max, string label = null, bool showInput = false) : base(path, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.lowValue = min;\n      this.highValue = max;\n    }\n    public BindIntAttribute(string path, BindingMode bindingMode, int startingValue, int min, int max, string label = null, bool showInput = false) : base(path, bindingMode, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.lowValue = min;\n      this.highValue = max;\n    }\n  }\n  // For fields & properties\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindRangeAttribute : BindBaseFieldAttribute\n  {\n\tpublic Vector2 startingValue;\n\tpublic float lowLimit;\n\tpublic float highLimit;\n\n\tpublic BindRangeAttribute(string path, Vector2 startingValue, float min, float max, string label = null) : base(path, label, showInput:false)\n\t{\n\t  this.startingValue = startingValue;\n\t  this.lowLimit = min;\n\t  this.highLimit = max;\n\t}\n\tpublic BindRangeAttribute(string path, BindingMode bindingMode, Vector2 startingValue, float min, float max, string label = null) : base(path, bindingMode, label, showInput: false)\n\t{\n\t  this.startingValue = startingValue;\n\t  this.lowLimit = min;\n\t  this.highLimit = max;\n\t}\n  }\n\n  // For fields & properties\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindStringAttribute : BindBaseFieldAttribute\n  {\n    public readonly string startingValue;\n    public readonly int maxLength;\n    public readonly bool readOnly;\n    public readonly bool multiLine;\n    public readonly bool password;\n\n    public BindStringAttribute(string path, string startingValue, int maxLength = -1, bool readOnly = false, bool multiLine = false, bool password = false, string label = null, bool showInput = false) : base(path, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.maxLength = maxLength;\n      this.readOnly = readOnly;\n      this.multiLine = multiLine;\n    }\n    public BindStringAttribute(string path, BindingMode bindingMode, string startingValue, int maxLength = -1, bool readOnly = false, bool multiLine = false, bool password = false, string label = null, bool showInput = false) : base(path, bindingMode, label, showInput)\n    {\n      this.startingValue = startingValue;\n      this.maxLength = maxLength;\n      this.readOnly = readOnly;\n      this.multiLine = multiLine;\n    }\n  }\n\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindTooltip : BindAttribute\n  {\n    public BindTooltip(string path) : base(path)\n    {\n    }\n  }\n\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class BindValueChangeCallbackAttribute : BindAttribute\n  {\n    public BindValueChangeCallbackAttribute(string path) : base(path)\n    {\n    }\n  }\n\n\n  /// <summary>\n  /// Marks a field or property to be drawn when an entire type is marked for rendering\n  /// </summary>\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Class)]\n  public class DrawAttribute : System.Attribute\n  {\n    public ControlType controlType = ControlType.None;\n\n    public int order;\n\n    //public DrawAttribute(ControlType controlType)\n    //{\n    //  this.controlType = controlType;\n    //}\n\n    public DrawAttribute([CallerLineNumber]int order = 0)\n    {\n      this.order = order;\n    }\n\n    public DrawAttribute(ControlType controlType, [CallerLineNumber] int order = 0)\n    {\n      this.controlType = controlType;\n      this.order = order;\n    }\n  }\n\n  public enum Typography\n  {\n    None,\n    h1,\n    h2,\n    h3,\n    h4,\n    h5,\n    h6,\n    subtitle1,\n    subtitle2,\n    body1,\n    body2,\n    caption,\n    overline\n  }\n\n  /// <summary>\n  /// Marks a field or property to be drawn when an entire type is marked for rendering\n  /// </summary>\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property | System.AttributeTargets.Class)]\n  public class DrawTextAttribute : DrawAttribute\n  {\n    public Typography typography = Typography.None;\n\n    public DrawTextAttribute([CallerLineNumber] int order = 0) : base(order)\n    {\n      this.order = order;\n    }\n\n    public DrawTextAttribute(Typography typography, [CallerLineNumber] int order = 0) : base(ControlType.Label, order)\n    {\n      this.typography = typography;\n    }\n  }\n\n  [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property)]\n  public class RouteAttribute : UIAttribute\n  {\n    public RouteAttribute()\n    {\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Binding/BindingAttribute.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1f614598db3d14645979fed59a3b5945\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding/BindingsManager.cs",
    "content": "﻿\n\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using global::Graphene.Elements;\n  using Kinstrife.Core.ReflectionHelpers;\n  using UnityEngine;\n  using UnityEngine.Profiling;\n\n  /// <summary>\n  /// Manages the creation, updating, and disposal of bindings between UI elements and data contexts.\n  /// </summary>\n  public class BindingsManager : GrapheneComponent\n  {\n\t#region ShowInInspectorAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.ShowInInspector]\n#endif\n\t#endregion\n\t/// <summary>\n\t/// Mapping of all current bindings, keyed by panels\n\t/// </summary>\n\tDictionary<Plate, List<Binding>> bindings = new Dictionary<Plate, List<Binding>>();\n\n\t#region ShowInInspectorAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.ShowInInspector]\n#endif\n\t#endregion\n\tDictionary<Plate, List<Binding>> disposePostUpdate = new Dictionary<Plate, List<Binding>>();\n    Dictionary<Plate, List<Binding>> createPostUpdate = new Dictionary<Plate, List<Binding>>();\n\n    internal uint bindingsCount;\n\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.InfoBox(\"$bindingsInfo\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.InfoBox(\"bindingsInfo\")]\n#endif\n\t[SerializeField] float bindingRefreshRate = 0.2f;\n\n\n#if UNITY_EDITOR\n\tpublic string bindingsInfo => $\"{bindingsCount} bindings\";\n#endif\n\n\t#region ReadOnlyAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ReadOnly, Sirenix.OdinInspector.ShowInInspector]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.ReadOnly, NaughtyAttributes.ShowInInspector]\n#endif\n\t#endregion\n\tfloat lastRefreshTime;\n\n\t//#if UNITY_EDITOR\n\t//    [UnityEditor.InitializeOnEnterPlayMode]\n\t//    public static void InitializeOnEnterPlayMode()\n\t//    {\n\t//      bindings = new Dictionary<Plate, List<Binding>>();\n\t//      disposePostUpdate = new Dictionary<Plate, List<Binding>>();\n\t//      createPostUpdate = new Dictionary<Plate, List<Binding>>();\n\t//    }\n\t//#endif\n\n\n\tvoid LateUpdate()\n\t{\n#if UNITY_EDITOR\n      if (!Application.isPlaying && !runInEditMode)\n\t\treturn;\n#endif\n\n      if (Time.unscaledTime - lastRefreshTime < bindingRefreshRate)\n\t\treturn;\n\n\t  if (!graphene || !graphene.IsActiveAndVisible)\n\t\treturn;\n\n\t  OnUpdate();\n\t  lastRefreshTime = Time.unscaledTime;\n\t}\n\tpublic void OnUpdate()\n\t{\n      bindingsCount = 0;\n#if UNITY_ASSERTIONS\n\t  Profiler.BeginSample(\"Update Bindings\", this);\n#endif\n\t  // Update the bindings for active/visible panels\n\t  foreach (var kvp in bindings)\n     {\n\t\tvar plate = kvp.Key;\n        // Was disposed\n        if (!plate)\n          continue;\n\t\t// The panel is invisible, or inactive\n\t\tif (!plate.IsActive || !plate.Graphene.IsActiveAndVisible)\n          continue;\n\n        if (plate.bindingRefreshMode == BindingRefreshMode.None || (plate.bindingRefreshMode == BindingRefreshMode.ModelChange && !plate.wasChangedThisFrame))\n          continue;\n        plate.wasChangedThisFrame = false;\n\n#if UNITY_ASSERTIONS\n\t\tProfiler.BeginSample(plate.DebugName, plate);\n#endif\n\t\tforeach (var binding in kvp.Value)\n        {\n          // Needs to be disposed\n          if (binding.scheduleDispose)\n          {\n            ScheduleDispose(kvp.Key, binding);\n          }\n          // Update the binding\n          else\n          {\n            binding.Update();\n            bindingsCount++;\n          }\n        }\n#if UNITY_ASSERTIONS\n        Profiler.EndSample();\n#endif\n\t  }\n#if UNITY_ASSERTIONS\n\t  Profiler.EndSample();\n#endif\n\n#if UNITY_ASSERTIONS\n\t  Profiler.BeginSample(\"CreateDispose\");\n#endif\n\t  // Create bindings\n\t  foreach (var kvp in createPostUpdate)\n      {\n        var list = GetList(kvp.Key, bindings);\n        foreach (var binding in kvp.Value)\n          list.Add(binding);\n      }\n\n      // Dispose unused bindings\n      foreach (var kvp in disposePostUpdate)\n        foreach (var binding in kvp.Value)\n          Destroy(kvp.Key, binding);\n\n\n      createPostUpdate.Clear();\n      disposePostUpdate.Clear();\n\n#if UNITY_ASSERTIONS\n\t  Profiler.EndSample();\n#endif\n\t}\n\n\tList<Binding> GetList(Plate panel, Dictionary<Plate, List<Binding>> bindings)\n    {\n      if (bindings.ContainsKey(panel))\n        return bindings[panel];\n\n      List<Binding> list = new List<Binding>();\n      bindings.Add(panel, list);\n      return list;\n    }\n\n    /// <summary>\n    /// Creates a continuous binding between a TextElement and a member variable on a context (scope) and panel\n    /// </summary>\n    /// <param name=\"el\"></param>\n    /// <param name=\"context\"></param>\n    /// <param name=\"bindingPath\"></param>\n    /// <param name=\"panel\"></param>\n    public void TryCreate(TextElement el, ref object context, in ValueWithAttribute<BindAttribute> member, Plate panel)\n    {\n      // Specifically set to one-time -> cancel binding\n      if (member.Attribute.bindingMode.HasValue && member.Attribute.bindingMode.Value == BindingMode.OneTime)\n        return;\n\n      CreateBinding<string>(el, in context, in member, panel);\n    }\n\n    /// <summary>\n    /// Creates a continuous binding between a BaseField<typeparamref name=\"TValueType\"/> and a member variable on a context (scope) and panel\n    /// </summary>\n    /// <typeparam name=\"TValueType\"></typeparam>\n    /// <param name=\"el\"></param>\n    /// <param name=\"context\"></param>\n    /// <param name=\"member\"></param>\n    /// <param name=\"panel\"></param>\n    public void TryCreate<TValueType>(BaseField<TValueType> el, in object context, in ValueWithAttribute<BindAttribute> member, Plate panel)\n    {\n      // Specifically set to one-time\n      if (member.Attribute.bindingMode.HasValue && member.Attribute.bindingMode.Value != BindingMode.OneTime)\n        return;\n\n      CreateBinding<TValueType>(el, in context, in member, panel);\n    }\n\n\n    /// <summary>\n    /// Creates a continuous binding between a TextElement and a member variable on a context (scope) and panel\n    /// </summary>\n    /// <param name=\"el\"></param>\n    /// <param name=\"context\"></param>\n    /// <param name=\"bindingPath\"></param>\n    /// <param name=\"panel\"></param>\n    public void TryCreate<TValueType>(BindableElement el, in object context, in ValueWithAttribute<BindAttribute> member, Plate panel)\n    {\n      // Specifically set to one-time -> cancel binding\n      if (member.Attribute.bindingMode.HasValue && member.Attribute.bindingMode.Value == BindingMode.OneTime)\n        return;\n\n      CreateBinding<TValueType>(el, in context, in member, panel);\n    }\n\n    internal void CreateBinding<TValueType>(BindableElement el, in object context, in ValueWithAttribute<BindAttribute> member, Plate panel)\n    {\n      Binding binding = null;\n      // Collection binding\n      if (el is ListView && typeof(TValueType).IsAssignableFrom(typeof(ICollection)))\n        binding = new CollectionBinding(el, in context, in member);\n      // Single binding\n      else\n        binding = new MemberBinding<TValueType>(el, in context, in member);\n\n      if (binding != null)\n        GetList(panel, createPostUpdate).Add(binding);\n    }\n\n    void ScheduleDispose(Plate panel, Binding binding)\n    {\n      GetList(panel, disposePostUpdate).Add(binding);\n    }\n\n    void Destroy(Plate panel, Binding binding)\n    {\n      GetList(panel, bindings).Remove(binding);\n      binding.Dispose();\n      binding = null;\n    }\n\n    internal void DisposePlate(Plate plate, bool isDestroyed)\n    {\n      if(bindings.TryGetValue(plate, out var list))\n      {\n        if (isDestroyed)\n          bindings.Remove(plate);\n        else\n          list.Clear();\n\t  }\n      if (disposePostUpdate.ContainsKey(plate))\n\t\tdisposePostUpdate.Remove(plate);\n    }\n  }\n\n}"
  },
  {
    "path": "src/Core/Binding/BindingsManager.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4e490e9f02423f245a808e66b6d9c602\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding/CollectionBinding.cs",
    "content": "﻿using System.Collections;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n    using Kinstrife.Core.ReflectionHelpers;\n\n    public class CollectionBinding : Binding<ICollection>\n  {\n    protected int? lastLength;\n\n    public CollectionBinding(BindableElement el, in object context, in ValueWithAttribute<BindAttribute> member) : base(el, in context, in member)\n    {\n      if (member.Value is ICollection)\n      {\n      }\n      else\n      {\n        scheduleDispose = true;\n        return;\n      }\n\n      lastValue = GetValueFromMemberInfo();\n      lastLength = lastValue?.Count;\n    }\n\n    protected override bool IsValidBinding()\n    {\n      return memberName != null;\n    }\n    protected override ICollection GetValueFromMemberInfo()\n    {\n      return (ICollection)extendedTypeInfo.Accessor[context, memberName];\n    }\n    protected override void SetValueFromMemberInfo(ICollection value)\n    {\n      extendedTypeInfo.Accessor[context, memberName] = value;\n    }\n\n    protected override void UpdateFromModel(in ICollection newValue)\n    {\n      // Collection reference/count changed -> Assign new list\n      if (!this.lastValue.Equals(newValue) || newValue.Count != lastLength.Value)\n        if (element is ListView listView && newValue is IList iList)\n        {\n          listView.itemsSource = iList;\n          lastLength = iList?.Count;\n        }\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Binding/CollectionBinding.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 6df237851253fc241a8dd9e269f6276c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding/MemberBinding.cs",
    "content": "﻿using UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Kinstrife.Core.ReflectionHelpers;\n  using UnityEngine;\n\n  public class MemberBinding<T> : Binding<T>\n  {\n\tpublic MemberBinding(BindableElement el, in object context, in ValueWithAttribute<BindAttribute> member) : base(el, in context, in member)\n\t{\n\t  lastValue = GetValueFromMemberInfo();\n\t}\n\n\tprotected override bool IsValidBinding()\n\t{\n\t  return context != null;\n\t}\n\n\tprotected override T GetValueFromMemberInfo()\n\t{\n#if UNITY_ASSERTIONS\n\t  var val = extendedTypeInfo.Accessor[context, memberName];\n\t // if(val == null)\n\t // {\n\t\t//Debug.LogError($\"Trying to cast a null member {memberName} {extendedTypeInfo.Accessor.Type}\");\n\t\t//return default(T);\n\t // }\n\t  if (val != null && !typeof(T).IsAssignableFrom(val.GetType()))\n\t  {\n\t\tDebug.LogError($\"InvalidCastException for member {memberName} {extendedTypeInfo.Accessor.Type}: Trying to cast binding {val.GetType().Name} to {typeof(T).Name}. \" +\n\t\t  $\"\\n{element.GetType().Name}\");\n\t\treturn default(T);\n\t  }\n#endif\n\n\t  return (T)extendedTypeInfo.Accessor[context, memberName];\n\t}\n\n\tprotected override void SetValueFromMemberInfo(T value)\n\t{\n\t  extendedTypeInfo.Accessor[context, memberName] = value;\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Binding/MemberBinding.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 51eed3340e1759745903e1cc5f44143d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Binding.meta",
    "content": "fileFormatVersion: 2\nguid: 6c2a5a11ef9582442b314d518afb15ec\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/ButtonGroup.cs",
    "content": "﻿\nusing Graphene.ViewModel;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class ButtonGroup : GroupBox, IBindableElement<int>, INotifyValueChanged<int>\n  {\n    public const string itemsPath = \"Items\";\n\n    [SerializeField]\n    private List<string> m_Items = new List<string>();\n\n    public List<string> items\n    {\n      get => m_Items;\n      set\n      {\n        SetItems(value);\n      }\n    }\n\n    /// <summary>\n    /// Instantiates a <see cref=\"id\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<ButtonGroup, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"id\"/>.\n    /// </summary>\n    public new class UxmlTraits : BindableElement.UxmlTraits\n    {\n      UxmlIntAttributeDescription m_ActiveIndex = new UxmlIntAttributeDescription { name = \"activeIndex\" };\n      UxmlStringAttributeDescription m_Items = new UxmlStringAttributeDescription { name = \"items\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"id\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n\n\t\tButtonGroup buttonGroup = (ButtonGroup)ve;\n\n\t\tbuttonGroup.value = m_ActiveIndex.GetValueFromBag(bag, cc);\n\t\tbuttonGroup.items = SelectField.ParseChoiceList(m_Items.GetValueFromBag(bag, cc));\n      }\n    }\n\n    [SerializeField]\n    private int m_ActiveIndex = 0;\n    public virtual int value\n    {\n      get { return m_ActiveIndex; }\n      set\n      {\n        value = Mathf.Clamp(value, 0, childCount - 1);\n        if (m_ActiveIndex == value)\n        {\n          SetValueWithoutNotify(value);\n          return;\n        }\n\n        // in order for the serialization binding to update it's expecting you\n        // to dispatch the event\n        using (ChangeEvent<int> valueChangeEvent = ChangeEvent<int>.GetPooled(m_ActiveIndex, value))\n        {\n          valueChangeEvent.target = this; // very umportant\n          SetValueWithoutNotify(value); // actually set the value and do any init with the value\n          SendEvent(valueChangeEvent);\n        }\n      }\n    }\n\n    public event System.Action<int, string> clicked;\n\n    public void SetValueWithoutNotify(int value)\n    {\n      m_ActiveIndex = Mathf.Clamp(value, 0, childCount - 1);\n      SetButtonActive();\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Unity adds this USS class to every instance of the TabGroup element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static new readonly string ussClassName = \"gr-button-group \";\n    public static readonly string ussActiveClassName = \"active\";\n\n    /// <summary>\n    /// Constructs an TabGroup.\n    /// </summary>\n    public ButtonGroup()\n    {\n      AddToClassList(ussClassName);\n    }\n\n    /// <summary>\n    /// Callback from two-way binding system that model changed\n    /// </summary>\n    /// <param name=\"newValue\"></param>\n    public void OnModelChange(int newValue)\n    {\n      tabIndex = newValue;\n    }\n\n    internal void SetButtonActive()\n    {\n      int i = 0;\n      foreach (var child in Children())\n      {\n        if (i == value)\n        {\n          child.AddToClassList(ussActiveClassName);\n        }\n        else\n          child.RemoveFromClassList(ussActiveClassName);\n        i++;\n      }\n    }\n\n    public void SetItems(List<string> items)\n    {\n      m_Items = items ?? new List<string>();\n      RefreshButtons();\n    }\n\n    public IList<ActionButton> SourceData = new List<ActionButton>();\n\n    public void RefreshButtons()\n\t{\n\t  Clear();\n\n      foreach (var item in items)\n      {\n\t\tInternalAddItem(item);\n\t  }\n\t}\n\n    internal void ButtonClicked(int i)\n    {\n      value = i;\n      clicked?.Invoke(i, items[i]);\n\n      if (SourceData != null && i < SourceData.Count)\n        SourceData[i].OnClick?.Invoke();\n    }\n\n    public void ClearItems()\n    {\n      m_Items.Clear();\n      Clear();\n    }\n\n    public void ClearCallback()\n    {\n      clicked = null;\n    }\n\n    public void AddItem(string text, string tooltip = null)\n    {\n      items.Add(text);\n      InternalAddItem(text, tooltip);\n\t}\n\n    void InternalAddItem(string text, string tooltip = null)\n\t{\n\t  int buttonIndex = childCount;\n\t  var btn = new Button(() => ButtonClicked(buttonIndex));\n\t  btn.text = text.ToUpper();\n\t  btn.tooltip = tooltip;\n\t  btn.AddToClassList(\"gr-button\");\n\t  Add(btn);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/ButtonGroup.cs.meta",
    "content": "fileFormatVersion: 2\nguid: bc01c42213f651244a970ed2b40bca5a\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/CycleField.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\n\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class CycleField : BaseField<int>\n  {\n    public const string itemsPath = \"Items\";\n\n    [SerializeField]\n    private List<string> m_Items = new List<string>();\n\n    public List<string> items { get => m_Items;\n    set\n      {\n        if (value != null)\n          m_Items = value;\n        else\n          m_Items = new List<string>();\n      }\n    }\n\n    /// <summary>\n    /// Instantiates a <see cref=\"CycleField\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<CycleField, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"CycleField\"/>.\n    /// </summary>\n    public new class UxmlTraits : BaseFieldTraits<int, UxmlIntAttributeDescription>\n    {\n      UxmlStringAttributeDescription m_Text = new UxmlStringAttributeDescription { name = \"text\" };\n      UxmlStringAttributeDescription m_Items = new UxmlStringAttributeDescription { name = \"items\" };\n      UxmlStringAttributeDescription m_Plus = new UxmlStringAttributeDescription { name = \"plusSymbol\" };\n      UxmlStringAttributeDescription m_Minus = new UxmlStringAttributeDescription { name = \"minusSymbol\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"CycleField\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n        var cycleField = (CycleField)ve;\n\t\tcycleField.text = m_Text.GetValueFromBag(bag, cc);\n\t\tcycleField.items = m_Items.GetValueFromBag(bag, cc).Split(';').Where(x => !string.IsNullOrEmpty(x)).ToList();\n        cycleField.plusSymbol = m_Plus.GetValueFromBag(bag, cc);\n        cycleField.minusSymbol = m_Minus.GetValueFromBag(bag, cc);\n\t  }\n\t}\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    public new static readonly string ussClassName = \"gr-cycle-field\";\n    /// <summary>\n    /// USS class name of labels in elements of this type.\n    /// </summary>\n    public new static readonly string labelUssClassName = ussClassName + \"__label\";\n    /// <summary>\n    /// USS class name of input elements in elements of this type.\n    /// </summary>\n    public new static readonly string inputUssClassName = ussClassName + \"__input\";\n    /// <summary>\n    /// USS class name of elements of this type, when there is no text.\n    /// </summary>\n    public static readonly string noTextVariantUssClassName = ussClassName + \"--no-text\";\n\n\n    public static readonly string previousButtonUssClassName = ussClassName + \"__button-previous\";\n    public static readonly string nextButtonUssClassName = ussClassName + \"__button-next\";\n\n    /// <summary>\n    /// USS class name of text elements in elements of this type.\n    /// </summary>\n    public static readonly string textUssClassName = ussClassName + \"__text\";\n\n    public static readonly string hiddenClassName = \"hidden\";\n\n\n    private Label m_Label;\n    private VisualElement visualInput;\n\n    Button m_Next;\n    Button m_Previous;\n\n\tpublic string plusSymbol = \"›\";   // › ▶〉→\n\tpublic string minusSymbol = \"‹\";  // ‹ ◀〈 ←\n\n\tpublic CycleField()\n        : this(null) {\n    }\n\n    public CycleField(string label)\n        : base(label, null)\n    {\n      // Hax\n      var children = hierarchy.Children().ToList();\n      visualInput = hierarchy.Children().ToList().Find(x => x.ClassListContains(\"unity-base-field__input\"));\n\n      AddToClassList(ussClassName);\n      AddToClassList(noTextVariantUssClassName);\n\n      visualInput.AddToClassList(inputUssClassName);\n      labelElement.AddToClassList(labelUssClassName);\n\n      // The picking mode needs to be Position in order to have the Pseudostate Hover applied...\n      visualInput.pickingMode = PickingMode.Position;\n\n      // Allocate and add the buttons to the hierarchy\n      m_Previous = new Button();\n      m_Next = new Button();\n\n      m_Previous.text = minusSymbol;\n      m_Next.text = plusSymbol;\n      //m_Next.AddToClassList(\"bold\");\n      //m_Previous.AddToClassList(\"bold\");\n\n      m_Previous.focusable = true;\n      m_Next.focusable = true;\n\n      m_Previous.AddToClassList(previousButtonUssClassName);\n      m_Next.AddToClassList(nextButtonUssClassName);\n\t  m_Previous.AddToClassList(\"nomargin\");\n\t  m_Next.AddToClassList(\"nomargin\");\n\n\t  visualInput.Add(m_Previous);\n\n      m_Label = new Label\n      {\n        pickingMode = PickingMode.Ignore\n      };\n      m_Label.text = \"Select an option\";\n      visualInput.Add(m_Label);\n      visualInput.Add(m_Next);\n\n      m_Previous.clicked += M_Previous_clicked;\n      m_Next.clicked += M_Next_clicked;\n\n      // Set-up the label and text...\n      text = null;\n    }\n\n    private void M_Previous_clicked()\n    {\n      int newValue = value - 1;\n      if (newValue < 0)\n        newValue = items.Count - 1;\n\n      value = newValue;\n    }\n\n    private void M_Next_clicked()\n    {\n      int newValue = value + 1;\n      if (newValue >= items.Count)\n        newValue = 0;\n\n      value = newValue;\n    }\n\n    public override void SetValueWithoutNotify(int newValue)\n    {\n      base.SetValueWithoutNotify(newValue);\n      string newText = \"\";\n\n      if (newValue >= 0 && newValue < items.Count)\n        newText = items[newValue];\n\n      text = newText;\n    }\n\n\n    /// <summary>\n    /// Optional text after the toggle.\n    /// </summary>\n    public string text\n    {\n      get { return m_Label?.text; }\n      set\n      {\n        if (!string.IsNullOrEmpty(value))\n        {\n          // Lazy allocation of label if needed...\n          if (m_Label == null)\n          {\n            m_Label = new Label\n            {\n              pickingMode = PickingMode.Ignore\n            };\n            m_Label.AddToClassList(textUssClassName);\n            RemoveFromClassList(noTextVariantUssClassName);\n            visualInput.Add(m_Label);\n          }\n\n          m_Label.text = value;\n        }\n      }\n    }\n\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/CycleField.cs.meta",
    "content": "fileFormatVersion: 2\nguid: eeb1b79b04839bd4389dd0e1a6457758\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/Dialog.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\n\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class Dialog : VisualElement, IDisposable\n  {\n    /// <summary>\n    /// Instantiates a <see cref=\"Dialog\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<Dialog, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"Dialog\"/>.\n    /// </summary>\n    public new class UxmlTraits : BindableElement.UxmlTraits\n    {\n      /// <summary>\n      /// Initialize <see cref=\"Dialog\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    public static readonly string ussClassName = \"gr-dialog\";\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    public static readonly string dialogBackgroundUssClassName = ussClassName + \"__background\";\n    public static readonly string dialogPanelUssClassName = ussClassName + \"__panel\";\n\n    public static readonly string hiddenClassName = \"hidden\";\n\n    VisualElement m_Background;\n    private View m_PanelView;\n\n    public event System.Action onClose;\n\n    public Dialog()\n        : this(null, null) {\n    }\n\n    public Dialog(IPanel panel, VisualElement content)\n    {\n      AddToClassList(ussClassName);\n\n      // Set-up the label and text...\n      //this.AddManipulator(new Clickable(OnClickEvent));\n\n      if (panel == null)\n        panel = this.panel;\n\n      m_Background = new VisualElement();\n      m_Background.AddToClassList(dialogBackgroundUssClassName);\n      m_Background.AddToClassList(\"unity-ui-document__child\");\n      m_Background.AddManipulator(new Clickable(OnClickBackground));\n\n      // Add background to root\n      panel?.TopRoot().Add(m_Background);\n      //panel.visualTree.Add(m_Background);\n      \n      // Add dialog to background\n      m_Background.Add(this);\n      //hierarchy.Add(m_Background);\n\n      //Set up view\n      m_PanelView = new View(\"GR__Content\");\n      this.Add(m_PanelView);\n\n      m_PanelView.isDefault = true;\n\n      m_PanelView.AddToClassList(dialogPanelUssClassName);\n      m_PanelView.Focus();\n\n      if (content != null)\n      {\n        m_PanelView.Add(content);\n        content.Focus();\n      }\n\n      m_Background.BringToFront();\n      this.BringToFront();\n\n    }\n\n\n    bool ProcessClick(EventBase evt)\n    {\n      if (evt.eventTypeId == MouseUpEvent.TypeId())\n      {\n        var ce = (IMouseEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true; \n        }\n      }\n      else if (evt.eventTypeId == PointerUpEvent.TypeId() || evt.eventTypeId == ClickEvent.TypeId())\n      {\n        var ce = (IPointerEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true;\n\n        }\n      }\n      return false;\n    }\n\n    void OnClickBackground(EventBase evt)\n    {\n      if (ProcessClick(evt))\n      {\n        onClose?.Invoke();\n        Dispose();\n      }\n    }\n\n    public void Dispose()\n    {\n      if(this.m_Background != null && this.m_Background.parent != null)\n        this.m_Background.parent.Remove(this.m_Background);\n    }\n\n    public Dialog WithStyles(VisualElementStyleSheetSet styleSheets)\n    {\n      for (int i = 0; i < styleSheets.count; i++)\n      {\n        m_Background.styleSheets.Add(styleSheets[i]);\n      }\n      return this;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/Dialog.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 6ba5abe3b1bb58c439073459c2b50355\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/GrapheneRoot.cs",
    "content": "﻿\nusing System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  /// <summary>\n  /// Root Graphene class that contains injec\n  /// </summary>\n  public class GrapheneRoot : BindableElement\n  {\n    ///// <summary>\n    ///// Instantiates a <see cref=\"GrapheneRoot\"/> using the data read from a UXML file.\n    ///// </summary>\n    //public new class UxmlFactory : UxmlFactory<GrapheneRoot, UxmlTraits> { }\n\n    [SerializeField]\n    private Router m_Router;\n    public virtual Router router\n    {\n      get { return router; }\n      set\n      {\n        m_Router = value;\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Unity adds this USS class to every instance of the GrapheneRoot element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static readonly string ussClassName = \"gr-root\";\n\n    /// <summary>\n    /// Constructs a GrapheneRoot.\n    /// </summary>\n    public GrapheneRoot() : this(null)\n    {\n    }\n\n    /// <summary>\n    /// Constructs a GrapheneRoot.\n    /// </summary>\n    public GrapheneRoot(Router router)\n    {\n      AddToClassList(ussClassName);\n      this.AddMultipleToClassList($\"absolute fill {VisualElementExtensions.documentChildUssClassName}\");\n      this.router = router;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/GrapheneRoot.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 41eb40e62488c9842b7021276222eaf1\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/If.cs",
    "content": "﻿\nusing System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class If : BindableElement, IBindableElement<object>\n  {\n    /// <summary>\n    /// Instantiates a <see cref=\"id\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<If, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"id\"/>.\n    /// </summary>\n    public new class UxmlTraits : BindableElement.UxmlTraits\n    {\n      UxmlBoolAttributeDescription m_Value = new UxmlBoolAttributeDescription { name = \"value\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"id\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n\n        bool startEnabled = false;\n#if UNITY_EDITOR\n\t\tstartEnabled = Application.isPlaying ? false : m_Value.GetValueFromBag(bag, cc);\n#endif\n        ((If)ve).value = startEnabled;\n      }\n    }\n\n    private bool m_Value = false;\n    public virtual bool value\n    {\n      get { return m_Value; }\n      set\n      {\n        m_Value = value;\n        if (m_Value)\n          this.Show();\n        else\n          this.Hide();\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Unity adds this USS class to every instance of the If element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static readonly string ussClassName = \"gr-if\";\n\n    /// <summary>\n    /// Constructs an If.\n    /// </summary>\n    public If() \n    {\n      AddToClassList(ussClassName);\n      value = false;\n    }\n\n    public void OnModelChange(object newValue)\n    {\n      if (newValue is bool b)\n        value = b;\n      else if (ReferenceEquals(newValue, null))\n        value = false;\n      else if (newValue is null || newValue.Equals(false))\n        value = false;\n      else if (newValue is string s && (string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s)))\n        value = false;\n      else\n        value = true;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/If.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 6c0886185b9292347a8eef22665cc9e5\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/Route.cs",
    "content": "﻿\nusing System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n    public class Route : BindableElement\n  {\n    /// <summary>\n    /// Instantiates a <see cref=\"Route\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<Route, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"Route\"/>.\n    /// </summary>\n    public new class UxmlTraits : BindableElement.UxmlTraits\n    {\n      UxmlStringAttributeDescription m_Route = new UxmlStringAttributeDescription { name = \"route\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"View\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n\n        ((Route)ve).route = m_Route.GetValueFromBag(bag, cc);\n      }\n    }\n\n\n    public Router<string> router;\n    [SerializeField]\n    private string m_Route = String.Empty;\n    public virtual string route\n    {\n      get { return m_Route; }\n      set\n      {\n        m_Route = value?.ToLower();\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Unity adds this USS class to every instance of the Route element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static readonly string ussClassName = \"unity-route\";\n  \n    /// <summary>\n    /// Constructs a Route.\n    /// </summary>\n    public Route() : this(null)\n    {\n    }\n\n    /// <summary>\n    /// Constructs a route with an Action that is triggered when the button is clicked.\n    /// </summary>\n    /// <param name=\"clickEvent\">The action triggered when the button is clicked.</param>\n    /// <remarks>\n    /// By default, a single left mouse click triggers the Action. To change the activator, modify <see cref=\"clickable\"/>.\n    /// </remarks>\n    public Route(string route)\n    {\n      AddToClassList(ussClassName);\n      this.route = route;\n\n      // Add click to itself\n      this.AddManipulator(new Clickable(OnClickEvent));\n\n      clicked += Clicked;\n    }\n\n    public Action clicked;\n\n    public void Clicked()\n    {\n      router.TryChangeState(route);\n    }\n\n    bool ProcessClick(EventBase evt)\n    {\n      if (evt.eventTypeId == MouseUpEvent.TypeId())\n      {\n        var ce = (IMouseEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true;\n        }\n      }\n      else if (evt.eventTypeId == PointerUpEvent.TypeId() || evt.eventTypeId == ClickEvent.TypeId())\n      {\n        var ce = (IPointerEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true;\n\n        }\n      }\n      return false;\n    }\n\n    void OnClickEvent(EventBase evt)\n    {\n      if (ProcessClick(evt)) {\n        clicked.Invoke();\n      }\n    }\n\n    internal void SetRouter(Router r)\n    {\n      this.router = r as Router<string>;\n      r.onRoutingBlocked += OnRoutingBlocked;\n      r.onRoutingUnblocked += OnRoutingUnblocked;\n    }\n\n    private void OnRoutingUnblocked()\n    {\n      SetEnabled(true);\n    }\n\n    private void OnRoutingBlocked()\n    {\n      SetEnabled(false);\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/Route.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 2cc316cb1922df641ac18819655d7e7b\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/SelectField.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\n\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class SelectField : BaseField<int>, IDisposable\n  {\n    public const string itemsPath = \"Items\";\n    public int defaultItemHeight = 24;\n\n    [SerializeField]\n    private List<string> m_Items = new List<string>();\n\n    public List<string> items { get => m_Items;\n    set\n      {\n        if (value != null)\n          m_Items = value;\n        else\n          m_Items = new List<string>();\n      }\n    }\n\n    /// <summary>\n    /// Instantiates a <see cref=\"SelectField\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<SelectField, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"SelectField\"/>.\n    /// </summary>\n    public new class UxmlTraits : BaseFieldTraits<int, UxmlIntAttributeDescription>\n    {\n      UxmlIntAttributeDescription m_ItemHeight = new UxmlIntAttributeDescription { name = \"itemHeight\" };\n      UxmlStringAttributeDescription m_Text = new UxmlStringAttributeDescription { name = \"text\" };\n      UxmlStringAttributeDescription m_Items = new UxmlStringAttributeDescription { name = \"items\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"SelectField\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n\n        int itemHeight = m_ItemHeight.GetValueFromBag(bag, cc);\n        if (itemHeight > 0)\n          ((SelectField)ve).defaultItemHeight = itemHeight;\n\n        ((SelectField)ve).text = m_Text.GetValueFromBag(bag, cc);\n        ((SelectField)ve).items = ParseChoiceList(m_Items.GetValueFromBag(bag, cc));\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    public new static readonly string ussClassName = \"gr-select-field\";\n    /// <summary>\n    /// USS class name of labels in elements of this type.\n    /// </summary>\n    public new static readonly string labelUssClassName = ussClassName + \"__label\";\n    /// <summary>\n    /// USS class name of input elements in elements of this type.\n    /// </summary>\n    public new static readonly string inputUssClassName = ussClassName + \"__input\";\n    /// <summary>\n    /// USS class name of elements of this type, when there is no text.\n    /// </summary>\n    public static readonly string noTextVariantUssClassName = ussClassName + \"--no-text\";\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    public static readonly string listContainerUssClassName = ussClassName + \"__list-container\";\n    public static readonly string listViewUssClassName = ussClassName + \"__list-view\";\n    /// <summary>\n    /// USS class name of text elements in elements of this type.\n    /// </summary>\n    public static readonly string textUssClassName = ussClassName + \"__text\";\n\n    public static readonly string hiddenClassName = \"hidden\";\n\n    private Label m_Label;\n    private VisualElement visualInput;\n    private ListView m_ListView;\n    private Dialog m_Dialog;\n\n    Toggle m_Toggle;\n    \n\n    public SelectField()\n        : this(null) {\n    }\n\n    public SelectField(string label)\n        : base(label, null)\n    {\n      // Hax\n      var children = hierarchy.Children().ToList();\n      visualInput = hierarchy.Children().ToList().Find(x => x.ClassListContains(\"unity-base-field__input\"));\n\n      AddToClassList(ussClassName);\n      AddToClassList(noTextVariantUssClassName);\n\n      visualInput.AddToClassList(inputUssClassName);\n      labelElement.AddToClassList(labelUssClassName);\n\n      // The picking mode needs to be Position in order to have the Pseudostate Hover applied...\n      //visualInput.pickingMode = PickingMode.Position;\n\n      // Set-up the label and text...\n      text = null;\n      this.AddManipulator(new Clickable(OnClickEvent));\n\n      m_Toggle = new Toggle();\n      m_Toggle.text = text;\n      m_Toggle.RegisterValueChangedCallback((evt) =>\n      {\n        SetToggleState(m_Toggle.value);\n        evt.StopPropagation();\n      });\n      visualInput.Add(m_Toggle);\n      m_Toggle.Hide();\n\n      // Manual dispose\n      RegisterCallback<DetachFromPanelEvent>((evt) => Dispose());\n\n    }\n\n    private void M_ListView_onItemsChosen(IEnumerable<object> obj)\n    {\n      value = m_ListView.selectedIndex;\n    }\n\n    private void M_ListView_onSelectionChange(IEnumerable<object> obj)\n    {\n      value = m_ListView.selectedIndex;\n    }\n\n    public override void SetValueWithoutNotify(int newValue)\n    {\n      base.SetValueWithoutNotify(newValue);\n\n      string newText = \"\";\n      if (newValue >= 0 && newValue < m_Items.Count)\n        newText = m_Items[newValue];\n\n      text = newText;\n      m_Toggle.text = newText;\n    }\n\n    /// <summary>\n    /// Optional text after the toggle.\n    /// </summary>\n    public string text\n    {\n      get { return m_Label?.text; }\n      set\n      {\n        if (!string.IsNullOrEmpty(value))\n        {\n          // Lazy allocation of label if needed...\n          if (m_Label == null)\n          {\n            m_Label = new Label\n            {\n              pickingMode = PickingMode.Ignore\n            };\n            m_Label.AddToClassList(textUssClassName);\n            RemoveFromClassList(noTextVariantUssClassName);\n            visualInput.Add(m_Label);\n          }\n\n          m_Label.text = value;\n        }\n        else if (m_Label != null)\n        {\n          m_Label.RemoveFromHierarchy();\n          AddToClassList(noTextVariantUssClassName);\n          m_Label = null;\n        }\n      }\n    }\n\n    bool ProcessClick(EventBase evt)\n    {\n      if (evt.eventTypeId == MouseUpEvent.TypeId())\n      {\n        var ce = (IMouseEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true; \n        }\n      }\n      else if (evt.eventTypeId == PointerUpEvent.TypeId() || evt.eventTypeId == ClickEvent.TypeId())\n      {\n        var ce = (IPointerEvent)evt;\n        if (ce.button == (int)MouseButton.LeftMouse)\n        {\n          return true;\n\n        }\n      }\n      return false;\n    }\n\n    void OnClickEvent(EventBase evt)\n    {\n      if(ProcessClick(evt))\n        OnClick();\n    }\n\n    protected override void ExecuteDefaultActionAtTarget(EventBase evt)\n    {\n      base.ExecuteDefaultActionAtTarget(evt);\n\n      if (evt == null)\n      {\n        return;\n      }\n\n      if (IsActivationEvent(evt))\n      {\n        OnClick();\n        evt.StopPropagation();\n      }\n\n      bool IsActivationEvent(EventBase e)\n      {\n        if (e.eventTypeId == KeyDownEvent.TypeId())\n        {\n          var keyDownEvent = (KeyDownEvent)e;\n          return keyDownEvent.keyCode == KeyCode.KeypadEnter ||\n              keyDownEvent.keyCode == KeyCode.Return;\n        }\n        return false;\n      }\n    }\n\n\n    void OnClick()\n    {\n      m_Toggle.value = !m_Toggle.value;\n    }\n\n    void SetToggleState(bool value)\n    {\n      if (value)\n      {\n        m_ListView = CreateListView();\n        m_Dialog = new Dialog(panel, m_ListView);\n        m_Dialog.onClose += M_Dialog_onClose;\n      }\n    }\n\n    private void M_Dialog_onClose()\n    {\n      // Focus back on the select field\n      Focus();\n    }\n\n    VisualElement MakeItem()\n    {\n      return new Button();\n    }\n\n    void BindItem(VisualElement el, int index)\n    {\n      (el as TextElement).text = items[index];\n      Debug.Log($\"Created item at element {index}\");\n    }\n\n    ListView CreateListView()\n    {\n      var listView = new ListView(items, defaultItemHeight, MakeItem, BindItem);\n\n      listView.AddToClassList(listViewUssClassName);\n      listView.AddToClassList(\"h6\");\n      listView.bindingPath = \"Items\";\n      listView.focusable = true;\n\n      listView.onSelectionChange += M_ListView_onSelectionChange;\n      listView.onItemsChosen += M_ListView_onItemsChosen;\n\n      Func<VisualElement> makeItem = () => new Label(\"ListViewOption\");\n      Action<VisualElement, int> bindItem = (e, i) => (e as Label).text = (e as Label).text + \" \" + i;\n      bindItem = (e, i) => (e as Label).text = items[i];\n\n      listView.makeItem = makeItem;\n      listView.bindItem = bindItem;\n      //m_ListView.reorderable = true;\n      listView.itemsSource = items;\n      return listView;\n    }\n\n    public void Dispose()\n    {\n      if (m_Dialog != null)\n        m_Dialog.Dispose();\n    }\n\n\t#region Util\n\tinternal static List<string> ParseChoiceList(string choicesFromBag)\n\t{\n\t  if (string.IsNullOrEmpty(choicesFromBag.Trim()))\n\t  {\n\t\treturn null;\n\t  }\n\n\t  string[] array = choicesFromBag.Split(new char[1] { ',' });\n\t  if (array.Length != 0)\n\t  {\n\t\tList<string> list = new List<string>();\n\t\tstring[] array2 = array;\n\t\tforeach (string text in array2)\n\t\t{\n\t\t  list.Add(text.Trim());\n\t\t}\n\n\t\treturn list;\n\t  }\n\n\t  return null;\n\t}\n\t#endregion\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/SelectField.cs.meta",
    "content": "fileFormatVersion: 2\nguid: bac1afa6a79c1714680bfc884ada8948\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/TemplateTypes/Button.cs",
    "content": "﻿\nusing System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class GrButton : TemplateRef, IBindableElement<ControlType>, IGrapheneElement\n  {\n    /// <summary>\n    /// Instantiates a <see cref=\"id\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<GrButton, UxmlTraits> { }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Graphene adds this USS class to every instance of the Template element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static readonly new string ussClassName = \"gr-button-ref\";\n\n    /// <summary>\n    /// Constructs an Template.\n    /// </summary>\n    public GrButton() : this(null)\n    {\n      m_Type = ControlType.Button;\n    }\n\n    public GrButton(Renderer renderer) : base(renderer)\n    {\n      m_Type = ControlType.Button;\n      AddToClassList(ussClassName);\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/TemplateTypes/Button.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4063ec610a60a9049a8dc4a3a2d75ab8\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/TemplateTypes/TemplateRef.cs",
    "content": "﻿using System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class TemplateRef : BindableElement, IBindableElement<ControlType>, IGrapheneElement\n  {\n\t/// <summary>\n\t/// Instantiates a <see cref=\"id\"/> using the data read from a UXML file.\n\t/// </summary>\n\tpublic new class UxmlFactory : UxmlFactory<TemplateRef, UxmlTraits> { }\n\n\t/// <summary>\n\t/// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"type\"/>.\n\t/// </summary>\n\tpublic new class UxmlTraits : BindableElement.UxmlTraits\n\t{\n\t  UxmlEnumAttributeDescription<ControlType> m_Value = new UxmlEnumAttributeDescription<ControlType> { name = \"type\" };\n\n\t  /// <summary>\n\t  /// Initialize <see cref=\"id\"/> properties using values from the attribute bag.\n\t  /// </summary>\n\t  /// <param name=\"ve\">The object to initialize.</param>\n\t  /// <param name=\"bag\">The attribute bag.</param>\n\t  /// <param name=\"cc\">The creation context; unused.</param>\n\t  public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n\t  {\n\t\tbase.Init(ve, bag, cc);\n\t\tControlType type = m_Value.GetValueFromBag(bag, cc);\n\n\t\t// Only override if defined\n\t\tif (type != ControlType.None)\n\t\t  ((TemplateRef)ve).type = type;\n\t  }\n\t}\n\n\tprivate GrapheneRoot root;\n\tprotected VisualElement m_ChildTemplate;\n\tprotected Renderer renderer;\n\n\t[SerializeField]\n\tprotected ControlType m_Type = ControlType.None;\n\tpublic virtual ControlType type\n\t{\n\t  get { return m_Type; }\n\t  set\n\t  {\n\t\tm_Type = value;\n\n\t\tRender();\n\t  }\n\t}\n\n\t/// <summary>\n\t/// USS class name of elements of this type.\n\t/// </summary>\n\t/// <remarks>\n\t/// Unity adds this USS class to every instance of the Template element. Any styling applied to\n\t/// this class affects every button located beside, or below the stylesheet in the visual tree.\n\t/// </remarks>\n\tpublic static readonly string ussClassName = \"gr-template-ref\";\n\n\t/// <summary>\n\t/// Constructs an Template.\n\t/// </summary>\n\tpublic TemplateRef() : this(null)\n\t{\n\t}\n\n\tpublic TemplateRef(Renderer renderer)\n\t{\n\t  AddToClassList(ussClassName);\n\t  this.renderer = renderer;\n\t}\n\n\tpublic void Inject(GrapheneRoot root, Plate plate, Renderer renderer)\n\t{\n\t  this.renderer = renderer;\n\t}\n\n\tpublic void OnModelChange(ControlType newValue)\n\t{\n\t  type = newValue;\n\t}\n\n\tvoid InstantiateTemplate()\n\t{\n\t  renderer.Templates.TryGetTemplateAsset(this.type, out VisualTreeAsset template);\n\t  var clone = Binder.InternalInstantiate(template, renderer.Plate);\n\n\t  string name = this.name.Replace(\"Template\", \"\").Replace(\"TemplateRef\", \"\");\n\t  // Transfer binding path to top-level children & custom classes\n\t  if (!string.IsNullOrWhiteSpace(bindingPath))\n\t  {\n\t\tvar classes = this.GetClasses().Where(c => c != ussClassName);\n\n\t\tclone.Query<TemplateContainer>().Children<BindableElement>().ForEach(x =>\n\t\t{\n\t\t  x.bindingPath = bindingPath;\n\t\t  x.AddMultipleToClassList(classes);\n\n\t\t  if (!string.IsNullOrWhiteSpace(name))\n\t\t\tclone.name = name;\n\t\t}\n\t\t);\n\n\t\tthis.ClearClassList();\n\t\tthis.AddToClassList(ussClassName);\n\t  }\n\n\t  Add(clone);\n\t  m_ChildTemplate = clone;\n\t}\n\n\tpublic void Render()\n\t{\n\t  if (m_ChildTemplate != null)\n\t\tClearTemplate();\n\n\t  if (!renderer)\n\t\treturn;\n\n\t  // Clear \n\t  InstantiateTemplate();\n\t}\n\n\tvoid ClearTemplate()\n\t{\n\t  Remove(m_ChildTemplate);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/TemplateTypes/TemplateRef.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d20f4dbc23d31124ca95a7906001617f\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/TemplateTypes.meta",
    "content": "fileFormatVersion: 2\nguid: 4de6f3d3fa92a844a98caeaeabfba7d4\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/View.cs",
    "content": "﻿\nusing System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Elements\n{\n  public class View : VisualElement\n  {\n    /// <summary>\n    /// Instantiates a <see cref=\"id\"/> using the data read from a UXML file.\n    /// </summary>\n    public new class UxmlFactory : UxmlFactory<View, UxmlTraits> { }\n\n    /// <summary>\n    /// Defines <see cref=\"UxmlTraits\"/> for the <see cref=\"id\"/>.\n    /// </summary>\n    public new class UxmlTraits : BindableElement.UxmlTraits\n    {\n      UxmlStringAttributeDescription m_Id = new UxmlStringAttributeDescription { name = \"id\" };\n      UxmlBoolAttributeDescription m_Default = new UxmlBoolAttributeDescription { name = \"default\" };\n\n      /// <summary>\n      /// Initialize <see cref=\"id\"/> properties using values from the attribute bag.\n      /// </summary>\n      /// <param name=\"ve\">The object to initialize.</param>\n      /// <param name=\"bag\">The attribute bag.</param>\n      /// <param name=\"cc\">The creation context; unused.</param>\n      public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)\n      {\n        base.Init(ve, bag, cc);\n\n        ((View)ve).id = m_Id.GetValueFromBag(bag, cc);\n        ((View)ve).isDefault = m_Default.GetValueFromBag(bag, cc);\n      }\n    }\n\n    [SerializeField]\n    private string m_Id = String.Empty;\n    public virtual string id\n    {\n      get { return m_Id; }\n      set\n      {\n        m_Id = value;\n      }\n    }\n\n    [SerializeField]\n    private bool m_Default = false;\n    public virtual bool isDefault\n    {\n      get { return m_Default; }\n      set\n      {\n        m_Default = value;\n      }\n    }\n\n    /// <summary>\n    /// USS class name of elements of this type.\n    /// </summary>\n    /// <remarks>\n    /// Unity adds this USS class to every instance of the View element. Any styling applied to\n    /// this class affects every button located beside, or below the stylesheet in the visual tree.\n    /// </remarks>\n    public static readonly string ussClassName = \"unity-view\";\n\n    /// <summary>\n    /// Constructs a View.\n    /// </summary>\n    public View() : this(null)\n    {\n    }\n\n    /// <summary>\n    /// Constructs a View with an Action that is triggered when the button is clicked.\n    /// </summary>\n    /// <param name=\"clickEvent\">The action triggered when the button is clicked.</param>\n    /// <remarks>\n    /// By default, a single left mouse click triggers the Action. To change the activator, modify <see cref=\"clickable\"/>.\n    /// </remarks>\n    public View(string id) : base ()\n    {\n      AddToClassList(ussClassName);\n      this.id = id;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/View.cs.meta",
    "content": "fileFormatVersion: 2\nguid: a191edcfe0e527d4d91c17ee1926751d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions/VisualElementExtensions.cs",
    "content": "﻿using System.Collections.Generic;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  public static class VisualElementExtensions\n  {\n    internal const string documentRootUssClassName = \"unity-ui-document__root\";\n    internal const string documentChildUssClassName = \"unity-ui-document__child\";\n    internal const string hiddenUssClassName = \"hidden\";\n    internal const string invisibleUssClassName = \"invisible\";\n\tinternal const string fadeoutUssClassName = \"fadeout\";\n    internal const string transitioningUssClassName = \"transitioning\";\n    //internal const string fadeinUssClassName = \"fadein\";\n\n    internal const string activeUssClassName = \"active\";\n    internal const string selectedUssClassName = \"selected\";\n\n    public static bool IsHidden(this VisualElement el)\n    { \n      return el.ClassListContains(hiddenUssClassName);\n    }\n\n    public static void Show(this VisualElement el)\n    {\n      el.RemoveFromClassList(hiddenUssClassName);\n    }\n    public static void Hide(this VisualElement el)\n    {\n      el.AddToClassList(hiddenUssClassName);\n    }\n\n    public static void SetShowHide(this VisualElement el, bool value)\n    {\n      if (value)\n        el.Show();\n      else\n        el.Hide();\n    }\n\n\tpublic static void SetVisibility(this VisualElement el, bool value)\n\t{\n\t  if (value)\n\t\tel.RemoveFromClassList(invisibleUssClassName);\n\t  else\n\t\tel.AddToClassList(invisibleUssClassName);\n\t}\n\tpublic static void SetActive(this VisualElement el, bool value)\n    {\n      if(value)\n        el.AddToClassList(activeUssClassName);\n      else\n        el.RemoveFromClassList(activeUssClassName);\n\t}\n\n\tpublic static void ToggleClass(this VisualElement el, string className, bool value)\n\t{\n\t  if (value)\n\t\tel.AddToClassList(className);\n\t  else\n\t\tel.RemoveFromClassList(className);\n\t}\n\n\n\tpublic static void FadeIn(this VisualElement el)\n    {\n      el.RemoveFromClassList(fadeoutUssClassName);\n    }\n\n    public static void FadeOut(this VisualElement el)\n    {\n      el.AddToClassList(fadeoutUssClassName);\n    }\n\tpublic static void StartTransition(this VisualElement el)\n\t{\n\t  el.AddToClassList(transitioningUssClassName);\n\t}\n\tpublic static void StopTransition(this VisualElement el)\n\t{\n\t  el.RemoveFromClassList(transitioningUssClassName);\n\t}\n\n\tpublic static bool IsFadingOut(this VisualElement el)\n\t{\n\t  return el.ClassListContains(fadeoutUssClassName);\n\t}\n\n\tpublic static bool IsTransitioning(this VisualElement el)\n\t{\n\t  return el.ClassListContains(transitioningUssClassName);\n\t}\n\tpublic static VisualElement TopRoot(this VisualElement el)\n    {\n      return el.panel?.visualTree.Q(null, documentRootUssClassName);\n    }\n    public static VisualElement TopRoot(this IPanel panel)\n    {\n      return panel?.visualTree.Query(null, documentRootUssClassName).Last();\n    }\n\n    public static void AddStyles(this VisualElement el, VisualElementStyleSheetSet styleSheets)\n    {\n      for (int i = 0; i < styleSheets.count; i++)\n      {\n        var sheet = styleSheets[i];\n        if (sheet != null)\n          el.styleSheets.Add(sheet);\n#if UNITY_EDITOR\n        else\n          UnityEngine.Debug.LogError(\"Trying to add null stylesheet\");\n#endif\n      }\n    }\n\n    public static void AddStyles(this VisualElement el, IEnumerable<StyleSheet> styleSheets)\n    {\n      foreach (var styleSheet in styleSheets)\n      {\n        if(styleSheet)\n          el.styleSheets.Add(styleSheet);\n#if UNITY_EDITOR\n        else\n          UnityEngine.Debug.LogError(\"Trying to add null stylesheet\");\n#endif\n      }\n    }\n\n    /// <summary>\n    /// Adds multiple classes to VisualElement. ClassNames separated by space ' '.\n    /// </summary>\n    /// <param name=\"el\"></param>\n    /// <param name=\"classes\">Separated by space</param>\n    public static void AddMultipleToClassList(this VisualElement el, string classes)\n    {\n      if (string.IsNullOrWhiteSpace(classes))\n        return;\n        AddMultipleToClassList(el, Parse(classes));\n    }\n\n    public static void AddMultipleToClassList(this VisualElement el, IEnumerable<string> classes)\n    {\n      foreach (var className in classes)\n        el.AddToClassList(className);\n    }\n\n\n\t/// <summary>\n\t/// Removes multiple classes from VisualElement. ClassNames separated by space ' '.\n\t/// </summary>\n\t/// <param name=\"el\"></param>\n\t/// <param name=\"classes\">Separated by space</param>\n\tpublic static void RemoveMultipleFromClassList(this VisualElement el, string classes)\n\t{\n\t  if (string.IsNullOrWhiteSpace(classes))\n\t\treturn;\n\n\t  RemoveMultipleFromClassList(el, Parse(classes));\n\t}\n\tpublic static void RemoveMultipleFromClassList(this VisualElement el, IEnumerable<string> classes)\n\t{\n\t  foreach (var className in classes)\n\t\tel.RemoveFromClassList(className);\n\t}\n\n    public static IEnumerable<string> Parse(string classes)\n    {\n      return classes?.Split(new string[] { \" \" }, System.StringSplitOptions.RemoveEmptyEntries);\n\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Extensions/VisualElementExtensions.cs.meta",
    "content": "fileFormatVersion: 2\nguid: fd3bb9a5a9100d24cb94c9a528a30eb0\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Extensions.meta",
    "content": "fileFormatVersion: 2\nguid: 4abeed0c73932b946972d66da4b7ae42\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Graphene.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.Profiling;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n  using System.Collections;\n\n  [ExecuteInEditMode]\n  [RequireComponent(typeof(UIDocument))]\n  [RequireComponent(typeof(BindingsManager))]\n  [DisallowMultipleComponent]\n  public class Graphene : MonoBehaviour\n  {\n\t[SerializeField, Tooltip(\"Disable this if you want to manually initialize Graphene\")] bool initializeOnStart = true;\n\t[SerializeField, Tooltip(\"Disable this if you want to manually initialize Graphene\")] new bool runInEditMode = false;\n\t[SerializeField] string addClasses;\n\t[SerializeField] public PickingMode defaultPickingMode;\n\n\t[SerializeField] List<Plate> plates = new List<Plate>(); public IReadOnlyList<Plate> Plates => plates;\n\n\tList<IGrapheneDependent> dependents = new List<IGrapheneDependent>();\n\tpublic event System.Action<ICollection<IGrapheneDependent>> onPreInitialize;\n\tpublic event System.Action<ICollection<IGrapheneDependent>> onPostInitialize;\n\tpublic event System.Action<BindableElement, object> onBindElement;\n\n\t/// <summary>\n\t/// Root Graphene element controller\n\t/// </summary>\n\tGrapheneRoot grapheneRoot; public GrapheneRoot GrapheneRoot => grapheneRoot;\n\n\t/// <summary>\n\t/// The UI Document\n\t/// </summary>\n\t[SerializeField] UIDocument doc; public UIDocument Doc => doc;\n\t/// <summary>\n\t/// The router\n\t/// </summary>\n\t[SerializeField] Router router; public Router Router => router;\n\t[SerializeField] BindingsManager binder; public BindingsManager Binder => binder;\n\n\tpublic bool IsInitialized => grapheneRoot != null;\n\n\tpublic bool IsActiveAndInitialized => isActiveAndEnabled && IsInitialized;\n\n\tpublic bool IsActiveAndVisible => IsActiveAndInitialized && !grapheneRoot.IsHidden();\n\n\t#region Events\n\tpublic event System.Action<Plate> plateOnShow;\n\tpublic event System.Action<Plate> plateOnHide;\n\t#endregion\n\n\tprotected void Start()\n\t{\n\t  GetLocalReferences();\n\t  if (!Application.isPlaying)\n\t  {\n\t\tGetChildPlates();\n\t\treturn;\n\t  }\n\n\t  if (enabled && initializeOnStart)\n\t\tInitialize();\n\t}\n\n\t#region Attributes\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.ShowInInspector]\n#endif\n\t#endregion\n\tpublic bool Initialized { get; private set; }\n\n\tpublic bool IsValid => doc && doc.panelSettings && doc.visualTreeAsset;\n\n\t#region ButtonAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n\t#endregion\n\tpublic void Initialize()\n\t{\n\t  if (Initialized)\n\t\treturn;\n\t  GetLocalReferences();\n\n\t  if (!IsValid)\n\t  {\n\t\tUnityEngine.Debug.LogError($\"Graphene missing requirements. Please make sure UIDocument is present, has PanelSettings and a VisualTreeAsset\", this);\n\t\treturn;\n\t  }\n\n\t  RunInstallation();\n\n\t  //doc.enabled = false;\n\t  //doc.enabled = true;\n\t  Initialized = true;\n\n\t  FinalizeInitialzation();\n\t}\n\n\tprotected void GetLocalReferences()\n\t{\n\t  doc ??= GetComponent<UIDocument>();\n\t  router ??= GetComponent<Router>();\n\t  binder = GetComponent<BindingsManager>();\n\t}\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup]\n#endif\n\tpublic void GetChildPlates()\n\t{\n\t  GetComponentsInChildren<Plate>(includeInactive: true, plates);\n\t  foreach (var p in plates)\n\t\tp.Inject(this);\n\t}\n\n\tprotected void RunInstallation()\n\t{\n\t  var sw = new Stopwatch();\n\t  sw.Start();\n\n\t  // Get all dependents\n\t  GetComponentsInChildren<IGrapheneDependent>(true, dependents);\n\t  GetChildPlates();\n\n\t  // Inject Graphene into\n\t  foreach (var c in dependents)\n\t  {\n\t\tif (c is IGrapheneInjectable gc)\n\t\t  gc.Inject(this);\n\t  }\n\n\t  Profiler.BeginSample(\"Graphene Initialize\", this);\n\t  onPreInitialize?.Invoke(dependents);\n\t  // First initialize\n\t  foreach (var item in dependents)\n\t  {\n\t\tif (item is IGrapheneInitializable i)\n\t\t  i.Initialize();\n\t  }\n\t  Profiler.EndSample();\n\n\t  Profiler.BeginSample(\"Graphene Construct VisualTree\", this);\n\t  // Construct the visual tree hierarchy \n\t  ConstructVisualTree(plates);\n\t  Profiler.EndSample();\n\n\t  Profiler.BeginSample(\"Graphene Late Initialize\", this);\n\t  // Second initialize\n\t  // First initialize\n\t  foreach (var item in dependents)\n\t  {\n\t\tif (item is IGrapheneLateInitializable i)\n\t\t  i.LateInitialize();\n\t  }\n\t  onPostInitialize?.Invoke(dependents);\n\t  Profiler.EndSample();\n\t  sw.Stop();\n\n\t  //#if UNITY_EDITOR\n\t  //\t  UnityEngine.Debug.Log($\"Graphene ({gameObject.scene.name}/{gameObject.name}) initialization: {sw.ElapsedMilliseconds}ms\", this);\n\t  //#endif\n\t}\n\n\n\tvoid ConstructVisualTree(List<Plate> plates)\n\t{\n\t  var sw = new Stopwatch();\n\t  sw.Start();\n\n\t  // Create the root controller\n\t  CreateRootElement();\n\n\t  // Clone the visual tree for each plate\n\t  foreach (Plate plate in plates)\n\t  {\n\t\tif (!plate.VisualTreeAsset)\n\t\t{\n\t\t  UnityEngine.Debug.LogError($\"Missing Plate VisualTreeAsset {plate}\", plate);\n\t\t  continue;\n\t\t}\n\n\t\ttry\n\t\t{\n\t\t  plate.Dispose();\n\t\t  plate.ConstructVisualTree();\n\t\t}\n\t\tcatch (System.Exception e)\n\t\t{\n\t\t  UnityEngine.Debug.LogError(e, plate);\n\t\t}\n\t  }\n\n\t  // Refresh hierarchy -> render & compose children\n\t  foreach (Plate plate in plates)\n\t  {\n\t\tif (!plate.VisualTreeAsset)\n\t\t  continue;\n\n\t\tRegisterPlate(plate);\n\t  }\n\n\t  sw.Stop();\n\t  //UnityEngine.Debug.Log($\"Graphene ConstructVisualTree: {sw.ElapsedMilliseconds}ms\");\n\t}\n\n\tpublic void AddPlate(Plate plate)\n\t{\n\t  if (!plates.Contains(plate))\n\t\tplates.Add(plate);\n\t}\n\n\tpublic void RegisterPlate(Plate plate)\n\t{\n\t  if (plate.IsRootPlate)\n\t  {\n\t\tgrapheneRoot.Add(plate.Root);\n\t\tplate.Root.AddToClassList(\"unity-ui-document__child\");\n\t  }\n\n\t  plate.Root.name = $\"{plate.gameObject.name}-container\";\n\t  plate.RenderAndComposeChildren();\n\t  plate.HideImmediately(); // Hide immediately by default\n\n\t  plate.onShow.AddListener(() => { plateOnShow?.Invoke(plate); });\n\t  plate.onHide.AddListener(() => { plateOnHide?.Invoke(plate); });\n\n\t  // Enable on start\n\t  //if(plate.gameObject.activeSelf)\n\t  plate.ReevaluateState();\n\t}\n\n\t#region Build VisualElement\n\n\tvoid CreateRootElement()\n\t{\n\t  // Create the root controller\n\t  grapheneRoot = new GrapheneRoot(router);\n\t  grapheneRoot.AddMultipleToClassList(addClasses);\n\t  grapheneRoot.pickingMode = defaultPickingMode;\n\n#if UNITY_EDITOR\n\t  grapheneRoot.RegisterCallback<DetachFromPanelEvent>(DetachFromPanel);\n#endif\n\n\t  doc.rootVisualElement.Add(grapheneRoot);\n\t}\n\n#if UNITY_EDITOR\n\tIEnumerator coroutine;\n\tvoid DetachFromPanel(DetachFromPanelEvent evt)\n\t{\n\t  if (!Application.isPlaying || !gameObject.activeInHierarchy || !doc.enabled)\n\t\treturn;\n\t  coroutine = RebuildDelayed(0.1f);\n\t  StartCoroutine(coroutine);\n\t}\n\n\tIEnumerator RebuildDelayed(float delay)\n\t{\n\t  yield return new WaitForSeconds(delay);\n\t  grapheneRoot.UnregisterCallback<DetachFromPanelEvent>(DetachFromPanel);\n\t  doc.rootVisualElement.Add(grapheneRoot);\n\t  grapheneRoot.RegisterCallback<DetachFromPanelEvent>(DetachFromPanel);\n\t  yield break;\n\t  //Rebuild();\n\t}\n#endif\n\n\tvoid RebuildRootElement()\n\t{\n\t  var oldRoot = grapheneRoot;\n\n\t  CreateRootElement();\n\n\t  if (oldRoot != null)\n\t  {\n\t\t// Add root plates\n\t\tif (oldRoot.childCount > 0)\n\t\t{\n\t\t  var children = oldRoot.Children().ToList();\n\t\t  foreach (var child in children)\n\t\t\tgrapheneRoot.Add(child);\n\t\t}\n\n\t\tdoc.rootVisualElement.Remove(oldRoot);\n\t  }\n\t}\n\t#endregion\n\n\t#region BuildHierarchy\n\t#endregion\n\n\tvoid FinalizeInitialzation()\n\t{\n\t  /// Needs to go here because UIDocuments may initialize late\n\t  grapheneRoot.BringToFront();\n\t}\n\n\tpublic void RebuildBranch(Plate plate)\n\t{\n\n\t}\n\n\t// Needs to be in because UIDocument destroys the root\n\tprivate void OnEnable()\n\t{\n#if UNITY_EDITOR\n\t  if (UnityEditor.EditorApplication.isCompiling || UnityEditor.BuildPipeline.isBuildingPlayer)\n\t\treturn;\n#endif\n\n\t  if (grapheneRoot != null && initializeOnStart)\n\t\t// Live reload\n\t\tRebuild();\n\t  return;\n\t}\n\n\n\tprivate void OnDisable()\n\t{\n#if UNITY_EDITOR\n\t  if (UnityEditor.EditorApplication.isCompiling || UnityEditor.BuildPipeline.isBuildingPlayer)\n\t\treturn;\n#endif\n\t}\n\n\tbool canRebuild => Initialized && doc.enabled && isActiveAndEnabled;\n\n\tpublic event System.Action onRebuild;\n\n\tint rebuildCount;\n\n\t#region ButtonAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n\t#endregion\n\tpublic void Rebuild()\n\t{\n\t  if (!Application.isPlaying && !runInEditMode)\n\t\treturn;\n\n\t  if (!Initialized)\n\t  {\n\t\tInitialize();\n\t\treturn;\n\t  }\n\n\t  if (!canRebuild)\n\t\treturn;\n\n//#if UNITY_EDITOR\n//\t\tUnityEngine.Debug.Log($\"Rebuilding Graphene {name} {gameObject.scene.name}\", this);\n//#endif\n\n\t  if (grapheneRoot != null)\n\t  {\n\t\tgrapheneRoot.parent?.Remove(grapheneRoot);\n\t\tgrapheneRoot.Clear();\n\t\tgrapheneRoot = null;\n\t  }\n\n\t  ConstructVisualTree(plates);\n\t  FinalizeInitialzation();\n\t  onRebuild?.Invoke();\n\t}\n\n\tpublic void RefreshAllActiveContent()\n\t{\n\t  foreach (var p in plates)\n\t  {\n\t\tif (!p.IsActive)\n\t\t  continue;\n\t\tp.Renderer?.Refresh();\n\t  }\n\t}\n\n\t//private void OnValidate()\n\t//{\n\n\t//  LiveLink\n\t//  if (Application.isPlaying || doc.rootVisualElement == null)\n\t//    return;\n\n\t//  RebuildRootElement();\n\t//}\n\n\tpublic void BroadcastBindCallback(BindableElement el, object context, Plate plate) => onBindElement?.Invoke(el, context);\n  }\n}"
  },
  {
    "path": "src/Core/Graphene.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 3df2f2c9411e2af419b0ebc039577ce4\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: a7567e94e4334dd4bb5bed2d09495114, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/GrapheneComponent.cs",
    "content": "using UnityEngine;\n\nnamespace Graphene\n{\n    /// <summary>\n    /// Base class for all components that participate in the Graphene dependency injection system.\n    /// Provides access to the injected <see cref=\"Graphene\"/> instance and its <see cref=\"BindingsManager\"/>.\n    /// </summary>\n    public class GrapheneComponent : MonoBehaviour, IGrapheneInjectable\n    {\n        /// <summary>\n        /// Cached debug name for this component, used for identification and logging.\n        /// </summary>\n        internal string debugNameCached;\n\n        /// <summary>\n        /// Gets the cached debug name for this component.\n        /// </summary>\n        public string DebugName => debugNameCached;\n\n#if ODIN_INSPECTOR\n        [Sirenix.OdinInspector.HideIf(nameof(graphene))]\n#endif\n        /// <summary>\n        /// The injected Graphene instance that manages bindings and dependencies for this component.\n        /// </summary>\n        [SerializeField] protected Graphene graphene;\n\n        /// <summary>\n        /// Gets the injected <see cref=\"Graphene\"/> instance.\n        /// </summary>\n        public Graphene Graphene => graphene;\n\n        /// <summary>\n        /// Gets the <see cref=\"BindingsManager\"/> from the injected <see cref=\"Graphene\"/> instance.\n        /// </summary>\n        public BindingsManager BindingsManager => graphene?.Binder;\n\n        /// <summary>\n        /// Injects the specified <see cref=\"Graphene\"/> instance into this component.\n        /// </summary>\n        /// <param name=\"graphene\">The Graphene instance to inject.</param>\n        public virtual void Inject(Graphene graphene)\n        {\n            this.graphene = graphene;\n\n            if (string.IsNullOrEmpty(debugNameCached))\n                debugNameCached = name;\n        }\n\n        /// <summary>\n        /// Unity Awake callback. Ensures the debug name is initialized.\n        /// </summary>\n        protected virtual void Awake()\n        {\n            if (string.IsNullOrEmpty(debugNameCached))\n                debugNameCached = name;\n        }\n    }\n}"
  },
  {
    "path": "src/Core/GrapheneComponent.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d32a65210f6ccde4ba4983f9164de0b3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Injector.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  [DisallowMultipleComponent]\n  public class Injector : MonoBehaviour\n  {\n    [SerializeField] Graphene graphene; \n    private void Awake()\n    {\n      if (graphene || (graphene = GetComponent<Graphene>()))\n        graphene.onPreInitialize += Graphene_onPreInitialize;\n    }\n\n    private void Graphene_onPreInitialize(ICollection<IGrapheneDependent> dependents)\n    {\n      foreach (var dependent in dependents)\n      {\n        if (dependent is Router router)\n          router.InjectIntoHierarchy();\n      }\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Injector.cs.meta",
    "content": "fileFormatVersion: 2\nguid: d8e759c0d6277b6448cce98e37e22305\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: d249967811185734b8114d4b8977f433, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Interfaces.cs",
    "content": "﻿namespace Graphene\n{\n  using Elements;\n  using UnityEngine.UIElements;\n\n  public interface IGrapheneDependent\n  {\n  }\n\n  public interface IBindableToVisualElement : IHasTooltip\n  {\n    bool isShown { get; }\n    System.Action<bool> onShowHide { get; set; }\n\n    bool isEnabled { get; }\n    System.Action<bool> onSetEnabled { get; set; }\n\n\tbool isActive2 { get; }\n\tSystem.Action<bool> onSetActive { get; set; }\n\n    //event System.Action<VisualElement> onBindToElement;// { get; set; }\n\n\tVisualElement boundToElement { get; }\n\n\tvoid ResetCallbacks ();\n\n    void SetBinding(VisualElement boundToElement);\n\n    //System.Action syncVisualElement { get; set; }\n  }\n\n  public interface IBindableElement<TValue>\n  {\n    /// <summary>\n    /// Callback to VisualElement control that model changed, and view needs to be updated\n    /// </summary>\n    /// <param name=\"newValue\"></param>\n    void OnModelChange(TValue newValue);\n  }\n\n  public enum InteractionMode\n  {\n    Button,\n    Submit,\n    Cancel\n  }\n\n  public interface IBindableInteractionType\n  {\n    public InteractionMode InteractionType { get; }\n    public int Size { get; }\n  }\n\n  public interface IGrapheneElement\n  {\n    void Inject(GrapheneRoot root, Plate plate, Renderer renderer);\n  }\n\n#if !DEPENDENCY_INJEcTION\n  public interface IGrapheneInitializable : IGrapheneDependent\n  {\n    void Initialize();\n  }\n  public interface IGrapheneLateInitializable : IGrapheneDependent\n  {\n    void LateInitialize();\n  }\n\n  public interface IGrapheneInjectable : IGrapheneDependent\n  {\n    void Inject(Graphene graphene);\n  }\n#endif\n\n  #region IModel\n\n  public interface IModel\n  {\n\tvoid Initialize(VisualElement container, Plate plate);\n\tbool Render { get; }\n\n\tSystem.Action onModelChange { get; set; }\n\n\tvoid Refresh(VisualElement container);\n  }\n\n  /// <summary>\n  /// Instructs the <see cref=\"Renderer\"/> to use a custom <see cref=\"BindAttribute\"/> context\n  /// </summary>\n  public interface ICustomBindContext\n  {\n\tpublic object GetCustomBindContext { get; }\n  }\n\n  /// <summary>\n  /// Instructs the <see cref=\"Renderer\"/> to use a custom <see cref=\"DrawAttribute\"/> context\n  /// </summary>\n  public interface ICustomDrawContext\n  {\n\tpublic object GetCustomDrawContext { get; }\n  }\n\n  public interface IForm\n  {\n\t[Bind(\"Title\")]\n\tstring Title { get; }\n\tvoid OnSubmit();\n\tvoid OnCancel();\n  }\n  #endregion\n\n  #region Misc\n\n  public interface IHasTooltip\n  {\n\tstring Tooltip { get; }\n  }\n\n  public interface IHasCustomVisualTreeAsset\n  {\n\tVisualTreeAsset VisualTreeAsset { get; }\n  }\n\n  #endregion\n\n}"
  },
  {
    "path": "src/Core/Interfaces.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 59b985a9f1ab787479e438f05a3b2567\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/BindableObjects/BindableBaseField.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.Serialization;\nusing System.Text;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\nusing Sirenix.OdinInspector;\n#endif\n\nnamespace Graphene.ViewModel\n{\n  [System.Serializable]\n  [DataContract]\n  public abstract class BindableBaseField : BindableObjectBase\n  {\n\t[field: SerializeField]\n\t[Bind(nameof(Label), BindingMode.OneWay)]\n\t[IgnoreDataMember]\n\tpublic virtual string Label { get; set; }\n  }\n\n  [System.Serializable]\n  [DataContract]\n  public abstract class BindableBaseField<T> : BindableBaseField, INotifyValueChanged<T>, INotifyPropertyChanged\n  {\n\t[SerializeField]\n\tprotected T m_Value;\n\n\tpublic BindableBaseField() { }\n\n\tpublic BindableBaseField([CallerMemberName] string label = \"Label\") : this()\n\t{\n\t  this.Label = label;\n\t}\n\n\t[BindBaseField(\"Value\")]\n\t[DataMember(Name = \"Value\")]\n\tpublic virtual T value\n\t{\n\t  get => m_Value; set\n\t  {\n\t\tSetValueWithoutNotify(value);\n\t\tValueChangeCallback(m_Value);\n\t  }\n\t}\n\n\n\t/*[BindValueChangeCallback(nameof(ValueChange))]*/\n\t[IgnoreDataMember]\n\tpublic EventCallback<ChangeEvent<T>> ValueChange => (changeEvent) => { ValueChangeCallback(changeEvent.newValue); };\n\n\tpublic event System.EventHandler<T> OnValueChange;\n\tpublic event PropertyChangedEventHandler PropertyChanged;\n\n\tpublic virtual void SetValueWithoutNotify(T newValue)\n\t{\n\t  m_Value = newValue;\n\t}\n\n#if UNITY_EDITOR\n\tPropertyChangedEventArgs propertyChangedArgs;\n#endif\n\n\tprotected virtual void ValueChangeCallback(T value)\n\t{\n\t  OnValueChange?.Invoke(this, value);\n#if UNITY_EDITOR\n\t  if (propertyChangedArgs == null)\n\t\tpropertyChangedArgs = new PropertyChangedEventArgs(Label);\n\t  PropertyChanged?.Invoke(this, propertyChangedArgs);\n#else\n\t  PropertyChanged?.Invoke(this, null);\n#endif\n\t}\n\n\tpublic override void ResetCallbacks()\n\t{\n\t  base.ResetCallbacks();\n\t  OnValueChange = null;\n\t  PropertyChanged = null;\n\t}\n  }\n\n\n\n  [System.Serializable, Draw(ControlType.Toggle), DataContract]\n  public class BindableBool : BindableBaseField<bool>\n  {\n\tpublic BindableBool(string name = null) : base(name) { }\n\tpublic BindableBool() : base(null) \t{}\n  }\n\n\n\n  [System.Serializable, Draw(ControlType.Slider), DataContract]\n  public abstract class RangeBaseField<TValueType> : BindableBaseField<TValueType>\n  {\n\tpublic abstract float normalizedValue { get; }\n\n\t// These come first, so the ranges are deserialized _before_ the value\n#if ODIN_INSPECTOR\n\t[PropertyOrder(1)]\n#endif\n\t[Bind(\"Min\"), DataMember(Name = \"Min\")]\n\tpublic TValueType min;\n#if ODIN_INSPECTOR\n\t[PropertyOrder(1)]\n#endif\n\t[Bind(\"Max\"), DataMember(Name = \"Max\")]\n\tpublic TValueType max;\n\n\t[BindBaseField(\"Value\")]\n\t[DataMember(Name = \"Value\")]\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector, Sirenix.OdinInspector.PropertyRange(0, 1, MinGetter = nameof(min), MaxGetter = nameof(max))]\n#endif\n\tpublic override TValueType value { get => base.value; set => base.value = value; }\n\n\tpublic TValueType Min { get => min; set => min = value; }\n\tpublic TValueType Max { get => max; set => max = value; }\n\n\tpublic RangeBaseField() : base() { }\n\n\tprotected float Normalize(float value, float min, float max)\n\t{\n\t  return Mathf.InverseLerp(min, max, value);\n\t}\n  }\n\n  public interface INumericBindable\n  {\n\tfloat normalizedValue { get; }\n\tfloat value { get; set; }\n\n\tfloat Min { get; set; }\n\tfloat Max { get; set; }\n\n\tvoid SetValueWithoutNotify(float value);\n  }\n\n  [System.Serializable, Draw(ControlType.Slider), DataContract]\n  public class BindableFloat : RangeBaseField<float>, INumericBindable\n  {\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector, Sirenix.OdinInspector.PropertyRange(0, 1)]\n#endif\n\tpublic override float normalizedValue => Normalize(m_Value, min, max);\n\n\tpublic BindableFloat() : base()\n\t{\n\t  min = 0;\n\t  max = 1;\n\t  m_Value = 0.5f;\n\t}\n\n\tpublic override void SetValueWithoutNotify(float newValue)\n\t{\n\t  m_Value = Mathf.Clamp(newValue, min, max);\n\t}\n  }\n\n  [System.Serializable, Draw(ControlType.SliderInt), DataContract]\n  public class BindableInt : RangeBaseField<int>, INumericBindable\n  {\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector, Sirenix.OdinInspector.PropertyRange(0, 1)]\n#endif\n\tpublic override float normalizedValue => Normalize(m_Value, min, max);\n\n\tfloat INumericBindable.value { get => value; set => base.value = (int)value; }\n\tfloat INumericBindable.Min { get => min; set => min = (int)value; }\n\tfloat INumericBindable.Max { get => max; set => max = (int)value; }\n\n\tpublic BindableInt() : base()\n\t{\n\t  min = 0;\n\t  max = 100;\n\t  m_Value = 50;\n\t  //m_Value = 5;\n\t}\n\n\tpublic override void SetValueWithoutNotify(int newValue)\n\t{\n\t  m_Value = Mathf.Clamp(newValue, min, max);\n\t}\n\n\tvoid INumericBindable.SetValueWithoutNotify(float value) => SetValueWithoutNotify((int)value);\n  }\n\n  [System.Serializable, Draw(ControlType.CycleField), DataContract]\n  public class BindableNamedInt : BindableBaseField<int>\n  {\n\t[field: SerializeField]\n\t[Bind(\"Items\"), IgnoreDataMember]\n\tpublic List<string> items { get; set; } = new List<string>();\n\n\tpublic float normalizedValue => (float)m_Value / items.Count;\n\n\tpublic void InitFromEnum<T>(bool splitUppercase = true)\n\t{\n\t  this.items.Clear();\n\n\t  foreach (string s in System.Enum.GetNames(typeof(T)).ToList())\n\t  {\n\t\tstring item = s;\n\t\tif (splitUppercase)\n\t\t  item = StringUtility.InsertSpaceBeforeUpperCase(s);\n\t\titem = item.Replace(\"_\", \" \").Trim();\n\n\t\tthis.items.Add(item);\n\t  }\n\t}\n\tpublic void InitFromList(IEnumerable<string> list)\n\t{\n\t  this.items.Clear();\n\t  this.items = list.ToList();\n\t}\n\n\tpublic BindableNamedInt() : base() { }\n\n  }\n\n  [System.Serializable, Draw(ControlType.DropdownField), DataContract]\n  public class BindableStringSelect : BindableBaseField<string>\n  {\n\t[field: SerializeField]\n\t[Bind(\"Items\"), IgnoreDataMember]\n\tpublic List<string> items { get; set; } = new List<string>();\n\n\tpublic float normalizedValue => (float)Index / items.Count;\n\n\tpublic int Index => items.IndexOf(value);\n\n\tpublic void InitFromEnum<T>(bool splitUppercase = true)\n\t{\n\t  this.items.Clear();\n\n\t  foreach (string s in System.Enum.GetNames(typeof(T)).ToList())\n\t  {\n\t\tstring item = s;\n\t\tif (splitUppercase)\n\t\t  item = StringUtility.InsertSpaceBeforeUpperCase(s);\n\n\t\tthis.items.Add(item);\n\t  }\n\t}\n\tpublic void InitFromList(IEnumerable<string> list)\n\t{\n\t  this.items.Clear();\n\t  this.items = list.ToList();\n\t}\n\n\tpublic BindableStringSelect() : base() { }\n  }\n\n\n  [System.Serializable, Draw(ControlType.TextField), DataContract]\n  public class BindableInput : BindableBaseField<string>\n  {\n\tpublic BindableInput() : base() { }\n  }\n\n\n  [System.Serializable, Draw(ControlType.MinMaxSlider), DataContract]\n  public class BindableRange : BindableBaseField<Vector2>\n  {\n\t[BindBaseField(\"Value\")]\n\t[DataMember(Name = \"Value\")]\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector, Sirenix.OdinInspector.MinMaxSlider(0, 1, MinValueGetter = nameof(min), MaxValueGetter = nameof(max))]\n#endif\n\tpublic override Vector2 value { get => base.value; set => base.value = value; }\n\n\t[Bind(\"Min\"), DataMember(Name = \"Min\")]\n\tpublic float min;\n\t[Bind(\"Max\"), DataMember(Name = \"Max\")]\n\tpublic float max;\n\n\tpublic float Min { get => min; set => min = value; }\n\tpublic float Max { get => max; set => max = value; }\n\n\tpublic BindableRange() : base()\n\t{\n\t  min = 0;\n\t  max = 1;\n\t  m_Value = new Vector2(0, 1);\n\t}\n\n\tprotected float Normalize(float value, float min, float max)\n\t{\n\t  return Mathf.InverseLerp(min, max, value);\n\t}\n  }\n\n  public static class StringUtility\n  {\n\tpublic static string InsertSpaceBeforeUpperCase(this string str)\n\t{\n\t  var sb = new StringBuilder();\n\n\t  char previousChar = char.MinValue; // Unicode '\\0'\n\n\t  foreach (char c in str)\n\t  {\n\t\tif (char.IsUpper(c))\n\t\t{\n\t\t  // If not the first character and previous character is not a space, insert a space before uppercase\n\n\t\t  if (sb.Length != 0 && previousChar != ' ')\n\t\t  {\n\t\t\tsb.Append(' ');\n\t\t  }\n\t\t}\n\n\t\tsb.Append(c);\n\n\t\tpreviousChar = c;\n\t  }\n\n\t  return sb.ToString();\n\t}\n  }\n}\n"
  },
  {
    "path": "src/Core/Model/BindableObjects/BindableBaseField.cs.meta",
    "content": "fileFormatVersion: 2\nguid: b7a28165c3f71c34e9b0eb23b27cb50d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/BindableObjects/BindableObject.cs",
    "content": "\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\nusing UnityEngine;\nusing UnityEngine.Events;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\nusing Sirenix.OdinInspector;\n#endif\n\nnamespace Graphene.ViewModel\n{\n  /// <summary>\n  /// BindableObjects are the atomic data units that can be bound to VisualElements. They are used to author data in the inspector, or can be generated at runtime to decouple data from view logic.\n  /// </summary>\n  /// <example>Common use cases include buttons, labels, toggles, sliders etc.</example>\n  [System.Serializable]\n#if ODIN_INSPECTOR\n  [Toggle(\"<isEnabled>k__BackingField\")]\n#endif\n  public abstract class BindableObjectBase : IBindableToVisualElement\n  {\n#if ODIN_INSPECTOR\n\t[field: LabelText(SdfIconType.TypeStrikethrough)]\n#endif\n\t[field: SerializeField, IgnoreDataMember, OnValueChanged(nameof(SetEnabled))] public bool isEnabled { get; set; } = true;\n\tpublic Action<bool> onSetEnabled { get; set; }\n\n#if ODIN_INSPECTOR\n\t[field: LabelText(SdfIconType.Eye), OnValueChanged(nameof(SetShow))]\n#endif\n\t[field: SerializeField, IgnoreDataMember] public bool isShown { get; set; } = true;\n\tpublic Action<bool> onShowHide { get; set; }\n\n#if ODIN_INSPECTOR\n\t[field: LabelText(SdfIconType.Check), OnValueChanged(nameof(SetActive))]\n#endif\n\t[field: SerializeField, IgnoreDataMember] public bool isActive2 { get; set; } = false;\n\tpublic Action<bool> onSetActive { get; set; }\n\n\tpublic VisualElement boundToElement { get; set; }\n\tpublic System.Action<VisualElement> onBindToElement;// { get; set; }\n\n\tpublic void SetBinding(VisualElement el)\n\t{\n\t  boundToElement = el;\n\t  onBindToElement?.Invoke(el);\n\t}\n\n\t[field: SerializeField] public string Tooltip { get; set; }\n\n\tvoid ToggleEnable() => SetEnabled(!isEnabled);\n\tvoid ToggleShow() => SetShow(!isShown);\n\tvoid ToggleActive() => SetActive(!isActive2);\n\n\tpublic void SetEnabled(bool enabled)\n\t{\n\t  isEnabled = enabled;\n\t  onSetEnabled?.Invoke(enabled);\n\t}\n\n\tpublic void SetShow(bool show)\n\t{\n\t  isShown = show;\n\t  onShowHide?.Invoke(show);\n\t}\n\tpublic void SetActive(bool active)\n\t{\n\t  isActive2 = active;\n\t  onSetActive?.Invoke(active);\n\t}\n\n\tpublic virtual void ResetCallbacks()\n\t{\n\t  onSetEnabled = null;\n\t  onShowHide = null;\n\t}\n  }\n\n  /// <summary>\n  /// BindableObjects are the atomic data units that can be bound to VisualElements. They are used to author data in the inspector, or can be generated at runtime to decouple data from view logic.\n  /// </summary>\n  /// <example>Common use cases include buttons, labels, toggles, sliders etc.</example>\n  // Atomic \"Über\" object for the view\n  [System.Serializable, Draw(ControlType.Button)]\n  public class BindableObject : BindableObjectBase, IRoute, ICustomControlType, ICustomAddClasses, ICustomName, IHasCustomVisualTreeAsset\n  {\n\t[field: SerializeField]\n\tpublic ControlType ControlType { get; set; }\n\n\t[field: SerializeField]\n\tpublic VisualTreeAsset VisualTreeAsset { get; set; }\n\n\t[field: SerializeField]\n\t[Bind(\"Label\", BindingMode.OneWay)]\n\tpublic string Name { get; set; }\n\n\t[field: SerializeField]\n\t[Bind(\"Value\")]\n\tpublic string Value { get; set; }\n\n\t[field: SerializeField]\n\t[Route]\n\tpublic string route;\n\n\t[field: SerializeField]\n\t[BindTooltip(\"Description\")]\n\tpublic string Description { get; set; }\n\n\t[field: SerializeField]\n\t[Bind(\"Image\")]\n\tpublic Texture Image { get; set; }\n\n\t#region FoldoutAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.FoldoutGroup(\"Additionals\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Foldout(\"Additionals\")]\n#endif\n\t#endregion\n\tpublic string addClass; public string ClassesToAdd => addClass;\n\n\t#region FoldoutAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.FoldoutGroup(\"Additionals\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Foldout(\"Additionals\")]\n#endif\n\t#endregion\n\tpublic string customName; public string CustomName => customName;\n\n\t#region FoldoutAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.FoldoutGroup(\"Additionals\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Foldout(\"Additionals\")]\n#endif\n\t#endregion\n\t[Bind(\"\")]\n\tpublic UnityEvent OnClick = new UnityEvent();\n\n\t#region Util\n\tpublic override string ToString()\n\t{\n\t  return $\"{this.GetType().Name} - [{this.Name}]\";\n\t}\n\t#endregion\n\n\tpublic BindableObject()\n\t{\n\t}\n\n\tpublic BindableObject(UnityAction callback) : this()\n\t{\n\t  OnClick.AddListener(callback);\n\t}\n  }\n\n  // Atomic \"Über\" object for the view\n  [System.Serializable, Draw(ControlType.Button)]\n  public class ContextBindableObject : BindableObject, IBindableInteractionType\n  {\n\t[Bind(\"Content\")]\n\t[field: SerializeField] public List<ContextBindableObject> Content { get; private set; }\n\n\t[Bind(\"HasContent\")][field: SerializeField] public bool HasContent { get; private set; }\n\n\t[Bind(\"Actions\")]\n\t[field: SerializeField]\n\tpublic List<ActionButton> Actions { get; set; }\n\n\t[Bind(\"HasActions\")]\n\tpublic bool HasActions => Actions != null && Actions.Count > 0;\n\n\n\t[field: SerializeField]\n\tpublic InteractionMode InteractionType { get; set; }\n\n\t[field: SerializeField]\n\tpublic int Size { get; set; }\n\n\tpublic void AddAction(string name, Action callback, string tooltip = null)\n\t{\n\t  if (Actions == null)\n\t\tActions = new List<ActionButton>();\n\t  var action = new ActionButton\n\t  {\n\t\tLabel = name,\n\t\tTooltip = tooltip,\n\t\tOnClick = callback\n\t  };\n\t  Actions.Add(action);\n\t}\n  }\n\n  [Draw(ControlType.Button)]\n  [System.Serializable]\n  public class ActionButton : IHasTooltip\n  {\n\t[field: SerializeField]\n\t[Bind(\"Label\")]\n\tpublic string Label { get; set; }\n\n\t[field: SerializeField]\n\t[Bind]\n\tpublic System.Action OnClick { get; set; }\n\n\t[field: SerializeField]\tpublic string Tooltip { get; set; }\n\n\t[Bind(\"Enabled\")]\n\tpublic bool Enabled => OnClick != null;\n  }\n}\n"
  },
  {
    "path": "src/Core/Model/BindableObjects/BindableObject.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 2a481b0795ebfad4c9771a06eb5677c0\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/BindableObjects/ListBindable.cs",
    "content": "﻿using Sirenix.OdinInspector;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  public interface IListViewBindable\n  {\n\tIList ItemsSource { get; }\n\tControlType ItemControlType { get; }\n\tCollectionVirtualizationMethod CollectionVirtualizationMethod { get; }\n\tSelectionType SelectionType { get; }\n\tbool ShowBorder { get; }\n\tstring HeaderTitle { get; }\n\tbool ShowFoldoutHeader { get; }\n\tbool ShowAddRemoveFooter { get; }\n\tAlternatingRowBackground AlternatingRowBackground { get; }\n\tbool Reorderable { get; }\n\tListViewReorderMode ReorderMode { get; }\n\tbool ShowCollectionSize { get; }\n\n\tvoid Apply(ListView el);\n\n\tbool ElementsPickable { get; }\n  }\n\n  public abstract class ListBindable : BindableObjectBase\n  {\n\t[SerializeField] private ControlType controlType = ControlType.ListItem; public ControlType ItemControlType => controlType;\n\tpublic CollectionVirtualizationMethod collectionVirtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; public CollectionVirtualizationMethod CollectionVirtualizationMethod => collectionVirtualizationMethod;\n\n\t[Range(0, 100)] public int height = 30;\n\tpublic SelectionType selectionType = SelectionType.Single; public SelectionType SelectionType => selectionType;\n\tpublic bool showBorder; public bool ShowBorder => showBorder;\n\tpublic string headerTitle; public string HeaderTitle => headerTitle;\n\tpublic bool showFoldoutHeader; public bool ShowFoldoutHeader => showFoldoutHeader;\n\tpublic bool showAddRemoveFooter; public bool ShowAddRemoveFooter => showAddRemoveFooter;\n\tpublic AlternatingRowBackground alternatingRowBackground; public AlternatingRowBackground AlternatingRowBackground => alternatingRowBackground;\n\tpublic bool reorderable; public bool Reorderable => reorderable;\n\tpublic ListViewReorderMode reorderMode; public ListViewReorderMode ReorderMode => reorderMode;\n\tpublic bool showCollectionSize; public bool ShowCollectionSize => showCollectionSize;\n\tpublic bool elementsPickable = true; public bool ElementsPickable => elementsPickable;\n\n\t// Bound element\n\tpublic ListView listView;\n\n\t[ResponsiveButtonGroup]\n\tpublic void Rebuild()\n\t{\n\t  if (listView == null)\n\t\treturn;\n\n\t  Apply(listView);\n\t  listView.Rebuild();\n\t}\n\n\tpublic void Apply(ListView el)\n\t{\n\t  this.listView = el;\n\t  el.virtualizationMethod = collectionVirtualizationMethod;\n\t  el.fixedItemHeight = this.height;\n\t  el.selectionType = selectionType;\n\t  el.showBorder = showBorder;\n\t  el.headerTitle = headerTitle;\n\t  el.showFoldoutHeader = showFoldoutHeader;\n\t  el.showAddRemoveFooter = showAddRemoveFooter;\n\t  el.showAlternatingRowBackgrounds = alternatingRowBackground;\n\t  el.reorderable = reorderable;\n\t  el.reorderMode = reorderMode;\n\t  el.showBoundCollectionSize = showCollectionSize;\n\t}\n  }\n\n  [System.Serializable]\n  [Draw(controlType = ControlType.ListView)]\n  public class ListBindable<TObjectType> : ListBindable, IListViewBindable//, IList<TObjectType>\n  {\n\t[Bind(\"Items\")] public List<TObjectType> SourceItems = new List<TObjectType>();\n\n\tpublic IList ItemsSource => SourceItems;\n\n\t#region IList<T>\n\tpublic TObjectType this[int index] { get => SourceItems[index]; set => SourceItems[index] = value; }\n\n\tpublic int Count => SourceItems.Count;\n\n\tpublic bool IsReadOnly => false;\n\n\tpublic void Add(TObjectType item) => SourceItems.Add(item);\n\n\tpublic void Clear() => SourceItems.Clear();\n\n\tpublic bool Contains(TObjectType item) => SourceItems.Contains(item);\n\n\tpublic void CopyTo(TObjectType[] array, int arrayIndex) => SourceItems.CopyTo(array, arrayIndex);\n\n\tpublic IEnumerator<TObjectType> GetEnumerator() => SourceItems.GetEnumerator();\n\n\tpublic int IndexOf(TObjectType item) => SourceItems.IndexOf(item);\n\n\tpublic void Insert(int index, TObjectType item) => SourceItems.Insert(index, item);\n\n\tpublic bool Remove(TObjectType item) => SourceItems.Remove(item);\n\n\tpublic void RemoveAt(int index) => SourceItems.RemoveAt(index);\n\n\t//IEnumerator IEnumerable.GetEnumerator() => SourceItems.GetEnumerator();\n\t#endregion\n  }\n}\n"
  },
  {
    "path": "src/Core/Model/BindableObjects/ListBindable.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 90b16a7dd3d8c654693abccb70045774\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/BindableObjects.meta",
    "content": "fileFormatVersion: 2\nguid: 21f4f86d9ed39a04dae5f8a10dc50541\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/GenericModelAsset.cs",
    "content": "﻿using System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  public abstract class ScriptableObjectModel<T> : ScriptableObjectModel\n  {\n    [Draw(ControlType.Button)]\n    public List<T> model = new List<T>();\n  }\n\n  /// <summary>\n  /// A generic model asset that can be used to author a list of BindableObject.\n  /// </summary>\n  [CreateAssetMenu(menuName = \"Graphene/Model/GenericModelAsset\")]\n  public class GenericModelAsset : ScriptableObjectModel<BindableObject>\n  {\n    public override void Initialize(VisualElement container, Plate plate)\n    {\n    }\n  }\n}\n"
  },
  {
    "path": "src/Core/Model/GenericModelAsset.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 3c77b204580fbe947b11e389b5321ed7\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 806f5574c9706cd4e812686596744160, type: 3}\n  userData:\n  assetBundleName:\n  assetBundleVariant:\n"
  },
  {
    "path": "src/Core/Model/GenericModelBehaviour.cs",
    "content": "﻿using System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  public abstract class GenericModelBehaviour<T> : ViewModelComponent\n  {\n\tpublic override bool HasContent => items?.Count > 0; \n\n\t[Draw(ControlType.Button)]\n    [SerializeField]\n    protected List<T> items = new List<T>();\n\n    public abstract List<T> Items { get; }\n  }\n\n  public class GenericModelBehaviour : GenericModelBehaviour<BindableObject>\n  {\n\t[SerializeField] bool validateRouteAdresses = true;\n\t[SerializeField] bool hideInvalidAdress;\n\tprotected StringRouter router;\n\n\tpublic override List<BindableObject> Items => items;\n\n\tpublic override void Inject(Graphene graphene)\n\t{\n\t  base.Inject(graphene);\n\t  if (graphene.Router is StringRouter stringRouter)\n\t\trouter = stringRouter;\n\t}\n\n\tprotected override void OnShow()\n\t{\n\t  base.OnShow();\n\t  ValidateAddresses();\n\t}\n\n\tvoid ValidateAddresses()\n\t{\n\t  if (router && validateRouteAdresses)\n\t  {\n\t\tforeach (var item in items)\n\t\t{\n\t\t  if (!string.IsNullOrEmpty(item.route))\n\t\t  {\n\t\t\tbool exists = router.ValidateAddress(item.route);\n\t\t\titem.SetEnabled(exists);\t\t\t\n\t\t\t//item.isEnabled = exists;\n\t\t\tif (hideInvalidAdress)\n\t\t\t  item.SetShow(exists);\n\t\t  }\n\t\t}\n\t  }\n\t}\n\n\t//public override void Refresh(VisualElement container)\n\t//{\n\t//  if (router && validateRouteAdresses)\n\t//  {\n\t//\tforeach (var item in items)\n\t//\t{\n\t//\t  if (!string.IsNullOrEmpty(item.route))\n\t//\t  {\n\t//\t\tbool exists = router.ValidateAddress(item.route);\n\t//\t\titem.isEnabled = exists;\n\t//\t\tif (hideInvalidAdress)\n\t//\t\t  item.isShown = exists;\n\t//\t  }\n\t//\t}\n\t//  }\n\n\t//  base.Refresh(container);\n\t//}\n\n\tpublic override void Initialize(VisualElement container, Plate plate)\n    {\n\t  ValidateAddresses();\n    }\n  }\n}\n"
  },
  {
    "path": "src/Core/Model/GenericModelBehaviour.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 35cb04044c5eb594495aaeae5ccb11a2\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 806f5574c9706cd4e812686596744160, type: 3}\n  userData:\n  assetBundleName:\n  assetBundleVariant:\n"
  },
  {
    "path": "src/Core/Model/ScriptableObjectModel.cs",
    "content": "﻿\nusing System;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// An IModel that is also a ScriptableObject, allowing it to be saved as an asset in Unity.\n  /// </summary>\n  public abstract class ScriptableObjectModel : ScriptableObject, IModel\n  {\n    [field: SerializeField][Bind(\"Title\")] public string Title { get; set; } = \"Title\";\n    [field: SerializeField][Bind(\"Render\")] public bool Render { get; set; } = true;\n    public Action onModelChange { get; set; }\n\n    public event System.Action Redraw;\n\n    //public abstract List<object> GetDrawableObjects() { }\n\n    public abstract void Initialize(VisualElement container, Plate plate);\n\n\tpublic void Refresh(VisualElement container)\n\t{\n\t}\n\n\n\t#region ButtonAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.Button]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    #endregion\n    internal void ForceRedraw()\n    {\n      Redraw?.Invoke();\n    }\n\n    //public abstract void Render(UnityEngine.UIElements.VisualElement container, UIControlsTemplates templates);\n\n    //[Button]\n    //public abstract void OnSubmit();\n    //[Button]\n    //public abstract void OnCancel();\n  }\n}"
  },
  {
    "path": "src/Core/Model/ScriptableObjectModel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 9c7aa560e9cd18a45baf58f91137a8cb\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 806f5574c9706cd4e812686596744160, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/ViewModel/FormViewModel.cs",
    "content": "﻿using Sirenix.OdinInspector;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  public interface IFormViewModel : IModel\n  {\n\tbool IsModelDirty { get; }\n\tbool BlockRoutingOnDirty { get; set; }\n\tbool HideButtons { get; set; }\n\n\tbool PlateIsActive { get; }\n\n\tvoid Submit();\n\tvoid Cancel();\n\tvoid Reset();\n\n\tvoid PromptReset();\n\n\tpublic event System.Action onSubmit;\n\tpublic event System.Action onCancel;\n\n\tvoid UpdateFormButtonsState(bool enabled, bool active);\n  }\n\n  public abstract class FormViewModel : ViewModelComponent, IFormViewModel, IStateInterpreter<string>, IGrapheneInitializable\n  {\n\t#region Bindables\n\t//[Bind(\"Title\")] public override string Title => originalCached?.Title;\n\n\tpublic event System.Action onSubmit;\n\tpublic event System.Action onCancel;\n\tpublic event System.Action onReset;\n\n\t[Bind(\"Submit\")]\n\tpublic BindableObject submitBinding =  new BindableObject();\n\t[Bind(\"Cancel\")]\n\tpublic BindableObject cancelBinding =  new BindableObject();\n\t[Bind(\"Reset\")]\n\tpublic BindableObject resetBinding = new BindableObject();\n\n\tbool initialized;\n\n#if ODIN_INSPECTOR\n\t[field: ShowInInspector]\n#endif\n\tpublic bool IsModelDirty { get; set; }\n\t[field: SerializeField] public bool BlockRoutingOnDirty { get; set; } = true;\n\n\t[field: SerializeField] public bool HideButtons { get; set; } = false;\n\t#endregion\n\n\t#region VisualElement\n\tButton submitButton;\n\tButton cancelButton;\n\tButton resetButton;\n\t#endregion\n\n\tRouter<string> router;\n\t#region LifeCycle\n\tprotected override void Awake()\n\t{\n\t  base.Awake();\n\t  //submitBinding = Submit;\n\t  //cancelBinding = Cancel;\n\t  submitBinding.OnClick.AddListener(Submit);\n\t  cancelBinding.OnClick.AddListener(Cancel);\n\t  resetBinding.OnClick.AddListener(Reset);\n\t  MarkDirty(false);\n\t}\n\n\tpublic virtual void Initialize()\n\t{\n\t  router = graphene.Router as Router<string>;\n\t  router.RegisterInterpreter(this);\n\t  initialized = true;\n\t}\n\n\tvoid OnEnable()\n\t{\n\t  if (!initialized)\n\t\treturn;\n\t  router.RegisterInterpreter(this);\n\t  Subscribe();\n\t}\n\n\tvoid OnDisable()\n\t{\n\t  UnSubscribe();\n\t  graphene?.Router.UnregisterInterpreter(this);\n\t}\n\tbool subscribed;\n\n\tvoid Subscribe()\n\t{\n\t  if (subscribed)\n\t\treturn;\n\t  subscribed = true;\n\t}\n\tvoid UnSubscribe()\n\t{\n\t  subscribed = false;\n\t}\n\t#endregion\n\n\tpublic bool TrySubmit()\n\t{\n\t  if (!enabled)\n\t\treturn false;\n\n\t  Submit();\n\t  return true;\n\t}\n\n\tpublic bool TryCancel()\n\t{\n\t  Cancel();\n\t  return true;\n\t}\n\n\tpublic bool TryReset()\n\t{\n\t  Reset();\n\t  return true;\n\t}\n\tbool IStateInterpreter.CanCatch(object state) => TryCatch((string)state);\n\tpublic bool CanCatch(string state)\n\t{\n\t  if (!enabled || !gameObject.activeInHierarchy)\n\t\treturn false;\n\n\t  if (state == \"submit\")\n\t\treturn true;\n\t  else if (state == \"cancel\")\n\t\treturn true;\n\t  else if (state == \"reset\")\n\t\treturn true;\n\t  return false;\n\t}\n\tbool IStateInterpreter.TryCatch(object state) => TryCatch((string)state);\n\n\tpublic bool TryCatch(string state)\n\t{\n\t  if (!enabled || !gameObject.activeInHierarchy)\n\t\treturn false;\n\n\n\t  if (state == \"submit\")\n\t  {\n\t\tTrySubmit();\n\t\treturn true;\n\t  }\n\t  else if (state == \"cancel\")\n\t  {\n\t\tTryCancel();\n\t\treturn true;\n\t  }\n\t  else if (state == \"reset\")\n\t  {\n\t\tTryReset();\n\t\treturn true;\n\t  }\n\n\t  // Show confirmation dialog here\n\t  if (IsModelDirty)\n\t\treturn true;\n\t  \n\t  return false;\n\t}\n\n\n\t[ResponsiveButtonGroup]\n\tpublic abstract void Cancel();\n\t[ResponsiveButtonGroup]\n\tpublic abstract void Submit();\n\n\tpublic abstract void Reset();\n\tpublic abstract void PromptReset();\n\n\tprotected void MarkDirty(bool dirty)\n\t{\n\t  IsModelDirty = dirty;\n\t  SetButtonsDirty(dirty);\n\n\t  // Block router\n\t  if (BlockRoutingOnDirty && graphene?.Router)\n\t  {\n\t\tif (dirty)\n\t\t  graphene.Router.TryBlock(this);\n\t\telse\n\t\t  graphene.Router.TryUnblock(this);\n\t  }\n\t  ModelChange();\n\t}\n\n\t[Button]\n\tprotected virtual void SetButtonsDirty(bool dirty)\n\t{\n\t  submitButton?.SetEnabled(dirty && CanSubmit());\n\t  cancelButton?.SetEnabled(dirty && CanCancel());\n\t  submitBinding?.SetEnabled(dirty && CanSubmit());\n\t  cancelBinding?.SetEnabled(dirty && CanCancel());\n\n\n\t  resetButton?.SetEnabled(CanReset());\n\t  resetBinding?.SetEnabled(CanReset());\n\t}\n\tpublic void UpdateFormButtonsState(bool enabled, bool active)\n\t{\n\t  SetButtonsDirty(enabled);\n\t  submitButton?.SetActive(active);\n\t  cancelButton?.SetActive(active);\n\t  submitBinding?.SetActive(active);\n\t  cancelBinding?.SetActive(active);\n\n\n\t  resetButton?.SetActive(active);\n\t  resetBinding?.SetActive(active);\n\t}\n\n\tpublic virtual bool CanSubmit() => true;\n\tpublic virtual bool CanCancel() => true;\n\tpublic virtual bool CanReset() => true;\n  }\n\n  public abstract class FormViewModel<T> : FormViewModel, ICustomDrawContext\n  {\n\t[ShowInInspector] T originalCached;\n\t[System.NonSerialized, ShowInInspector, InlineEditor] public T viewModelCopy;\n\tobject ICustomDrawContext.GetCustomDrawContext { get => viewModelCopy; }\n\n\tpublic abstract void UpdateSourceData();\n  }\n}"
  },
  {
    "path": "src/Core/Model/ViewModel/FormViewModel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4966129684538d749affd14e9fd70661\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/ViewModel/MultiFormViewModel.cs",
    "content": "using Sirenix.OdinInspector;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n\n  public class MultiFormViewModel : FormViewModel, IStateInterpreter, IFormViewModel\n  {\n\t[SerializeField] GenericModelBehaviour buttonsViewModel;\n\n#if ODIN_INSPECTOR\n\t[ShowInInspector]\n#endif\n\tList<IFormViewModel> childForms;\n\tbool formsInitialized;\n\n\t#region LifeCycle\n\tpublic override void Initialize(VisualElement container, Plate plate)\n\t{\n\t  if (!formsInitialized)\n\t  {\n\t\tvar forms = transform.GetComponentsInChildren<IFormViewModel>().ToList();\n\t\tforms.Remove(this);\n\t\tchildForms = forms;\n\t\tforeach (var item in childForms)\n\t\t{\n\t\t  item.BlockRoutingOnDirty = false;\n\t\t  item.HideButtons = true;\n\t\t  item.UpdateFormButtonsState(false, false);\n\t\t}\n\t\tformsInitialized = true;\n\t  }\n\n\t  if (buttonsViewModel)\n\t  {\n\t\tbuttonsViewModel.Items.Clear();\n\t\tbuttonsViewModel.Items.Add(new BindableObject\n\t\t{\n\t\t  Name = \"SUBMIT\",\n\t\t  customName = \"SubmitButton\",\n\t\t  addClass = \"submit\",\n\t\t  route = \"index\"\n\t\t});\n\t\tbuttonsViewModel.Items.Add(new BindableObject\n\t\t{\n\t\t  Name = \"CANCEL\",\n\t\t  customName = \"CancelButton\",\n\t\t  addClass = \"return\",\n\t\t  route = \"index\"\n\t\t});\n\t\tbuttonsViewModel.Items.Add(new BindableObject\n\t\t{\n\t\t  Name = \"RESET\",\n\t\t  customName = \"ResetButton\",\n\t\t  addClass = \"cancel\",\n\t\t  //route = \"index\"\n\t\t});\n\n\t\tbuttonsViewModel.Items[0].OnClick.AddListener(Submit);\n\t\tbuttonsViewModel.Items[1].OnClick.AddListener(Cancel);\n\t\tbuttonsViewModel.Items[2].OnClick.AddListener(Reset);\n\t  }\n\t}\n\t#endregion\n\n\tpublic override void Cancel()\n\t{\n\t  foreach (var form in childForms)\n\t  {\n\t\tif (form.IsModelDirty)\n\t\t  form.Cancel();\n\t  }\n\t}\n\n\tpublic override void Submit()\n\t{\n\t  foreach (var form in childForms)\n\t  {\n\t\tif (form.IsModelDirty)\n\t\t  form.Submit();\n\t  }\n\t}\n\n\tpublic override void Reset()\n\t{\n\t  foreach (var form in childForms)\n\t  {\n\t\tif (form.PlateIsActive)\n\t\t  form.PromptReset();\n\t  }\n\t}\n\n\tpublic override void PromptReset()\n\t{\n\t  throw new System.NotImplementedException();\n\t}\n\n\tprotected override void SetButtonsDirty(bool dirty)\n\t{\n\t  if (buttonsViewModel && buttonsViewModel.Items.Count > 1)\n\t  {\n\t\tbuttonsViewModel.Items[0].SetEnabled(dirty);\n\t\tbuttonsViewModel.Items[1].SetEnabled(dirty);\n\t  }\n\t}\n\n  }\n}"
  },
  {
    "path": "src/Core/Model/ViewModel/MultiFormViewModel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: bf9529738e742e84fbf6e0d45ae2e2d8\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/ViewModel/NavViewModel.cs",
    "content": "using System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  public class NavViewModel : GenericModelBehaviour\n  {\n\t[Bind(\"HasContent\")]\n\tpublic override bool HasContent => Routes != null && Routes.Count > 0 || base.HasContent;\n\n\n\t[field: SerializeField]\n\tpublic bool TitleFromRoutes { get; private set; }\n\n\tpublic enum RenderMode\n\t{\n\t  Manual,\n\t  Siblings,\n\t  SiblingsWithState\n\t}\n\n\t[field: SerializeField]\n\tpublic RenderMode renderMode { get; private set; } = NavViewModel.RenderMode.SiblingsWithState;\n\n\t[field: SerializeField] public Plate OverridePlate { get; private set; }\n\n\t[Bind(\"Routes\")]\n\tpublic List<string> Routes = new List<string>();\n\n\tpublic override void Initialize(VisualElement container, Plate plate)\n\t{\n\t  switch (renderMode)\n\t  {\n\t\tcase RenderMode.SiblingsWithState:\n\t\t  CreateBindableObjectsFromSiblingsWithState(OverridePlate ?? plate);\n\t\t  break;\n\t  }\n\t}\n\n\tpublic override void Inject(Graphene graphene)\n\t{\n\t  base.Inject(graphene);\n\t  if(router)\n\t\trouter.onStateChange += Router_onStateChange;\n\t}\n\tprivate void Router_onStateChange(string newState)\n\t{\n\t  if (TitleFromRoutes)\n\t  {\n\t\tvar index = Routes.IndexOf(newState);\n\t\tif (index >= 0)\n\t\t  Title = Routes[index].ToUpper();\n\t\tModelChange();\n\t  }\n\t}\n\tvoid OnDestroy()\n\t{\n\t  if (router)\n\t\trouter.onStateChange -= Router_onStateChange;\n\t}\n\n\tvoid CreateBindableObjectsFromSiblingsWithState(Plate plate)\n\t{\n\t  this.Routes.Clear();\n\n\t  IReadOnlyList<Plate> children = plate.Parent ? plate.Parent.Children : null;\n\n\t  if (children == null || children.Count == 0)\n\t\treturn;\n\n\t  foreach (var sibling in children)\n\t  {\n\t\tif (!sibling || !(sibling.StateHandle is StringStateHandle stringStateHandle))\n\t\t  continue;\n\n\t\tif (stringStateHandle)\n\t\t{\n\t\t  Routes.Add(stringStateHandle.StateID);\n\t\t}\n\t  }\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Model/ViewModel/NavViewModel.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ddce112e23d338443b4d10a813ce4c25\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/ViewModel/ViewModelComponent.cs",
    "content": "﻿using System;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.ViewModel\n{\n  /// <summary>\n  /// A ViewModel is a component that implements IModel and is responsible for providing data to be rendered by a Renderer on a Plate.\n  /// </summary>\n  [RequireComponent(typeof(Renderer))]\n  public abstract class ViewModelComponent : GrapheneComponent, IModel\n  {\n\t[SerializeField, HideInInspector] protected new Renderer renderer; public Renderer Renderer => renderer;\n\t[SerializeField, HideInInspector] protected Plate plate; public Plate Plate => plate;\n\n\t[Bind(\"Title\", BindingMode.OneWay)]\n    [field: SerializeField] public virtual string Title { get; set; }\n\n\t[Bind(\"HasContent\")]\n\tpublic virtual bool HasContent => Render;\n\n\t[field: SerializeField] public bool Render { get; set; } = true;\n    public Action onModelChange { get; set; }\n\n\tpublic bool PlateIsActive => plate && plate.IsActive;\n\n\tprotected override void Awake()\n\t{\n\t  base.Awake();\n\t  if (!this.plate)\n\t\tthis.plate = GetComponent<Plate>();\n\t  if (!this.renderer)\n\t\trenderer = GetComponent<Renderer>();\n\t}\n\n\tpublic override void Inject(Graphene graphene)\n\t{\n\t  base.Inject(graphene);\n\t  if(!this.plate)\n\t\tthis.plate = GetComponent<Plate>();\n\t  if(!this.renderer)\n\t\trenderer = GetComponent<Renderer>();\n\n\t  plate.onShow.AddListener(OnShow);\n\t  plate.onHide.AddListener(OnHide);\n\t}\n\n\tpublic abstract void Initialize(VisualElement container, Plate plate);\n\n    // For enabled\n    void Start()\n    {\n    }\n\n\tprotected virtual void OnShow() { }\n\tprotected virtual void OnHide() { }\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup]\n#endif\n\tpublic virtual void ModelChange()\n\t{\n\t  onModelChange?.Invoke();\n\t}\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup]\n#endif\n\tpublic virtual void Refresh(VisualElement container)\n\t{\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Model/ViewModel/ViewModelComponent.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 94c3d559523a63c4298689c839b2109d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model/ViewModel.meta",
    "content": "fileFormatVersion: 2\nguid: c868cc3a0db70c940af6b51404f58456\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Model.meta",
    "content": "fileFormatVersion: 2\nguid: d78566ea2cede254091b6cbfd364100d\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Plate.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.Events;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\nusing Sirenix.OdinInspector;\n#endif\n\nnamespace Graphene\n{\n  using Elements;\n  using UnityEngine.Profiling;\n\n  /// <summary>\n  /// Determines the <see cref=\"Position\"> mode for a plate when the hierarchy is built\n  /// </summary>\n  public enum PositionMode\n  {\n\t/// <summary>\n\t/// Don't override the <see cref=\"Position\"/> mode\n\t/// </summary>\n\tNone,\n\t/// <summary>\n\t/// Sets the plate to <see cref=\"Position.Relative\"/>\n\t/// </summary>\n\tRelative,\n\t/// <summary>\n\t/// Sets the plate to <see cref=\"Position.Absolute\"/>\n\t/// </summary>\n\tAbsolute\n  }\n\n  /// <summary>\n  /// Determines how the binding system refreshes bindings on this plate\n  /// </summary>\n  public enum BindingRefreshMode\n  {\n\tNone,\n\t/// <summary>\n\t/// Bindings are refreshed every update of the BindingsManager.\n\t/// </summary>\n\tContinuous,\n\t/// <summary>\n\t/// Bindings only update when the model triggers its ModelChanged callback.\n\t/// </summary>\n\tModelChange\n  }\n\n  /// <summary>\n  /// Determines whether the plate shows/hides immediately or with a transition.\n  /// </summary>\n  public enum ShowHideMode\n  {\n\tImmediate,\n\tTransition\n  }\n  ///<summary>\n  /// <para>A `Plate` represents a view controller in the VisualTree, and is used when by Graphene to the hierarchy, its states and views.</para> \n  /// <para><see href=\"https://github.com/LudiKha/Graphene#plates\">Read more in the online documentation</see></para>\n  ///</summary>\n  [DisallowMultipleComponent]\n  public class Plate : GrapheneComponent, IGrapheneInitializable, IDisposable\n  {\n    #region Constants\n\n    /// <summary>\n    /// The default class name for a plate root element.\n    /// </summary>\n    public const string plateClassName = \"plate\";\n    /// <summary>\n    /// Selector for the content view.\n    /// </summary>\n    public const string contentViewSelector = \"Content\";\n    /// <summary>\n    /// Selector for the children view.\n    /// </summary>\n    public const string childViewSelector = \"Children\";\n    #endregion\n\n    #region Inspector/Authoring\n    /// <summary>\n    /// The VisualTreeAsset used to instantiate the plate's UI.\n    /// </summary>\n    [SerializeField, OnValueChanged(nameof(OnChangeDocument))]\n    VisualTreeAsset visualAsset;\n    /// <summary>\n    /// Gets or sets the VisualTreeAsset for this plate.\n    /// </summary>\n    public VisualTreeAsset VisualTreeAsset { get => visualAsset; set => SetVisualTreeAsset(value); }\n\n    [SerializeField, HideInInspector]\n    VisualTreeAsset cachedAsset;\n\n    /// <summary>\n    /// Determines how picking is handled for the root element.\n    /// </summary>\n    [SerializeField]\n    PickingMode pickingMode = PickingMode.Position;\n\n    /// <summary>\n    /// Controls how bindings are refreshed.\n    /// </summary>\n    [SerializeField]\n    public BindingRefreshMode bindingRefreshMode = BindingRefreshMode.ModelChange;\n\n    /// <summary>\n    /// Inline style overrides for the plate.\n    /// </summary>\n    [SerializeField, FoldoutGroup(\"Styles Overrides\")]\n    InlineStyleOverrides styleOverrides = new InlineStyleOverrides() { positionMode = PositionMode.Relative };\n\n    /// <summary>\n    /// List of style overrides for individual views.\n    /// </summary>\n    [SerializeField, FoldoutGroup(\"Styles Overrides\"), ListDrawerSettings(HideAddButton = true, HideRemoveButton = true, ListElementLabelName = \"Id\")]\n    List<SerializedView> viewStyleOverrides = new List<SerializedView>();\n    #endregion\n\n    #region State\n    /// <summary>\n    /// Indicates if the plate was changed this frame.\n    /// </summary>\n    internal bool wasChangedThisFrame;\n\n    bool isActive = true;\n    /// <summary>\n    /// Gets whether the plate is currently active and enabled in the hierarchy.\n    /// </summary>\n    public bool IsActive => isActive && enabled && gameObject.activeInHierarchy;\n    #endregion\n\n    #region Properties\n    /// <summary>\n    /// Returns true if this plate is the root plate (has no parent).\n    /// </summary>\n    public bool IsRootPlate => !parent;\n\n    /// <summary>\n    /// Returns true if this plate requires views to be rendered.\n    /// </summary>\n    public bool RequiresViews => children.Count > 0 || renderer != null;\n\n    /// <summary>\n    /// Returns true if the plate requires a rebuild of its views.\n    /// </summary>\n    public bool RequiresRebuild => cachedAsset != visualAsset || viewIds.Count != viewStyleOverrides.Count;\n    #endregion\n\n    #region Component Reference\n    [ShowInInspector]\n    Plate parent;\n    /// <summary>\n    /// Gets the parent plate, if any.\n    /// </summary>\n    public Plate Parent => parent;\n\n    [ShowInInspector]\n    List<Plate> children = new List<Plate>();\n    /// <summary>\n    /// Gets the child plates of this plate.\n    /// </summary>\n    public IReadOnlyList<Plate> Children => children;\n\n    /// <summary>\n    /// List of view IDs in the template.\n    /// </summary>\n    [SerializeField, ReadOnly]\n    public List<string> viewIds = new List<string>();\n\n    /// <summary>\n    /// Reference to the default view.\n    /// </summary>\n    [SerializeField]\n    public ViewRef defaultViewRef = new ViewRef(childViewSelector);\n\n    /// <summary>\n    /// Reference to the content view.\n    /// </summary>\n    [SerializeField]\n    public ViewRef contentViewRef = new ViewRef(contentViewSelector);\n\n    /// <summary>\n    /// Reference to the view to attach to in the parent plate.\n    /// </summary>\n    [SerializeField]\n    public ViewRef attachToParentView = new ViewRef(\"\");\n\n    /// <summary>\n    /// The router associated with this plate.\n    /// </summary>\n    [SerializeField]\n    protected Router router;\n    public Router Router => router;\n\n    /// <summary>\n    /// The state handle associated with this plate.\n    /// </summary>\n    [SerializeField]\n    protected StateHandle stateHandle;\n    public StateHandle StateHandle => stateHandle;\n\n    /// <summary>\n    /// The renderer associated with this plate.\n    /// </summary>\n    [SerializeField]\n    new protected Renderer renderer;\n    public Renderer Renderer => renderer;\n    #endregion\n\n    #region VisualElements Reference\n    private TemplateContainer clone;\n    /// <summary>\n    /// The root visual element of this plate.\n    /// </summary>\n    public VisualElement Root { get; private set; }\n\n    /// <summary>\n    /// Main container for attached renderer's output of (repeat) elements.\n    /// </summary>\n    public VisualElement ContentContainer => contentViewRef.view;\n\n    /// <summary>\n    /// The default view. This controller's children will be added to this by default.\n    /// </summary>\n    View defaultView => defaultViewRef.view;\n\n    /// <summary>\n    /// Dictionary of views in the template, keyed by ID.\n    /// </summary>\n    Dictionary<string, View> views = new Dictionary<string, View>();\n\n    /// <summary>\n    /// Tracks which child plates are attached to which views.\n    /// </summary>\n    Dictionary<View, List<Plate>> childAttachments = new Dictionary<View, List<Plate>>();\n    #endregion\n\n    #region (Unity) Events\n    /// <summary>\n    /// Invoked when the plate's state is evaluated.\n    /// </summary>\n    public event System.Action onEvaluateState;\n    /// <summary>\n    /// Invoked when static content is refreshed.\n    /// </summary>\n    public event System.Action onRefreshStatic;\n    /// <summary>\n    /// Invoked when dynamic content is refreshed.\n    /// </summary>\n    public event System.Action onRefreshDynamic;\n    /// <summary>\n    /// Invoked when the visual tree is refreshed.\n    /// </summary>\n    public event System.Action onRefreshVisualTree;\n\n    /// <summary>\n    /// Unity event invoked when the plate is shown.\n    /// </summary>\n    public UnityEvent onShow = new UnityEvent();\n    /// <summary>\n    /// Unity event invoked when the plate is hidden.\n    /// </summary>\n    public UnityEvent onHide = new UnityEvent();\n    #endregion\n\n    /// <summary>\n    /// Gets or sets whether the plate has been initialized.\n    /// </summary>\n    public bool Initialized { get; set; }\n    bool registeredToParent;\n\n    /// <summary>\n    /// Initializes the plate, setting up references and parent-child relationships.\n    /// </summary>\n    public virtual void Initialize()\n    {\n      GetLocalReferences();\n\n      if (parent && !registeredToParent)\n      {\n        parent.onShow.AddListener(Parent_OnShow);\n        parent.onHide.AddListener(Parent_OnHide);\n        registeredToParent = true;\n      }\n    }\n\n    /// <summary>\n    /// Called when the component awakens. Clears events and child list.\n    /// </summary>\n    protected override void Awake()\n    {\n      base.Awake();\n      // Clear events\n      onRefreshDynamic = null;\n      onRefreshStatic = null;\n      onRefreshVisualTree = null;\n      onShow.RemoveAllListeners();\n      onHide.RemoveAllListeners();\n      children.Clear();\n    }\n\n    /// <summary>\n    /// Gets local component references and registers with parent plate if applicable.\n    /// </summary>\n    protected virtual void GetLocalReferences()\n    {\n      if (graphene)\n      {\n        router ??= graphene.Router;\n      }\n      //customView ??= GetComponent<ViewHandle>();\n\n      stateHandle ??= GetComponent<StateHandle>();\n      renderer ??= GetComponent<Renderer>();\n\n      // Get nearest parent\n      if ((Application.isPlaying && parent) || (parent = transform.parent?.GetComponentInParent<Plate>(true))) \n          parent.RegisterChild(this);\n    }\n\n    /// <summary>\n    /// Constructs the visual tree for this plate, instantiating views and applying styles.\n    /// </summary>\n    internal void ConstructVisualTree()\n    {\n      Profiler.BeginSample(\"Graphene Plate Construct VisualTree\", this);\n      Root?.Clear();\n\n      clone = visualAsset.CloneTree();\n\t  Root = clone.Children().First();\n\n#if UNITY_ASSERTIONS\n      Debug.Assert(clone != null, this);\n      Debug.Assert(Root != null, this);\n      Debug.Assert(clone.childCount == 1, $\"{nameof(Plate)} {nameof(TemplateContainer)} must have exactly 1 child {nameof(VisualElement)}\", this);\n      //Assert.IsNotNull(RootPlateEl);\n#endif\n\n      Root.pickingMode = pickingMode;\n\n      if(pickingMode == PickingMode.Ignore)\n        Root.Query().ForEach(t => t.pickingMode = PickingMode.Ignore);\n\n\t  if (RequiresRebuild)\n\t\tEditModeCacheViewIds();\n\n\t  // Get views\n\t  InitViewsRuntime();\n\n      RefreshClassesAndStyles();\n\n      Root.AddToClassList(plateClassName);\n\n      Initialized = true;\n      onRefreshVisualTree?.Invoke();\n\n      Root.RegisterCallback<MouseOverEvent>((evt) => ChangeEvent());\n      Root.RegisterCallback<PointerCaptureEvent>((evt) => ChangeEvent());\n      Root.RegisterCallback<PointerMoveEvent>((evt) => ChangeEvent());\n      Root.RegisterCallback<PointerDownEvent>((evt) => ChangeEvent());\n      Root.RegisterCallback<PointerUpEvent>((evt) => ChangeEvent());\n\n      if (IsRootPlate)\n      {\n        graphene.GrapheneRoot.Add(Root);\n        Root.AddToClassList(\"unity-ui-document__child\");\n      }\n\n\t  // Hide on start\n\t  if (styleOverrides.showHideMode == ShowHideMode.Transition)\n\t\tRoot.FadeOut();\n\n\t  // Fadeout events\n\t  //Root.RegisterCallback<TransitionStartEvent>(Root_StartTransition);\n\n\t  Profiler.EndSample();\n    }\n\n    void Root_StartTransition(TransitionStartEvent evt)\n    {\n      //Debug.Log($\"Start transition {evt.target}\");\n    }\n    void Root_EndTransition(TransitionEndEvent evt)\n\t{\n\t  //Debug.Log($\"End transition {evt.target}\");\n\t  if (evt.target != Root)\n        return;\n\n      ApplyActiveState();\n\t  Root.UnregisterCallback<TransitionEndEvent>(Root_EndTransition);\n\n\t  //if (!isActive || Root.ClassListContains(VisualElementExtensions.fadeoutUssClassName))\n\t  //{\n\n\t  //}\n\t  //else\n\t  //  Show();\n\t}\n\n\tvoid ChangeEvent()\n    {\n      wasChangedThisFrame = true;\n    }\n\n    protected void RegisterChild(Plate child)\n    {\n      if (!children.Contains(child))\n        children.Add(child);\n    }\n\n    #region ButtonAttribute\n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.ResponsiveButtonGroup(\"ShowHide/Actions\"), FoldoutGroup(\"ShowHide\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    #endregion\n    protected virtual void Clear()\n    {\n      // Clear the dynamic content\n      ContentContainer?.Clear();\n    }\n\n    public void RefreshContentContainer()\n    {\n      DetachChildPlates(contentViewRef.view);\n      Clear();\n      ReattachChildPlates(contentViewRef.view);\n    }\n\n\t#region ButtonAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup(\"ShowHide/Actions\"), FoldoutGroup(\"ShowHide\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    #endregion\n    internal virtual void RenderAndComposeChildren()\n    {\n      Profiler.BeginSample(\"RenderAndComposeChildren\", this);\n      // Detach the children so they don't get bound to the scope\n      DetachChildPlates();\n      Clear();\n\n      onRefreshStatic?.Invoke();\n\n      // (Re)attach & compose the tree\n      AttachChildPlates();\n\n      onRefreshDynamic?.Invoke();\n      Profiler.EndSample();\n    }\n\n    // UIDocument removes the root OnDisable, so we only need OnEnable\n    private void OnEnable()\n    {\n      if (!Initialized)\n        return;\n      Show();\n      //ConstructVisualTree();\n      //RenderAndComposeChildren();\n      //ReevaluateState();\n    }\n\n    private void OnDisable()\n    {\n      if (!Initialized)\n        return;\n      Hide();\n    }\n\n    void InitViewsRuntime()\n    {\n      if (Root == null)\n        return;\n\n      var views = Root.Query<View>().ToList();\n      this.views.Clear();\n      View resolvedDefaultView = null;\n      foreach (var view in views)\n      {\n        this.views.Add(view.id, view);\n        if (view.isDefault || (resolvedDefaultView == null))\n\t\t  resolvedDefaultView = view;\n      }\n\n      defaultViewRef.ResolveView(this);\n\n      if (resolvedDefaultView == null)\n\t\tresolvedDefaultView = views.FirstOrDefault();\n\n      if (!defaultViewRef.initialized)\n        defaultViewRef.view = resolvedDefaultView;\n\n      if (!defaultViewRef.initialized)\n      {\n#if UNITY_ASSERTIONS\n        if(children.Count > 0 || renderer != null)\n          Debug.LogWarning($\"No default view {defaultViewRef.Id}\", this);\n#endif\n      }\n\n\t  contentViewRef.ResolveView(this);\n\n      if (parent != null)\n        attachToParentView.ResolveView(parent);\n    }\n\n    void EditModeCacheViewIds()\n    {\n      // Playing & already initialized\n      if (Root != null)\n        return;\n\n      var el = Root != null ? Root : this.visualAsset.CloneTree();\n      viewIds.Clear();\n      el.Query<View>().ForEach(v => viewIds.Add(v.id));\n\n      // Sync Ids\n      if (this.viewStyleOverrides.Count != viewIds.Count)\n      {\n        this.viewStyleOverrides = new List<SerializedView>();\n        foreach (var view in viewIds)\n          this.viewStyleOverrides.Add(new SerializedView(view));\n\t  }\n    }\n\n    void SetVisualTreeAsset(VisualTreeAsset asset)\n    {\n      if(this.visualAsset == asset) \n        return;\n      this.visualAsset = asset;\n      OnChangeDocument();\n\t}\n\n    void OnChangeDocument()\n    {\n\t  UpdateViewPlates();\n      RefreshClassesAndStyles();\n    }\n\n    void UpdateViewPlates()\n    {\n\t  EditModeCacheViewIds();\n\n\t  defaultViewRef.SetPlate(this);\n      contentViewRef.SetPlate(this);\n\n\t  if (parent != null)\n\t\tattachToParentView.SetPlate(parent);\n\t  else\n\t\tattachToParentView.NoParent();\n\n\t}\n\n    #region ButtonAttribute\n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.ResponsiveButtonGroup(\"ShowHide/Actions\"), FoldoutGroup(\"ShowHide\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    #endregion\n    public void Show()\n    {\n      if (!Initialized)\n        return;\n\n      if (isActive) return;\n\n      if (canShow != null && !canShow.Invoke())\n        return;\n\n      // Cannot show\n      if (transform.parent?.gameObject.activeInHierarchy == false)\n        return;\n\n      SetActive(true);\n      ApplyActiveState(); // Immediately activate GO\n\n      // Enable\n      if (styleOverrides.showHideMode == ShowHideMode.Transition)\n        Root.FadeIn();\n    }\n\n    #region ButtonAttribute\n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.ResponsiveButtonGroup(\"ShowHide/Actions\"), FoldoutGroup(\"ShowHide\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    #endregion\n    public void Hide()\n    {\n      if (!Initialized)\n        return;\n\n      if (!isActive)\n        return;\n\n      SetActive(false);\n\n      if (styleOverrides.showHideMode == ShowHideMode.Immediate)\n        ApplyActiveState();\n      else\n      {\n\t\tRoot.RegisterCallback<TransitionEndEvent>(Root_EndTransition);\n\t\tRoot.FadeOut();\n      }\n    }\n\n    internal void HideImmediately()\n    {\n      if (!Initialized)\n        return;\n      SetActive(false);\n      ApplyActiveState();\n    }\n\n    void SetActive(bool active)\n    {\n      // Not changed\n      if (this.isActive == active)\n        return;\n\n      this.isActive = active;\n    }\n\n    void ApplyActiveState()\n    {\n      gameObject.SetActive(isActive);\n      RefreshClassesAndStyles();\n\n      if (Root != null)\n      {\n        if (isActive)\n        {\n          Root.Show();\n          //ContentContainer?.Focus();\n          onShow?.Invoke();\n          ChangeEvent();\n        }\n        else\n        {\n          Root.Hide();\n          onHide?.Invoke();\n        }\n      }\n\n#if UNITY_EDITOR\n      if (Application.isPlaying)\n      {\n        //gameObject.SetActive(IsActive);\n\n        gameObject.name = gameObject.name.Trim('*');\n        if (isActive)\n          gameObject.name = gameObject.name + \"*\";\n      }\n#endif\n    }\n\n    internal void ReevaluateState()\n    {\n      if (!Initialized)\n        return;\n\n      if (!stateHandle)\n      {\n        if(!parent || parent.isActive)\n          Show();\n      }\n\n      onEvaluateState?.Invoke();\n    }\n\n    public Func<bool> canShow;\n    void Parent_OnShow()\n    {\n      if (!stateHandle)\n        Show();\n    }\n\n    void Parent_OnHide()\n    {\n      if (!stateHandle)\n        Hide();\n    }\n\n    VisualElement temp;\n\tinternal void Detach()\n    {\n      if(temp == null)\n        temp = new VisualElement();\n      temp.Add(Root);\n    }\n\n\tprotected virtual void DetachChildPlates()\n\t{\n\t  foreach (var child in children)\n\t  {\n\t\tif (child)\n\t\t  child.Detach();\n\t  }\n\t}\n\tprotected virtual void DetachChildPlates(View view)\n\t{\n      if(view == null)\n        return;\n\n\t  if (childAttachments.TryGetValue(view, out var children))\n\t  {\n        foreach (var child in children)\n\t\t  child.Detach();\n\t  }\n\t}\n\n\tprotected virtual void ReattachChildPlates(View view)\n\t{\n\t  if (view == null)\n\t\treturn;\n\t  if (childAttachments.TryGetValue(view, out var children))\n\t  {\n\t\tforeach (var child in children)\n          InternalAttach(view, child);\n\t  }\n\t}\n\n\t/// <summary>\n\t/// Attaches child plates into designated view(s)\n\t/// </summary>\n\tvoid AttachChildPlates()\n    {\n      // Prolly unnecessary and will prevent dynamic child attackments\n      if (childAttachments.Count > 0)\n        ReattachChildren();\n\n      // Rebuild from afresh\n      childAttachments.Clear();\n\n\t  foreach (var child in children)\n      {\n        // Child can have optional view override\n        if (child.attachToParentView)\n        {\n          var customView = GetViewById(child.attachToParentView.Id);\n\t\t  AttachChild(customView, child);\n\n\t\t  //customView.Add(child.Root);\n        }\n        else\n        {\n\t\t  // By default we attach children to default view\n\t\t  AttachChild(defaultView, child);\n\t\t  //defaultView.Add(child.Root);\n        }\n      }\n    }\n\n    void ReattachChildren()\n    {\n      foreach (var kvp in childAttachments)\n      {\n        var view = kvp.Key;\n        var children = kvp.Value;\n        foreach (var child in children)\n        {\n          InternalAttach(view, child);\n        }\n      }\n    }\n\n    void AttachChild(View view, Plate child)\n    {\n      if(childAttachments.TryGetValue(view, out var children))\n      {\n        children.Add(child);\n      }\n      else\n      {\n        var list = new List<Plate>();\n        childAttachments.Add(view, list);\n        list.Add(child);\n      }\n      InternalAttach(view, child);\n\t}\n\n    void InternalAttach(View view, Plate child)\n    {\n      view.Add(child.Root);\n    }\n\n\t#region Helper  Methods\n\t/// <summary>\n\t/// Gets a visual element for a collection of selectors by name\n\t/// </summary>\n\t/// <param name=\"names\"></param>\n\t/// <returns></returns>\n\tpublic VisualElement GetVisualElement(ICollection<string> names)\n    {\n      VisualElement target = Root;\n\n      foreach (var name in names)\n      {\n        // Drill down\n        var newTarget = target.Q(name);\n\n        if (newTarget == null)\n        {\n          Debug.LogError($\"VisualElement with name {name} not found as child of {target}\", this);\n          break;\n        }\n\n        target = newTarget;\n      }\n\n      return target;\n    }\n\n    public View GetViewById(string id)\n    {\n      if (views.TryGetValue(id, out View view))\n        return view;\n      else\n        return defaultView;\n    }\n\n    private void OnDestroy()\n    {\n      Dispose();\n    }\n    public void Dispose()\n    {\n      if(BindingsManager)\n        BindingsManager.DisposePlate(this, false);\n    }\n\n    void OnValidate()\n    {\n      if (isPrefab && !RequiresRebuild)\n        return;\n\n      GetLocalReferences();\n\t  UpdateViewPlates();\n      RefreshClassesAndStyles();\n      this.cachedAsset = visualAsset;\n    }\n\n    bool isPrefab => !gameObject.scene.isLoaded;\n\n    const string positionModeRelativeClassNames = \"flex-grow\";\n    const string positionModeAbsoluteClassNames = \"absolute fill\";\n    const string showHideModeTransitionClassNames = \"fade\";\n\n    internal void RefreshClassesAndStyles()\n    {\n      if (Root == null)\n        return;\n\n      this.styleOverrides.Apply(Root);\n            \n      foreach (var viewStyleOverride in viewStyleOverrides)\n      {\n        if (!viewStyleOverride.Enabled)\n          continue;\n\n        if (views.TryGetValue(viewStyleOverride.Id, out View view))\n        {\n          viewStyleOverride.Apply(view);\n        }        \n      }\n    }\n    #endregion\n  }\n}"
  },
  {
    "path": "src/Core/Plate.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 53b76209daa58fc4787cd6e9815c5a8c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences:\n  - graphene: {instanceID: 0}\n  - visualAsset: {fileID: 9197481963319205126, guid: 79af3ccbc09e9cf409acfe9306ae2a11,\n      type: 3}\n  - cachedAsset: {instanceID: 0}\n  - router: {instanceID: 0}\n  - stateHandle: {instanceID: 0}\n  - renderer: {instanceID: 0}\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: d5720deaef5568642aae7621cd85c1d4, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Rendering/RenderUtils.cs",
    "content": "﻿using Kinstrife.Core.ReflectionHelpers;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  internal static class RenderUtils\n  {\n\tinternal static TemplatePreset templatesDefault; // Hax\n\n\tinternal readonly static System.Type stringType = typeof(string);\n\n\t/// <summary>\n\t/// \n\t/// </summary>\n\t/// <param name=\"context\"></param>\n\t/// <returns></returns>\n\tinternal static bool IsPrimitiveContext(this System.Type type) => type.IsPrimitive || type.IsEnum || type == stringType;\n\n\tstatic int recursiveCheck = 0;\n\t/// <summary>\n\t/// Draws controls for all members of a context object\n\t/// </summary>\n\t/// <param name=\"plate\"></param>\n\t/// <param name=\"container\"></param>\n\t/// <param name=\"context\"></param>\n\t/// <param name=\"templates\"></param>\n\tinternal static void DrawDataContainer(Plate plate, VisualElement container, in object context, TemplatePreset templates)\n\t{\n\t  if (recursiveCheck > 5)\n\t  {\n\t\tDebug.LogError($\"Recursive error {plate}\", plate);\n\t\treturn;\n\t  }\n\t  if (context is ICustomDrawContext customDrawContext)\n\t  {\n\t\trecursiveCheck++;\n\t\tDrawDataContainer(plate, container, customDrawContext.GetCustomDrawContext, templates);\n\t\trecursiveCheck--;\n\t\treturn;\n\t  }\n\n\t  if (!templates)\n\t  {\n\t\tUnityEngine.Debug.LogError($\"Assign templates to Renderer for plate for {plate}\", plate);\n\t\treturn;\n\t  }\n\t  else if (container == null)\n\t  {\n#if UNITY_ASSERTIONS\n\t\tUnityEngine.Debug.LogWarning($\"Trying to draw to null VisualElement container {plate.name}\", plate);\n#endif\n\t\treturn;\n\t  }\n\t  else if (context == null)\n\t  {\n#if UNITY_ASSERTIONS && false\n        UnityEngine.Debug.LogError(\"Trying to draw null context\", plate);\n#endif\n\t\treturn;\n\t  }\n\t  templatesDefault = templates;\n\n\t  // Get members\n\t  List<ValueWithAttribute<DrawAttribute>> drawableMembers = new List<ValueWithAttribute<DrawAttribute>>();\n\t  TypeInfoCache.GetMemberValuesWithAttribute(context, drawableMembers);\n\n\t  // Sort draw order\n\t  drawableMembers = drawableMembers.OrderBy(x => x.Attribute.order).ToList();// as List<ValueWithAttribute<DrawAttribute>>;\n\n\t  List<ValueWithAttribute<BindAttribute>> bindableMembers = new List<ValueWithAttribute<BindAttribute>>();\n\t  TypeInfoCache.GetMemberValuesWithAttribute(context, bindableMembers);\n\n\t  // Check how each member should be drawn\n\t  foreach (var member in drawableMembers)\n\t  {\n\t\tif (member.Value is null)\n\t\t  continue;\n\t\telse if (member.Type.IsPrimitiveContext())\n\t\t  DrawFromPrimitiveContext(plate, container, in context, templates, member, bindableMembers);\n\t\telse if (member.Value is IEnumerable enumerable)\n\t\t  DrawFromEnumerableContext(plate, container, in context, templates, member, bindableMembers);\n\t\telse\n\t\t  DrawFromObjectContext(plate, container, in context, templates, member);\n\t  }\n\t}\n\n\tstatic void DrawFromObjectContext(Plate panel, VisualElement container, in object context, TemplatePreset templates, ValueWithAttribute<DrawAttribute> drawMember)\n\t{\n\t  VisualTreeAsset template;\n\n\t  if (drawMember.Value is IHasCustomVisualTreeAsset customVisualTreeAsset && customVisualTreeAsset.VisualTreeAsset != null)\n\t\ttemplate = customVisualTreeAsset.VisualTreeAsset;\n\t  else\n\t  {\n\t\tControlType controlType = ControlType.None;\n\n\t\t// Override control for class\n\t\tif (drawMember.Value is ICustomControlType customControl && customControl.ControlType != ControlType.None)\n\t\t  controlType = customControl.ControlType;\n\t\telse\n\t\t  controlType = TemplatePreset.ResolveControlType(drawMember.Value, isPrimitiveContext: false, drawMember.Attribute);\n\n\n\t\t// Drill down to subcontext\n\t\tif (controlType == ControlType.None || controlType == ControlType.SubContext)\n\t\t{\n\t\t  //Debug.Log($\"{controlType} {panel.name} {drawMember} {context}\", panel);\n\t\t  recursiveCheck++;\n\t\t  DrawDataContainer(panel, container, drawMember.Value, templates);\n\t\t  recursiveCheck--;\n\t\t  return;\n\t\t}\n\n\t\tif (!templates.TryGetTemplateAsset(controlType, out template))\n\t\t{\n\t\t  Debug.LogError($\"Failed to instantiate template {controlType} for field {drawMember.MemberInfo.Name}\", panel);\n\t\t  return;\n\t\t}\n\t  }\n\n\t  // Clone & bind the control\n\t  VisualElement clone = Binder.Instantiate(in drawMember.Value, template, panel);\n\n\t  // Add the control to the container\n\t  container.Add(clone);\n\t}\n\n\tstatic void DrawFromPrimitiveContext(Plate panel, VisualElement container, in object context, TemplatePreset templates, ValueWithAttribute<DrawAttribute> drawMember, List<ValueWithAttribute<BindAttribute>> bindableMembers)\n\t{\n\t  var bind = bindableMembers.Find(x => x.MemberInfo.Equals(drawMember.MemberInfo));\n\t  if (bind.MemberInfo == null)\n\t\tbind = new ValueWithAttribute<BindAttribute>(drawMember.Value, new BindAttribute(\"Label\", BindingMode.OneTime), drawMember.MemberInfo);\n\n\t  // Get template, clone & bind the control\n\t  ControlType controlType = TemplatePreset.ResolveControlType(drawMember.Value, isPrimitiveContext: true, drawMember.Attribute);\n\t  templates.TryGetTemplateAsset(controlType, out VisualTreeAsset template);\n\t  VisualElement clone = Binder.InstantiatePrimitive(in context, ref bind, template, panel);\n\n\t  // Add any custom typography\n\t  if (drawMember.Attribute is DrawTextAttribute text && text.typography != Typography.None)\n\t\tclone.AddToClassList(Enum.GetName(typeof(Typography), text.typography));\n\n\t  // Add the control to the container\n\t  container.Add(clone);\n\t}\n\n\tstatic void DrawFromEnumerableContext(Plate plate, VisualElement container, in object context, TemplatePreset templates, ValueWithAttribute<DrawAttribute> drawMember, List<ValueWithAttribute<BindAttribute>> bindableMembers)\n\t{\n\t  // Don't support primitives or string\n\t  //if (typeof(T).IsPrimitive)\n\t  //  return;\n\n\t  // If element is listview -> use native functionality\n\t  if (container is ListView listView && drawMember.Value is IList listContext)\n\t  {\n\t\tvar bind = bindableMembers.Find(x => x.MemberInfo.Equals(drawMember.MemberInfo));\n\t\tDrawListView(plate, listView, listContext, templates, in drawMember, bind);\n\t\treturn;\n\t  }\n\n\t  foreach (var item in drawMember.Value as IEnumerable)\n\t  {\n\t\tif (IsPrimitiveContext(item.GetType()))\n\t\t{\n\t\t  // Fugly, but works (for now)\n\t\t  var bind = new ValueWithAttribute<BindAttribute>(item, new BindAttribute(\"Label\", BindingMode.OneTime), drawMember.MemberInfo);\n\t\t  DrawFromPrimitiveContext(plate, container, in item, templates, new ValueWithAttribute<DrawAttribute>(item, drawMember.Attribute, drawMember.MemberInfo), new List<ValueWithAttribute<BindAttribute>> { bind });\n\t\t}\n\t\telse\n\t\t{\n\t\t  var draw = new ValueWithAttribute<DrawAttribute>(item, drawMember.Attribute, drawMember.MemberInfo);\n\t\t  DrawFromObjectContext(plate, container, in item, templates, draw);\n\n\t\t  //var template = templates.TryGetTemplateAsset(item, drawMember.Attribute);\n\t\t  //// Clone & bind the control\n\t\t  //VisualElement clone = Binder.Instantiate(in item, template, panel);\n\t\t  //// Add the control to the container\n\t\t  //container.Add(clone);\n\t\t}\n\t  }\n\t}\n\n\tstatic void DrawListView(Plate plate, ListView listView, in object context, TemplatePreset templates, in ValueWithAttribute<DrawAttribute> drawMember, in ValueWithAttribute<BindAttribute> bindMember)\n\t{\n\t  ControlType controlType = TemplatePreset.ResolveControlType(drawMember.Value, isPrimitiveContext: false, drawMember.Attribute);\n\t  templates.TryGetTemplateAsset(controlType, out VisualTreeAsset template);\n\t  Binder.BindListView(listView, in context, plate, template, in bindMember);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Rendering/RenderUtils.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 8d558fdcdf4792b49aaa6d7a87ce230c\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Rendering/Renderer.cs",
    "content": "﻿using System.Collections;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n\n  /// <summary>\n  /// A Renderer is responsible for binding a Model to a Plate, and rendering the Model's data to the Plate's content container.\n  /// It works with the static part of the Plate (the TemplateRef components) and a dynamic part (the data drawn from the Model).\n  /// </summary>\n  [RequireComponent(typeof(Plate))]\n  public class Renderer : MonoBehaviour, IGrapheneInitializable\n  {\n    [SerializeField] Plate plate; public Plate Plate => plate;\n    [field: SerializeField]/*[Bind(\"Model\")]*/ public Object Model { get; set; }\n    [SerializeField] protected TemplatePreset templates; public TemplatePreset Templates => templates;\n\n    /// <summary>\n    /// Overriding this will target a non-default content container (as defined in Plate)\n    /// </summary>\n    [SerializeField] protected string[] contentSelector = new string[] { };\n\n    /// <summary>\n    /// The ViewModel attached to this Renderer\n    /// </summary>\n    IModel viewModel;\n\n    public void Initialize()\n    {\n      if (plate || (plate = GetComponent<Plate>()))\n      {\n        plate.onRefreshStatic += RebindStatic;\n        plate.onRefreshDynamic += HardRefresh;\n\n        if ((Model && Model is IModel || (Model = GetComponent<IModel>() as Object)))\n        {\n          if (Model is IModel iModel)\n            SetModel(iModel);\n        }\n      }\n    }\n\n    void SetModel(IModel newViewModel)\n    {\n      // Unsubscribe to old\n      if(viewModel != null)\n        viewModel.onModelChange -= Model_onModelChange;\n\n      // Subscribe to new\n      viewModel = newViewModel;\n      viewModel.onModelChange = Model_onModelChange;\n    }\n\n    public void RebindStatic()\n    {     \n      // Render the template components\n      plate.Root.Query<TemplateRef>().ForEach(t => {\n        t.Inject(null, plate, this);\n        t.Render();\n      }\n      );\n\n      // Initialize the ViewModel\n      if (viewModel != null)\n      {\n        try\n        {\n          viewModel.Initialize(GetDrawContainer(), plate);\n        }\n        catch(System.Exception e)\n        {\n          Debug.LogException(e, this);\n        }\n\n        if (!viewModel.Render)\n          return;\n      }\n\n      // Bind the static template to the viewmodel\n      BindStatic(viewModel);\n    }\n\n    internal void BindStatic(IModel viewModelContext)\n    {\n      if (viewModelContext is ICustomBindContext customBindContext && customBindContext != null)\n        Binder.BindRecursive(plate.Root, customBindContext.GetCustomBindContext, null, plate, true);\n      if (viewModelContext != null)\n        Binder.BindRecursive(plate.Root, viewModelContext, null, plate, true);\n      // Bind empty model -> routes (NOTE: Not that great of an option)\n      else\n      {\n        Binder.BindRecursive(plate.Root, this, null, plate, true);\n      }\n    }\n\n    // Callbacks\n    internal void Plate_onRefreshDynamic()\n    {\n      HardRefresh();\n    }\n\n    internal void Model_onModelChange()\n    {\n      plate.wasChangedThisFrame = true;\n    }\n\n\n    internal void RenderToContainer(VisualElement container)\n    {\n      // Initialize & render the form\n      if (viewModel == null)\n        return;\n\n      if (!templates)\n\t\ttemplates = GetTemplatesFromParentsRecursive(this);\n\n      //else\n        RenderUtils.DrawDataContainer(plate, container, viewModel, templates);\n      // if (viewModel is ICustomDrawContext customDrawContext)\n      //RenderUtils.DrawDataContainer(plate, container, customDrawContext.GetCustomDrawContext, templates);\n\n      viewModel.Refresh(container);\n      viewModel.onModelChange?.Invoke();\n    }\n\n    TemplatePreset GetTemplatesFromParentsRecursive(Renderer current)\n    {\n      if(current.templates)\n        return current.templates;\n      else if(current.plate?.Parent?.Renderer)\n        return GetTemplatesFromParentsRecursive(current.plate.Parent.Renderer);\n      else\n        return null;\n    }\n\n    #region Public API\n        \n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.ResponsiveButtonGroup(\"Actions\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    public void Refresh()\n    {\n      viewModel?.Refresh(GetDrawContainer());\n    }\n\n\tbool isRefreshing;\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup(\"Actions\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n\tpublic void HardRefresh()\n\t{\n      if (isRefreshing)\n      {\n        Debug.LogError($\"Recursing refresh {name}\", this);\n        return;\n      }\n      isRefreshing = true;\n\t  ClearContent();\n\t  RenderToContainer(GetDrawContainer());\n      isRefreshing = false;\n\t}\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ResponsiveButtonGroup(\"Actions\")]\n#elif NAUGHTY_ATTRIBUTES\n    [NaughtyAttributes.Button]\n#endif\n    public void ClearContent()\n    {\n\t  plate.RefreshContentContainer();\n    }\n    #endregion\n\n    #region Helper Methods\n    internal VisualElement GetDrawContainer()\n    {\n      // Default use the plate's default container\n      VisualElement container = plate.ContentContainer;\n\n      if (contentSelector.Length > 0)\n        container = plate.GetVisualElement(contentSelector);\n\n      return container;\n    }\n    #endregion\n  }\n}\n"
  },
  {
    "path": "src/Core/Rendering/Renderer.cs.meta",
    "content": "fileFormatVersion: 2\nguid: e3452e75856c69d42a3a5f4e1dbea800\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences:\n  - plate: {instanceID: 0}\n  - blueprint: {instanceID: 0}\n  - templates: {fileID: 11400000, guid: 577f37ff3c038f0418ead2d83c53c59c, type: 2}\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 9d85d8a741327e143b28987b5a69ef93, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Rendering.meta",
    "content": "fileFormatVersion: 2\nguid: 0dd9ac2526742164ea65cbcc31b12095\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/EnableOnState.cs",
    "content": "﻿using System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// Component that activates or deactivates a Plate based on the current state of a Router.\n  /// </summary>\n  /// <typeparam name=\"T\"></typeparam>\n  [RequireComponent(typeof(Plate))]\n  [DisallowMultipleComponent]\n  public abstract class EnableOnState<T> : GrapheneComponent, IGrapheneInjectable, IGrapheneInitializable\n  {\n    public enum ActivationMode\n    {\n      EnableOnStates,\n      DisableOnStates\n    }\n    [SerializeField] ActivationMode mode;\n\n    [SerializeField] protected Plate plate;\n    [SerializeField] List<T> states = new List<T>();\n    protected Router<T> router;\n\n    public bool Initialized { get; private set; }\n    public virtual void Initialize()\n    {\n      if (Initialized)\n        return;\n      Initialized = true;\n\n      if (plate || (plate = GetComponent<Plate>()))\n        plate.onEvaluateState += Plate_onEvaluateState;\n\n      // Get the router in case we didn't inject      \n      router ??= graphene.Router as Router<T>;\n\n      // Subscribe to router state changes\n      router.onStateChange += Router_onStateChange;\n    }\n\n    /// <summary>\n    /// Dependency injection handle\n    /// </summary>\n    /// <param name=\"router\"></param>\n    public void Inject(Router<T> router)\n    {\n      this.router = router;\n    }\n\n\n    private void Router_onStateChange(T address)\n    {\n      bool match = states.IndexOf(router.LeafStateFromAddress(address)) >= 0;\n      bool show = false;\n      switch (mode)\n      {\n        case ActivationMode.EnableOnStates:\n          show = match;\n          break;\n        case ActivationMode.DisableOnStates:\n          show = !match;\n          break;\n        default:\n          break;\n      }\n\n      if (show)\n        plate.Show();\n      else\n        plate.Hide();\n    }\n\n    protected void Plate_onEvaluateState()\n    {\n      Router_onStateChange(router.CurrentState);\n    }\n  }\n\n}"
  },
  {
    "path": "src/Core/Routing/EnableOnState.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5cb892b6070f21c4aa006bc9cde8ecf0\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 724b44860269a9f4abed118244135936, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/Interpreters/ApplicationStateInterpreter.cs",
    "content": "﻿using Graphene;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\nusing Sirenix.OdinInspector;\n#endif\n\nnamespace Graphene\n{\n  using UnityEngine.Events;\n\n  public enum RouterCommand\n  {\n    None,\n    Back,\n    Previous,\n    Exit,\n    Root,\n    Menu,\n    ToggleUI\n  }\n\n  [System.Flags]\n  public enum NavigationInput\n  {\n    None = 0,\n    NavigationMove = 1 << 0,\n    NavigationSubmit = 1 << 1,\n    NavigationCancel = 1 << 2\n  }\n\n#if ODIN_INSPECTOR\n  [Toggle(\"enabled\", CollapseOthersOnExpand = false)]\n#endif\n  [System.Serializable]\n  public class InputOverride\n  {\n\tpublic string name => input.ToString();\n\tpublic bool enabled;\n\tpublic NavigationInput input;\n    public TrickleDown trickleDown = TrickleDown.TrickleDown;\n\t[BoxGroup(\"Output\")] public RouterCommand routerCommand;\n\t[BoxGroup(\"Output\")] public UnityEvent OnInput;\n\t[BoxGroup(\"Output\")] public bool preventDefault = true;\n  }\n\n  //[RequireComponent(typeof(Plate))]\n  /// <summary>\n  /// Listens to state change commands at a Router, and triggers actions when a matching command is detected.\n  /// It can consume the state change request, reroute it to another state, and/or trigger Unity Events in response.\n  /// </summary>\n  public class ApplicationStateInterpreter : StateInterpreter<string>, IGrapheneInjectable, IGrapheneInitializable\n  {\n#if ODIN_INSPECTOR\n    [Toggle(\"enabled\", CollapseOthersOnExpand = false)]\n#endif\n    [System.Serializable]\n    public struct StateCommandHandle\n    {\n      public string name => stateCommand;\n      public bool enabled;\n      [SerializeField] public string stateCommand;\n\n#if ODIN_INSPECTOR\n      [ValidateInput(nameof(ValidateCustomState), \"Custom state reroute should be different from input state command\")]\n#endif\n      [BoxGroup(\"Output\")] public string customState;\n      [BoxGroup(\"Output\"), DisableIf(nameof(hasCustomState))] public RouterCommand routerCommand;\n      [BoxGroup(\"Output\")] public UnityEvent OnStateEnter;\n\n      internal bool hasCustomState => !System.String.IsNullOrWhiteSpace(customState) && customState != stateCommand;\n#if ODIN_INSPECTOR\n      bool ValidateCustomState(string customState)\n      {\n        return customState != stateCommand;\n      }\n#endif\n    }\n\n#if ODIN_INSPECTOR\n\t[ListDrawerSettings(ListElementLabelName = nameof(StateCommandHandle.name))]\n#endif\n    public StateCommandHandle[] commands = new StateCommandHandle[0];\n    public InputOverride[] inputs = new InputOverride[0];\n\n    Router<string> router;\n    Plate plate;\n\n    public void Inject(Router<string> router)\n    {\n      this.router = router;      \n    }\n\n    public bool Initialized { get; private set; }\n    public void Initialize()\n    {\n      if (Initialized) return;\n      Initialized = true;\n\n      router = graphene.Router as Router<string>;\n      router.RegisterInterpreter(this);\n\n      if (plate || TryGetComponent<Plate>(out plate))\n      {\n        plate.onShow.AddListener(Plate_OnShow);\n        plate.onHide.AddListener(Plate_OnHide);\n\t\tRegisterInput();\n\t\tplate.onRefreshVisualTree += RegisterInput;\n      }\n\n\t // foreach (var command in commands)\n\t // {\n\t\t//if (command.enabled && !string.IsNullOrEmpty(command.stateCommand))\n\t\t//  router.RegisterState(command.stateCommand, null);\n\t // }\n\t}\n\n\tpublic override bool CanCatch(object state)\n\t{\n\t  return CanCatch((string)state);\n\t}\n\n    public override bool CanCatch(string state)\n\t{\n\t  if (!enabled)\n\t\treturn false;\n\n\t  foreach (var command in commands)\n\t  {\n\t\tif (!command.enabled)\n\t\t  continue;\n        if(state.Equals(command.stateCommand, System.StringComparison.OrdinalIgnoreCase))\n          return true;\n\t  }\n      return false;\n\t}\n\n\tpublic override bool TryCatch(object state)\n    {\n      return TryCatch((string)state);\n    }\n\n    public override bool TryCatch(string state)\n    {\n      if (!enabled || !gameObject.activeInHierarchy)\n        return false;\n\n      foreach (var command in commands)\n      {\n        if (!command.enabled || command.stateCommand != state)\n          continue;\n\t\tif (command.OnStateEnter != null || command.routerCommand != RouterCommand.None || command.hasCustomState)\n\t\t{\n          if (command.hasCustomState)\n          {\n            if (command.customState.IndexOf(\"http\") == 0)\n\t\t\t  Application.OpenURL(command.customState);\n            else\n\t\t\t  router.TryChangeState(command.customState);\n          }\n          else\n            HandleRouterCommand(command.routerCommand);\n\t\t  command.OnStateEnter?.Invoke();\n\t\t  return true;\n\t\t}\n\t  }\n\n      return false;\n  }\n    void HandleRouterCommand(RouterCommand routerCommand)\n    {\n\t  switch (routerCommand)\n\t  {\n\t\tcase RouterCommand.None:\n\t\t  break;\n\t\tcase RouterCommand.Back:\n\t\t  router.TryGoUpOneState();\n\t\t  break;\n\t\tcase RouterCommand.Previous:\n\t\t  router.TryGoToPreviousState();\n\t\t  break;\n\t\tcase RouterCommand.Exit:\n\t\t  TryExit();\n\t\t  break;\n\t\tcase RouterCommand.Root:\n\t\t  router.ResetState();\n\t\t  break;\n\t\tdefault:\n\t\t  break;\n\t  }\n\t}\n\n    public virtual void TryExit()\n    {\n#if UNITY_EDITOR\n\t  UnityEditor.EditorApplication.isPlaying = false;\n#elif UNITY_WEBPLAYER\n      Application.OpenURL(\"https://github.com/LudiKha/Graphene\");\n#else\n        Application.Quit();\n#endif\n\t}\n\n\tprotected virtual void Plate_OnShow()\n    {\n      enabled = true;\n    }\n\n\tprotected virtual void Plate_OnHide()\n    {\n      enabled = false;\n    }\n\n    private void OnEnable()\n    {\n\t  if (!router)\n\t\treturn;\n\n      router.RegisterInterpreter(this);\n\t // foreach (var command in commands)\n\t // {\n\t\t//if (command.enabled && !string.IsNullOrEmpty(command.stateCommand))\n\t\t//  router.RegisterState(command.stateCommand, null);\n\t // }\n\t}\n\n    private void OnDisable()\n    {\n\t  if (!router)\n\t\treturn;\n\n      router.UnregisterInterpreter(this);\n\t // foreach (var command in commands)\n\t // {\n\t\t//if (!string.IsNullOrEmpty(command.stateCommand))\n\t\t//  router.UnregisterState(command.stateCommand);\n\t // }\n\t}\n\n\tprivate void OnDestroy()\n    {\n\t  router?.InterpreterDestroyed(this);\n\t}\n\n\tbool inputRegistered;\n    void RegisterInput()\n    {\n      if (inputRegistered || !plate || plate.Root == null)\n        return;\n      inputRegistered = true;\n      foreach (var item in inputs)\n\t  {\n\t\tDebug.Log($\"Registering Input {item.routerCommand}\");\n\n\t\tif ((item.input & NavigationInput.NavigationCancel) != 0)\n        {\n\t\t  plate.Root.RegisterCallback<NavigationCancelEvent>(ctx => OnNavigationCancel(item, ctx), item.trickleDown);\n\t\t  Debug.Log($\"Registering Input Event {typeof(NavigationCancelEvent).Name} {item.routerCommand}\");\n\t\t}\n\n\t\tif ((item.input & NavigationInput.NavigationSubmit) != 0)\n\t\t  plate.Root.RegisterCallback<NavigationSubmitEvent>(ctx => OnNavigationSubmit(item, ctx), item.trickleDown);\n\n\t\tif ((item.input & NavigationInput.NavigationMove) != 0)\n\t\t  plate.Root.RegisterCallback<NavigationMoveEvent>(ctx => OnNavigationMove(item, ctx), item.trickleDown);\n\t  }\n    }\n\n\tvoid OnNavigationSubmit(InputOverride item, NavigationSubmitEvent evt)\n\t{\n\t  Debug.Log($\"isActiveAndEnabled {item.routerCommand}\");\n\t  if (!isActiveAndEnabled) return;\n\t  Handle(item, evt);\n\t}\n\tvoid OnNavigationCancel(InputOverride item, NavigationCancelEvent evt)\n\t{\n\t  Debug.Log($\"isActiveAndEnabled {item.routerCommand}\");\n\t  if (!isActiveAndEnabled) return;\n\t  Handle(item, evt);\n\t}\n\tvoid OnNavigationMove(InputOverride item, NavigationMoveEvent evt)\n\t{\n\t  Debug.Log($\"isActiveAndEnabled {item.routerCommand}\");\n\t  if (!isActiveAndEnabled) return;\n      Handle(item, evt);\n\t}\n\n    void Handle(InputOverride item, EventBase evt)\n\t{\n      Debug.Log($\"Handling {item.routerCommand}\");\n\t  if (item.routerCommand != RouterCommand.None || item.OnInput != null)\n\t  {\n\t\tDebug.Log($\"Valid Handling {item.routerCommand}\");\n\n\t\titem.OnInput?.Invoke();\n\t\tHandleRouterCommand(item.routerCommand);\n\t\tevt.PreventDefault();\n\t  }\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Routing/Interpreters/ApplicationStateInterpreter.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 11e217e82870451488cdb1a2928425f1\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/Interpreters/NavigationStateHandler.cs",
    "content": "﻿using UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n\n  /// <summary>\n  /// NavigationStateHandler is a StateInterpreter that listens for \"previous\" and \"next\" commands, typically on a Navbar ButtonGroup, and navigates through the states registered in a Router.\n  /// </summary>\n  // Comment K: This should be split up into (ButtonGroup->StateButtonGroup) & (NavigationStateInterpreter)\n  [RequireComponent(typeof(Plate))]\n  public class NavigationStateHandler : StateInterpreter<string>, IGrapheneInjectable, IGrapheneInitializable\n  {\n    [SerializeField] string previousCommand = \"previous\";\n    [SerializeField] string nextCommand = \"next\";\n\n    Router<string> router;\n    Plate plate;\n\n    ButtonGroup navigationButtonGroup;\n\n    public void Inject(Router<string> router)\n    {\n      this.router = router;\n    }\n\n    public bool Initialized { get; private set; }\n    public void Initialize()\n    {\n      if (Initialized) return;\n      Initialized = true;\n\n      router ??= graphene.Router as Router<string>;\n      router.RegisterInterpreter(this);\n      router.onStateChange += Router_onStateChange;\n\n      plate ??= GetComponent<Plate>();\n      plate.onShow.AddListener(Plate_OnShow);\n      plate.onHide.AddListener(Plate_OnHide);\n\n      Plate_OnHide();\n    }\n\n\n    bool HasElements()\n    {\n      if (navigationButtonGroup != null)\n        return true;\n\n      navigationButtonGroup = plate?.Root?.Q<ButtonGroup>();\n\n      if (navigationButtonGroup == null)\n      {\n        Debug.LogError($\"{GetType().Name} requires a ButtonGroup VisualElement in its static template. Select a template that contains a ButtonGroup element.\", this);\n        return false;\n      }\n\n      //navigationButtonGroup.RegisterValueChangedCallback(evt =>\n      //{\n      //  router.TryChangeState(navigationButtonGroup.items[navigationButtonGroup.value]);\n      //});\n\n      return true;\n    }\n\n    private void Router_onStateChange(string newState)\n    {\n      if (!HasElements())\n        return;\n\n      int i = 0;\n\n      foreach (var state in navigationButtonGroup.items)\n      {\n        if (router.StateIsActive(state))\n        {\n          navigationButtonGroup.SetValueWithoutNotify(i);\n          return;\n        }\n        i++;\n      }\n    }\n\n    public override bool TryCatch(object state)\n    {\n      return TryCatch((string)state);\n    }\n\n    public override bool TryCatch(string state)\n    {\n      if (!enabled || !gameObject.activeInHierarchy)\n        return false;\n\n      else if (state == previousCommand)\n      {\n        navigationButtonGroup.value -= 1;\n        router.TryChangeState(navigationButtonGroup.items[navigationButtonGroup.value]);\n      }\n      else if (state == nextCommand)\n      {\n        navigationButtonGroup.value += 1;\n\t\trouter.TryChangeState(navigationButtonGroup.items[navigationButtonGroup.value]);\n\t  }\n\t  else\n        return false;\n\n      return true;\n    }\n\n\tpublic override bool CanCatch(object state)\n\t{\n\t  return CanCatch((string)state);\n\t}\n\n\tpublic override bool CanCatch(string state)\n\t{\n      if (!enabled || !gameObject.activeInHierarchy)\n        return false;\n      else if (state == previousCommand)\n\t\treturn true;\n\t  else if (state == nextCommand)\n        return true;\n\t  else\n\t\treturn false;\n\t}\n\tinternal void Plate_OnShow()\n    {\n      if (!HasElements())\n        return;\n\n      navigationButtonGroup.SetValueWithoutNotify(0);\n\n      enabled = true;\n    }\n\n    internal void Plate_OnHide()\n    {\n      enabled = false;\n    }\n\n    private void OnEnable()\n    {\n      if (!Initialized)\n        return;\n\n      router.RegisterInterpreter(this);\n    }\n\n    private void OnDisable()\n    {\n      if (!Initialized)\n        return;\n\n      router.UnregisterInterpreter(this);\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Routing/Interpreters/NavigationStateHandler.cs.meta",
    "content": "fileFormatVersion: 2\nguid: db9704c18ef205445b684710a72a91dc\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/Interpreters/StateInterpreter.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  public interface IStateInterpreter \n  {\n    bool CanCatch(object state);\n    bool TryCatch(object state);\n  }\n  public interface IStateInterpreter<TStateType> : IStateInterpreter \n  {\n    bool CanCatch(TStateType state);\n\n\tbool TryCatch(TStateType state);\n  }\n\n  /// <summary>\n  /// A StateInterpreter is a component that can intercept a state change commands  at a Router, consume it, prevent it from propagating further, and optionally trigger other logic.\n  /// </summary>\n  public abstract class StateInterpreter : GrapheneComponent, IStateInterpreter\n  {\n    public abstract bool CanCatch(object state);\n    public abstract bool TryCatch(object state);\n  }\n\n  public abstract class StateInterpreter<TStateType> : StateInterpreter, IStateInterpreter<TStateType>\n  {\n    public abstract bool CanCatch(TStateType state);\n    public abstract bool TryCatch(TStateType state);\n  }\n}"
  },
  {
    "path": "src/Core/Routing/Interpreters/StateInterpreter.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 8142e1a2c5cc51e4495dd52a607b82ae\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/Interpreters.meta",
    "content": "fileFormatVersion: 2\nguid: 7b0d94d5c697e88419f85d034e9257a5\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/Router.cs",
    "content": "﻿\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  using Elements;\n  using Kinstrife.Core.ReflectionHelpers;\n\n  /// <summary>\n  /// A router is responsible for managing application state and navigation between different states or views. It uses a hierarchical state model, allowing for nested states and complex navigation paths.\n  /// </summary>\n  [RequireComponent(typeof(Graphene))]\n  [DefaultExecutionOrder(-100)]\n  [DisallowMultipleComponent]\n  public abstract class Router : GrapheneComponent, IGrapheneDependent, IGrapheneInitializable\n  {\n\t/// <summary>\n\t/// List of interpreters in the hierarchy that can intercept a state change request\n\t/// </summary>\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#endif\n\tprotected List<IStateInterpreter> activeInterpreters = new();\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#endif\n\tprotected List<IStateInterpreter> allInterpreters = new();\n\n\tpublic abstract void InjectIntoHierarchy();\n\tpublic abstract void Initialize();\n\tpublic abstract void BindRouteToContext(BindableElement el, object data);\n\tpublic abstract void BindRoute(Route el, object data);\n\tpublic abstract void TryGoToPreviousState();\n\tpublic abstract void TryGoToNextState();\n\tpublic abstract void TryGoUpOneState();\n\tpublic abstract void ResetState();\n\n\tpublic void RegisterInterpreter(IStateInterpreter stateInterpreter)\n\t{\n\t  if (allInterpreters.Contains(stateInterpreter))\n\t\tallInterpreters.Add(stateInterpreter);\n\n\t  if (!activeInterpreters.Contains(stateInterpreter))\n\t\tactiveInterpreters.Add(stateInterpreter);\n\t}\n\tpublic void UnregisterInterpreter(IStateInterpreter stateInterpreter)\n\t{\n\t  if (activeInterpreters.Contains(stateInterpreter))\n\t\tactiveInterpreters.Remove(stateInterpreter);\n\t}\n\tprivate Object blocker; public bool IsBlocked => blocker;\n\tpublic event System.Action onRoutingBlocked;\n\tpublic event System.Action onRoutingUnblocked;\n\tpublic void TryBlock(Object caller)\n\t{\n\t  if (blocker)\n\t\treturn;\n\t  blocker = caller;\n\t  onRoutingBlocked?.Invoke();\n\t}\n\n\tpublic void TryUnblock(Object caller)\n\t{\n\t  if (!blocker)\n\t\treturn;\n\t  blocker = null;\n\t  onRoutingUnblocked?.Invoke();\n\t}\n\tbool isPrefab => !gameObject.scene.isLoaded;\n\n\tprotected void OnValidate()\n\t{\n\t  if (isPrefab)\n\t\treturn;\n\n\t  if (!graphene)\n\t\tgraphene = GetComponent<Graphene>();\n\t}\n  }\n\n  public abstract class Router<T> : Router, IGrapheneInitializable, IGrapheneLateInitializable\n  {\n\t// state & parent\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector] protected SortedDictionary<T, T> states = new SortedDictionary<T, T>();\n#endif\n\tpublic SortedDictionary<T, T> States => states;\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ValueDropdown(nameof(GetStateKeys))]\n#endif\n\t[SerializeField] public T startingState; public T StartingState => startingState;\n\n\tpublic T CurrentState => activeStates.LastOrDefault();\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#endif\n\tList<T> activeStates = new List<T>();\n\tpublic IReadOnlyList<T> ActiveStates => activeStates;\n\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#endif\n\tList<T> traversedStates = new List<T>();\n\tpublic IReadOnlyList<T> TraversedStates => traversedStates;\n\n\t// Events\n\tpublic event System.Action<T> onStateChange;\n\n#if UNITY_EDITOR && ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector, Sirenix.OdinInspector.ValueDropdown(\"StateKeys\"), Sirenix.OdinInspector.OnValueChanged(\"TryChangeState\")]\n\tT changeState;\n\n#endif\n\tinternal IEnumerable<T> StateKeys => states.Keys;\n\tIEnumerable<T> GetStateKeys()\n\t{\n\t  if (!graphene)\n\t\tgraphene = GetComponent<Graphene>();\n\n\t  foreach (var plate in graphene.Plates)\n\t  {\n\t\tif (!plate)\n\t\t  continue;\n\n\t\tif (plate.StateHandle is StateHandle<T> stateHandle)\n\t\t  yield return stateHandle.StateID;\n\t  }\n\t}\n\n\t#region Initialization\n\n\t// Injects the router into\n\tpublic override void InjectIntoHierarchy()\n\t{\n\t  foreach (var stateHandle in GetComponentsInChildren<StateHandle<T>>())\n\t  {\n\t\tstateHandle.Inject(this);\n\t  }\n\t}\n\n\tpublic bool Initialized { get; private set; }\n\tpublic override void Initialize()\n\t{\n\t  if (Application.isPlaying && Initialized)\n\t\treturn;\n\t  Initialized = true;\n\n\t  traversedStates.Clear();\n\t  states.Clear();\n\t  onStateChange = null;\n\t  RegisterState(startingState, default);\n\t}\n\n\tpublic bool LateInitialized { get; private set; }\n\tpublic void LateInitialize()\n\t{\n\t  if (Application.isPlaying && LateInitialized)\n\t\treturn;\n\n\t  var targetState = StartingState;\n\n\t  if (!ValidState(targetState) || !states.ContainsKey(targetState))\n\t  {\n\t\tforeach (var kvp in states)\n\t\t{\n\t\t  targetState = kvp.Key;\n\t\t  break;\n\t\t}\n\t  }\n\n\t  if (targetState != null)\n\t\tTryChangeState(targetState);\n\t}\n\t#endregion\n\n\t#region Binding\n\tpublic override void BindRouteToContext(BindableElement el, object context)\n\t{\n\t  if (el is Button btn)\n\t  {\n\t\t// Get members\n\t\tList<ValueWithAttribute<RouteAttribute>> members = new List<ValueWithAttribute<RouteAttribute>>();\n\t\tTypeInfoCache.GetMemberValuesWithAttribute(context, members);\n\n\t\tforeach (var item in members)\n\t\t{\n\t\t  T targetState = (T)item.Value;\n\t\t  if (!ValidState(targetState))\n\t\t\tcontinue;\n\n\t\t  btn.clicked += delegate { TryChangeState(targetState); };\n\t\t}\n\t  }\n\t  else if (el is Route routeEl)\n\t  {\n\n\t  }\n\t}\n\n\tpublic override void BindRoute(Route el, object context)\n\t{\n\t  T targetState = default;\n\t  if (el.route is T stateFromEl)\n\t\ttargetState = stateFromEl;\n\n\t  // Does the element have a route defined\n\t  if (ValidState(targetState))\n\t\tBind(targetState);\n\t  // Route is not defined in element -> look in members with Route attributes\n\t  else\n\t  {\n\t\t// Get members\n\t\tList<ValueWithAttribute<RouteAttribute>> members = new List<ValueWithAttribute<RouteAttribute>>();\n\t\tTypeInfoCache.GetMemberValuesWithAttribute(context, members);\n\n\t\tforeach (var item in members)\n\t\t{\n\t\t  if (ValidState((T)item.Value))\n\t\t  {\n\t\t\tBind((T)item.Value);\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t  }\n\n\t  void Bind(T target)\n\t  {\n\t\tif (target is string str)\n\t\t  el.route = str;\n\t\telse\n\t\t{\n\t\t  el.clicked += delegate\n\t\t  {\n\t\t\tTryChangeState(target);\n\t\t  };\n\t\t}\n\t  }\n\t}\n\t#endregion\n\n\t#region Public API\n\tpublic void RegisterState(T state, T parentState)\n\t{\n\t  if (!ValidState(state))\n\t  {\n\t\tDebug.LogError($\"Trying to register invalid route: {state}\", this);\n\t\treturn;\n\t  }\n\n\t  //state = ProcessRouteRequest(state);\n\n\t  if (states.ContainsKey(state))\n\t\treturn;\n\t  else\n\t\tstates.Add(state, parentState);\n\t}\n\n\tpublic void UnregisterState(T state)\n\t{\n\t  if (!ValidState(state))\n\t  {\n\t\tDebug.LogError($\"Trying to register invalid route: {state}\", this);\n\t\treturn;\n\t  }\n\t  if (states.ContainsKey(state))\n\t\tstates.Remove(state);\t  \n\t}\n\n\tpublic void RegisterInterpreter(IStateInterpreter<T> stateInterpreter)\n\t{\n\t  if (!allInterpreters.Contains(stateInterpreter))\n\t\tallInterpreters.Add(stateInterpreter);\n\n\t  if (!activeInterpreters.Contains(stateInterpreter))\n\t\tactiveInterpreters.Add(stateInterpreter);\n\t}\n\n\tpublic void UnregisterInterpreter(IStateInterpreter<T> stateInterpreter)\n\t{\n\t  if (activeInterpreters.Contains(stateInterpreter))\n\t\tactiveInterpreters.Remove(stateInterpreter);\n\t}\n\n\tpublic void InterpreterDestroyed(IStateInterpreter<T> stateInterpreter)\n\t{\n\t  if (allInterpreters.Contains(stateInterpreter))\n\t\tallInterpreters.Remove(stateInterpreter);\n\t  if (activeInterpreters.Contains(stateInterpreter))\n\t\tactiveInterpreters.Remove(stateInterpreter);\n\t}\n\n\tpublic bool ValidateAddress(T state)\n\t{\n\t  // Iterate over interpreters, last added ones (deeper in hierarchy) first, more generic (higher up last)\n\t  for (int i = allInterpreters.Count - 1; i >= 0; i--)\n\t  {\n\t\tvar interpreter = allInterpreters[i];\n\t\tif (interpreter != null && interpreter.CanCatch(state))\n\t\t  return true;\n\t  }\n\t  return AddressExists(state);\n\t}\n\n\tpublic virtual bool TryChangeState(T state)\n\t{\n\t  if (IsBlocked)\n\t\treturn false;\n\n\t  // See if the router\n\t  for (int i = activeInterpreters.Count - 1; i >= 0; i--)\n\t  {\n\t\tvar interpreter = activeInterpreters[i];\n\t\tif (interpreter != null && interpreter.TryCatch(state))\n\t\t  return true;\n\t  }\n\t  //foreach (var interpreter in interpreters)\n\t  //{\n\t  //  if (interpreter != null && interpreter.TryCatch(state))\n\t  //    return true;\n\t  //}\n\n\t  // Only contained keys allowed\n\t  if (!AddressExists(state))\n\t  {\n\t\tDebug.LogError($\"Trying to change state {state}, which isn't registered\", this);\n\t\treturn false;\n\t  }\n\n\t  this.activeStates = GetActiveStateHierarchy(state).ToList();\n\t  UpdateTraversedStates(this.activeStates.Last());\n\n\t  onStateChange?.Invoke(state);\n\t  return true;\n\t}\n\n\tpublic override void TryGoToPreviousState()\n\t{\n\t  if (traversedStates.Count <= 1)\n\t\treturn;\n\n\t  T previousState = traversedStates[traversedStates.IndexOf(CurrentState) - 1];\n\t  TryChangeState(previousState);\n\t}\n\n\tpublic override void TryGoUpOneState()\n\t{\n\t  // Already at root\n\t  if (activeStates.Count <= 1)\n\t  {\n\t\tResetState();\n\t\treturn;\n\t  }\n\n\t  T previousState = activeStates[activeStates.IndexOf(CurrentState) - 1];\n\t  TryChangeState(previousState);\n\t}\n\tpublic override void TryGoToNextState()\n\t{\n\t}\n\n\tpublic override void ResetState()\n\t{\n\t  TryChangeState(startingState);\n\t}\n\n\tpublic bool StateIsActive(T state)\n\t{\n\t  return activeStates.IndexOf(state) != -1;\n\t}\n\n\tpublic bool IsSiblingState(T state, T otherState)\n\t{\n\t  return EqualityComparer<T>.Default.Equals(states[state], states[otherState]);\n\t}\n\n\tpublic bool IsSiblingToCurrentState(T state)\n\t{\n\t  if (!states.TryGetValue(state, out var parent))\n\t\treturn false;\n\t  //Debug.Log($\"{state}@{parent} {CurrentState}@{states[CurrentState]}\");\n\t  return EqualityComparer<T>.Default.Equals(parent, states[CurrentState]);\n\t}\n\n\t#endregion\n\n\t#region Helper Methods\n\tpublic abstract bool ValidState(T state);\n\n\tpublic abstract bool AddressExists(T address);\n\tpublic abstract T[] GetStatesFromAddress(T address);\n\n\tpublic abstract T LeafStateFromAddress(T address);\n\n\tprotected abstract T[] GetActiveStateHierarchy(T state);\n\n\tprotected void UpdateTraversedStates(T newState)\n\t{\n\t  int curIndex = traversedStates.IndexOf(newState);\n\n\t  if (curIndex >= 0) // Already visited this state -> trim the tree\n\t  {\n\t\tint excessStates = traversedStates.Count - (curIndex + 1);\n\t\ttraversedStates.RemoveRange(curIndex + 1, excessStates);\n\t  }\n\t  else\n\t\ttraversedStates.Add(newState);\n\t}\n\n\t#endregion\n  }\n}"
  },
  {
    "path": "src/Core/Routing/Router.cs.meta",
    "content": "fileFormatVersion: 2\nguid: b0572ba23ed7bfb45bccb3d798fa328e\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 3fa61ff64b7c4e64e8b944ba5d308365, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/StateHandle.cs",
    "content": "﻿using UnityEngine;\n\nnamespace Graphene\n{\n  public enum ChildActivationMode\n  {\n    Manual,\n    ShowWithParent,\n    DefaultState\n  }\n\n  [RequireComponent(typeof(Plate))]\n  [DisallowMultipleComponent]\n  public abstract class StateHandle : GrapheneComponent, IGrapheneInjectable, IGrapheneInitializable\n  {\n    public abstract Router Router { get; }\n\n    [SerializeField] protected Plate plate;\n\n    /// <summary>\n    /// This will Show the plate when the parent is activated.\n    /// </summary>\n    [SerializeField] protected ChildActivationMode activationMode = ChildActivationMode.Manual;\n\n    public bool Initialized { get; private set; }\n    public virtual void Initialize()\n    {\n      if (Initialized)\n        return;\n      Initialized = true;\n\n      if (plate || (plate = GetComponent<Plate>()))\n      {\n        plate.onEvaluateState += Plate_onEvaluateState;\n      }\n    }\n\n    protected abstract void Plate_onEvaluateState();\n\n    public virtual bool TryActivate() => throw new System.NotImplementedException();\n  }\n\n  public class StateHandle<T> : StateHandle\n  {\n    [SerializeField] protected T stateID; public virtual T StateID => stateID;\n    [SerializeField] T parentStateID;\n    protected Router<T> router; public override Router Router => router as Router;\n    /// <summary>\n    /// Dependency injection handle\n    /// </summary>\n    /// <param name=\"router\"></param>\n    public void Inject(Router<T> router)\n    {\n      this.router = router;\n    }\n\n    public override void Initialize()\n    {\n      base.Initialize();\n\n      // Get the router in case we didn't inject\n      router ??= graphene.Router as Router<T>;\n\n      // Get parent state\n      StateHandle<T> parentStateHandle = transform.parent.GetComponentInParent<StateHandle<T>>(true);\n      parentStateID = parentStateHandle ? parentStateHandle.StateID : default;\n\n      // Register the state at the router\n      router.RegisterState(stateID, parentStateID);\n      // Subscribe to router state changes\n      router.onStateChange += Router_onStateChange;\n    }\n\n\tprivate void OnDestroy()\n\t{\n      if (router)\n      {\n        router.onStateChange -= Router_onStateChange;\n        router.UnregisterState(stateID);\n      }\n\t}\n\n\tprivate void Router_onStateChange(T address)\n    {\n      if (!router.ValidState(stateID))\n        return;\n\n      bool parentWasTarget = router.StateIsActive(parentStateID) && router.LeafStateFromAddress(address).Equals(parentStateID);\n\n      // Try Change state to this\n      if (activationMode == ChildActivationMode.DefaultState && parentWasTarget)\n      {\n        if (router.TryChangeState(stateID))\n          return;\n      }\n\n      // Check if our state is active\n      if (router.StateIsActive(stateID))\n        plate.Show();\n      else if (activationMode == ChildActivationMode.ShowWithParent && parentWasTarget)\n        plate.Show();\n      else\n        plate.Hide();\n    }\n\n    protected override void Plate_onEvaluateState()\n    {\n      Router_onStateChange(router.CurrentState);\n    }\n\n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.ResponsiveButtonGroup]\n#endif\n    public override bool TryActivate()\n    {\n      return router.TryChangeState(stateID);\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Routing/StateHandle.cs.meta",
    "content": "fileFormatVersion: 2\nguid: ed9a3898638b389418cb68abe6b00ef9\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 724b44860269a9f4abed118244135936, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/StringEnableOnState.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// Component that activates or deactivates a Plate based on the current state of a Router.\n  /// </summary>\n  public class StringEnableOnState : EnableOnState<string>\n  {\n  }\n}"
  },
  {
    "path": "src/Core/Routing/StringEnableOnState.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 67e79be0a9fe43f41bd545143176b6fa\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 724b44860269a9f4abed118244135936, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/StringRouter.cs",
    "content": "﻿\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// A string router uses string addresses to represent states and routes. It manages navigation between different states based on string paths.\n  /// </summary>\n  /// <remarks>Common examples of string-based routing include URL routing in web applications, file path navigation in operating system, such as \"index/settings/video\"</remarks>\n  /// <seealso cref=\"Router\"/>\n  public class StringRouter : Router<string> \n  {\n    const char separator = '/';\n    static char[] separatorArray = new char[] { separator };\n\n    public StringRouter()\n    {\n      startingState = \"app\";\n    }\n\n    public override string[] GetStatesFromAddress(string address)\n    {\n      if (string.IsNullOrWhiteSpace(address))\n        return new string[0];\n\n      var states = address.Split(separatorArray, System.StringSplitOptions.RemoveEmptyEntries);\n      return states;\n    }\n\n    protected override string[] GetActiveStateHierarchy(string routeRequest)\n    {\n      // Trim starting & trailing whitespace\n      routeRequest = routeRequest.Trim();\n\n      // Check if the address is relative, absolute or a leaf\n      int index = routeRequest.IndexOf(separator);\n      // Starting with '/', check for relative address\n      if (index == 0)\n        return AddressFromRelativeState(routeRequest);\n\n      return AddressFromRelativeState(routeRequest);\n    }\n\n\n    public string[] AddressFromRelativeState(string relativeAddress)\n    {\n      string[] states = GetStatesFromAddress(relativeAddress);\n\n\t  // Starting at the leaf, finding n parent states\n\t  //string current = default;\n\t  //for (int i = states.Length - 1; i >= 0; i++)\n\t  //{\n\t  //  current = states[i];\n\t  //  if (this.states.ContainsKey(current))\n\t  //    continue;\n\t  //  else\n\t  //    return false;\n\t  //}\n\t  // Get parent states\n\t  var parentStates = GetParentStatesRecursive(states.First(), states.ToList());\n\n\t  // Add relative states\n\t  parentStates.AddRange(states);\n      return parentStates.Distinct().ToArray();\n    }\n\n    List<string> GetParentStatesRecursive(string state, List<string> list)\n    {\n      // Travel upwards\n      if(states.TryGetValue(state, out string parent))\n      {\n        if (!ValidState(parent))\n          return list;\n\n        list.Insert(0, parent);\n        return GetParentStatesRecursive(parent, list);\n      }\n      // Out of parents\n      return list;\n    }\n\n#if ODIN_INSPECTOR\n    [Sirenix.OdinInspector.Button]\n#endif\n    public bool ChangeState(string path) => base.TryChangeState(path);\n\n    #region Helper Methods\n\n    public override bool ValidState(string state)\n    {\n      return !string.IsNullOrWhiteSpace(state);\n    }\n\n    // Should improve this check\n    public override bool AddressExists(string address)\n    {\n      foreach (var state in GetStatesFromAddress(address))\n      {\n        if (!states.ContainsKey(state))\n          return false;\n      }\n      return true;\n    }\n\n    public override string LeafStateFromAddress(string address)\n    {\n      return GetStatesFromAddress(address).LastOrDefault();\n    }\n    #endregion\n  }\n}"
  },
  {
    "path": "src/Core/Routing/StringRouter.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 905d8383600c12b4dba5c1916e276442\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 3fa61ff64b7c4e64e8b944ba5d308365, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing/StringStateHandle.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// Component that marks a Plate as having a string-based state ID. Adding this to a plate indicates that this Gameobject is capable of being routed to using a string address.\n  /// </summary>\n  public class StringStateHandle : StateHandle<string>\n  {\n    [field: SerializeField] public bool StateFromGameObjectName { get; private set; } = true;\n    public override string StateID => Application.isPlaying ? stateID : StateFromGameObjectName ? GameObjectNameAsStateId() : base.StateID;\n\n    protected override void Awake()\n    {\n      base.Awake();\n      if (StateFromGameObjectName)\n        this.stateID = GameObjectNameAsStateId();\n    }\n\n    string GameObjectNameAsStateId () => gameObject.name.ToLower();\n  }\n}"
  },
  {
    "path": "src/Core/Routing/StringStateHandle.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 7203da84032242f48a61f59888ca04d7\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 724b44860269a9f4abed118244135936, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Routing.meta",
    "content": "fileFormatVersion: 2\nguid: e687d2c6d99c2d4459b39b680325660b\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/AlignItemsOverride.cs",
    "content": "﻿using UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\n#endif\n\nnamespace Graphene\n{\n  [System.Serializable]\n  public sealed class AlignItemsOverride : StyleOverride<Align>\n  {\n    public override void TryApply(VisualElement visualElement)\n    {\n      if (enabled)\n        visualElement.style.alignItems = value;\n      else\n\t    visualElement.style.alignItems = base.Null;\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Styling/AlignItemsOverride.cs.meta",
    "content": "fileFormatVersion: 2\nguid: c9b2129d52baf0043a23668a36c090a4\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/FlexDirectionOverride.cs",
    "content": "﻿using UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\n#endif\n\nnamespace Graphene\n{\n  [System.Serializable]\n  public sealed class FlexDirectionOverride : StyleOverride<FlexDirection>\n  {\n\tpublic override void TryApply(VisualElement visualElement)\n\t{\n\t  if (enabled)\n\t\tvisualElement.style.flexDirection = value;\n\t  else\n\t\tvisualElement.style.flexDirection = base.Null;\n\t}\n  }\n\n  [System.Serializable]\n  public sealed class FlexGrowOverride : StyleOverride<float>\n  {\n\tpublic override void TryApply(VisualElement visualElement)\n\t{\n\t  if (enabled)\n\t\tvisualElement.style.flexGrow = value;\n\t  else\n\t\tvisualElement.style.flexGrow = new StyleFloat(StyleKeyword.Null);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Styling/FlexDirectionOverride.cs.meta",
    "content": "fileFormatVersion: 2\nguid: efa16af0abd783f4aab05bc4bec5f92b\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/InlineStyleOverrides.cs",
    "content": "﻿using UnityEngine;\nusing UnityEngine.UIElements;\n\nusing Sirenix.OdinInspector;\n\nnamespace Graphene\n{\n\n  /// <summary>\n  /// Used to author inline style overrides for an element, applied at runtime.\n  /// </summary>\n  [System.Serializable]\n  public class InlineStyleOverrides\n  {\n\tconst string positionModeRelativeClassNames = \"flex-grow\";\n\tconst string positionModeAbsoluteClassNames = \"absolute fill\";\n\tconst string showHideModeTransitionClassNames = \"fade\";\n\n\t[Tooltip(\"Adds any number of classes to the root element. Separated by space\")]\n\t[SerializeField] protected string addClasses;\n\n\t[SerializeField, EnumToggleButtons, HideLabel] internal PickingMode pickingMode = PickingMode.Position;\n\t[SerializeField, EnumToggleButtons, HideLabel] internal PositionMode positionMode = PositionMode.None;\n\t[SerializeField, EnumToggleButtons, HideLabel] internal ShowHideMode showHideMode = ShowHideMode.Immediate;\n\t[SerializeField, FoldoutGroup(\"Detail\")] FlexGrowOverride flexGrowOverride = new FlexGrowOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] JustifyOverride justifyContent = new JustifyOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] AlignItemsOverride alignItemsOverride = new AlignItemsOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] FlexDirectionOverride flexDirectionOverride = new FlexDirectionOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] WrapOverride wrapOverride = new WrapOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] WidthOverride widthOverride = new WidthOverride();\n\t[SerializeField, FoldoutGroup(\"Detail\")] HeightOverride heightOverride = new HeightOverride();\n\n\tinternal void Apply(VisualElement el)\n\t{\n\t  if (el == null)\n\t\treturn;\n\n\t  el.AddMultipleToClassList(addClasses);\n\n\t  if (positionMode == PositionMode.Relative)\n\t  {\n\t\tel.RemoveMultipleFromClassList(positionModeAbsoluteClassNames);\n\t\tel.AddToClassList(positionModeRelativeClassNames);\n\t  }\n\t  else if (positionMode == PositionMode.Absolute)\n\t  {\n\t\tel.RemoveFromClassList(positionModeRelativeClassNames);\n\t\tel.AddMultipleToClassList(positionModeAbsoluteClassNames);\n\t  }\n\n\t  if (showHideMode == ShowHideMode.Immediate)\n\t  {\n\t\tel.RemoveFromClassList(showHideModeTransitionClassNames);\n\t  }\n\t  else if (showHideMode == ShowHideMode.Transition)\n\t  {\n\t\tel.AddToClassList(showHideModeTransitionClassNames);\n\t\t// When transitioning, we can only position absolutely, as the fadeout process will interfere with routing \n\t\tel.AddMultipleToClassList(positionModeAbsoluteClassNames);\n\t  }\n\n\t  flexGrowOverride.TryApply(el);\n\t  justifyContent.TryApply(el);\n\t  //alignContent.TryApply(Root);\n\t  alignItemsOverride.TryApply(el);\n\t  flexDirectionOverride.TryApply(el);\n\t  wrapOverride.TryApply(el);\n\t  widthOverride.TryApply(el);\n\t  heightOverride.TryApply(el);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Styling/InlineStyleOverrides.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 1997ab07644fc094695caf1539f96170\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/JustifyOverride.cs",
    "content": "﻿using UnityEngine.UIElements;\nusing static UnityEngine.UI.CanvasScaler;\n\n\n#if ODIN_INSPECTOR\n#endif\n\nnamespace Graphene\n{\n  [System.Serializable]\n  public sealed class JustifyOverride : StyleOverride<Justify>\n  {\n    public override void TryApply(VisualElement visualElement)\n    {\n      if (enabled)\n        visualElement.style.justifyContent = value;\n      else\n        visualElement.style.justifyContent = base.Null;\n\t}\n  }\n\n  public abstract class StyleLengthOverride : StyleOverride<float>\n  {\n\tpublic LengthUnit unit = LengthUnit.Percent;\n  }\n\n  [System.Serializable]\n  public sealed class WidthOverride : StyleLengthOverride\n  {\n\tpublic override void TryApply(VisualElement visualElement)\n\t{\n\t  if (enabled)\n\t\tvisualElement.style.width = new Length(value, unit);\n\t  else\n\t\tvisualElement.style.width = new StyleLength(StyleKeyword.Null);// base.Null;\n\t}\n  }\n\n  [System.Serializable]\n  public sealed class HeightOverride : StyleLengthOverride\n  {\n\tpublic override void TryApply(VisualElement visualElement)\n\t{\n\t  if (enabled)\n\t\tvisualElement.style.height = new Length(value, unit);\n\t  else\n\t\tvisualElement.style.height = new StyleLength(StyleKeyword.Null);// base.Null;\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/Styling/JustifyOverride.cs.meta",
    "content": "fileFormatVersion: 2\nguid: cd169699503c46e49b265c0504c2e1e2\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/StyleOverride.cs",
    "content": "﻿using System;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\n#endif\n\nnamespace Graphene\n{\n#if ODIN_INSPECTOR\n  [Sirenix.OdinInspector.Toggle(\"enabled\")]\n#endif\n  [System.Serializable]\n  public abstract class StyleOverride<T>\n    where T : struct, IConvertible\n  {\n    public bool enabled;\n    public T value;\n\n    public abstract void TryApply(VisualElement visualElement);\n\n    public static implicit operator bool (StyleOverride<T> styleOverride) => styleOverride != null && styleOverride.enabled;\n\tprotected StyleEnum<T> Null => new StyleEnum<T>(StyleKeyword.Null);\n\n    public StyleOverride()\n    {\n    }\n\n    public StyleOverride(T value)\n    {\n      this.value = value;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Styling/StyleOverride.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 80d10b93078bca343931a18a83a5027d\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling/WrapOverride.cs",
    "content": "﻿using UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\n#endif\n\nnamespace Graphene\n{\n  [System.Serializable]\n  public sealed class WrapOverride : StyleOverride<Wrap>\n  {\n    public override void TryApply(VisualElement visualElement)\n    {\n      if (enabled)\n        visualElement.style.flexWrap = value;\n\t  else\n\t\tvisualElement.style.flexWrap = base.Null;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Styling/WrapOverride.cs.meta",
    "content": "fileFormatVersion: 2\nguid: abf88d1f7dd2cef43ae6a6fc899bdb04\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Styling.meta",
    "content": "fileFormatVersion: 2\nguid: 7243c0fb90369024fadd0cb72120c84f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Templating/TemplatePreset.cs",
    "content": "﻿\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEngine.Events;\nusing UnityEngine.UIElements;\n\nusing Kinstrife.Core.ReflectionHelpers;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// A list of supported control types that can be rendered by Graphene\n  /// </summary>\n  public enum ControlType\n  {\n    None,\n    Label,\n    Button,\n    Slider,\n    SliderInt,\n    Toggle,\n    Foldout,\n    ListView,\n    ListItem,\n    SelectField,\n    CycleField,\n    TextField,\n    Title,\n    SubTitle,\n    Body,\n    Border,\n    DropdownField,\n    Card,\n    ButtonGroup,\n    SubContext,\n    MinMaxSlider,\n    ProgressBar\n  }\n\n  public interface ICustomControlType\n  {\n    ControlType ControlType { get; }\n  }\n\n  public interface ICustomAddClasses\n  {\n    string ClassesToAdd { get; }\n  }\n\n  public interface ICustomName\n  {\n    string CustomName { get; }\n  }\n\n  public interface ISubContext\n  {\n\n  }\n\n  [Serializable] public class ControlVisualTreeAssetMapping : SerializableDictionary<ControlType, VisualTreeAsset> { }\n\n  /// <summary>\n  /// A TemplatePreset allows you to define a set of VisualTreeAssets to be used as templates for different <see cref=\"ControlType\">.\n  /// </summary>\n  [CreateAssetMenu(menuName = \"Graphene/Templating/ComponentTemplates\")]\n  public class TemplatePreset : ScriptableObject\n  {\n    [SerializeField] TemplatePreset parent; public TemplatePreset Parent => parent;\n\n    [SerializeField] ControlVisualTreeAssetMapping data = new ControlVisualTreeAssetMapping();\n\n\tpublic static ControlType ResolveControlType(object data, bool isPrimitiveContext, DrawAttribute drawAttribute = null)\n\t{\n\t  ControlType controlType = ControlType.None;\n\t  // Try get from attributes\n\t  // No member draw attribute -> try get ControlType from class attribute\n\t  if (!isPrimitiveContext && (drawAttribute == null || drawAttribute.controlType == ControlType.None))\n\t  {\n\t\tvar info = TypeInfoCache.GetExtendedTypeInfo(data.GetType());\n\t\tif (info.HasTypeAttribute<DrawAttribute>())\n\t\t  drawAttribute = info.GetTypeAttribute<DrawAttribute>();\n\t  }\n\t  // Set ControlType from attribute (if present)\n\t  if (drawAttribute != null && drawAttribute.controlType != ControlType.None)\n\t\tcontrolType = drawAttribute.controlType;\n\t  // Didn't find in attribute\n\t  else\n\t\tcontrolType = GetControlTypeFromData(data, isPrimitiveContext);\n\n      return controlType;\n\t}\n\n\tpublic static ControlType GetControlTypeFromData(object data, bool isPrimitiveContext)\n    {\n      if (data is bool)\n        return ControlType.Toggle;\n      else if (data is float)\n        return ControlType.Slider;\n      else if (data is int)\n        return ControlType.SliderInt;\n      else if (data is string)\n        return ControlType.Label;\n      else if (data is System.Action || data is UnityEvent)\n        return ControlType.Button;\n\t  else if (data is Vector2)\n\t\treturn ControlType.MinMaxSlider;\n\t  else if (data is IList)\n        return ControlType.ListView;\n      else if (data is Enum)\n        return ControlType.DropdownField;\n      else if (!isPrimitiveContext) // Use nested scope\n        return ControlType.SubContext;\n      return ControlType.None;\n    }\n\n    public bool TryGetTemplateAsset(ControlType controlType, out VisualTreeAsset visualTreeAsset)\n    {\n      if (data.TryGetValue(controlType, out visualTreeAsset))\n        return true;\n      else if (parent)\n        return parent.TryGetTemplateAsset(controlType, out visualTreeAsset);\n      else\n        Debug.LogError($\"Didn't find template for control {controlType}\", this);\n\n      return false;\n    }\n  }\n}"
  },
  {
    "path": "src/Core/Templating/TemplatePreset.cs.meta",
    "content": "fileFormatVersion: 2\nguid: fecf578bfcf34544fb992fb36cc63ce3\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: d17647a721ae9b6418d60aabba5be55f, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/Templating.meta",
    "content": "fileFormatVersion: 2\nguid: 18f3fea75a8c9b24da7bb9ab083e531d\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/View/SerializedView.cs",
    "content": "﻿using UnityEngine;\nusing UnityEngine.UIElements;\n\n#if ODIN_INSPECTOR\nusing Sirenix.OdinInspector;\n#endif\n\nnamespace Graphene\n{\n  /// <summary>\n  /// Used to serialize a reference of a <see cref=\"View\"/> in a <see cref=\"Plate\"/>, including style overrides to apply at runtime.\n  /// </summary>\n  [System.Serializable, Toggle(nameof(Enabled))]\n  public struct SerializedView\n  {\n    [SerializeField] public bool Enabled;\n    [ReadOnly, SerializeField] public string Id;\n    [SerializeField] InlineStyleOverrides StyleOverrides;\n\n    public SerializedView(string id)\n    {\n      Enabled = false;\n      this.Id = id;\n\t  StyleOverrides = new InlineStyleOverrides();\n\t}\n\n\tpublic void Apply(VisualElement el)\n    {\n      StyleOverrides.Apply(el);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/View/SerializedView.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 7b1deb9f87a8f914cac2440ff9be962a\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/View/ViewHandle.cs",
    "content": "﻿using Graphene.Elements;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene\n{\n  /// <summary>\n  /// Represents a reference to a <see cref=\"View\"/> within a <see cref=\"Plate\"/>.\n  /// Used to resolve and access views by ID, and to manage their association with plates.\n  /// </summary>\n  [Serializable]\n  public class ViewRef\n  {\n\t/// <summary>\n\t/// The default selector used if no ID is specified.\n\t/// </summary>\n\tpublic readonly string defaultSelector;\n\n\t[SerializeField, HideInInspector]\n\tPlate plate;\n\n\t#region ValueDropdownAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ValueDropdown(\"GetViewsFromVisualTreeAsset\")]\n#endif\n\t#endregion\n\t/// <summary>\n\t/// The ID of the view to reference.\n\t/// </summary>\n\t[SerializeField]\n\tprotected string id;\n\t/// <summary>\n\t/// Gets the ID of the referenced view.\n\t/// </summary>\n\tpublic string Id => id;\n\n\t/// <summary>\n\t/// The resolved <see cref=\"View\"/> instance.\n\t/// </summary>\n\tpublic View view;\n\n\t#region ShowInInspectorAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ShowInInspector]\n#endif\n\t#endregion\n\t/// <summary>\n\t/// Indicates whether the view has been successfully resolved.\n\t/// </summary>\n\tpublic bool initialized => view != null;\n\n\t/// <summary>\n\t/// Returns a list of available view IDs from the associated plate's visual tree asset.\n\t/// </summary>\n\tpublic IEnumerable<string> GetViewsFromVisualTreeAsset()\n\t{\n\t  if (!plate)\n\t\treturn Enumerable.Empty<string>();\n\t  else if (plate.viewIds != null && plate.viewIds.Count > 0)\n\t\treturn plate.viewIds;\n\t  else\n\t\treturn new List<string> { \"\" };\n\t}\n\n\t/// <summary>\n\t/// Constructs a new <see cref=\"ViewRef\"/> with the specified default ID.\n\t/// </summary>\n\t/// <param name=\"defaultId\">The default view selector.</param>\n\tpublic ViewRef(string defaultId)\n\t{\n\t  this.defaultSelector = defaultId;\n\t}\n\n\t/// <summary>\n\t/// Implicitly converts a <see cref=\"ViewRef\"/> to a boolean indicating if it is valid and has a non-empty ID.\n\t/// </summary>\n\tpublic static implicit operator bool(ViewRef viewRef) => viewRef != null && !string.IsNullOrWhiteSpace(viewRef.id);\n\n\t/// <summary>\n\t/// Resolves the view reference using the specified plate, updating the <see cref=\"view\"/> field.\n\t/// </summary>\n\t/// <param name=\"plate\">The plate to resolve the view from.</param>\n\tpublic void ResolveView(Plate plate)\n\t{\n\t  this.plate = plate;\n\n\t  // Use default selector if ID is not set\n\t  if (string.IsNullOrWhiteSpace(id))\n\t\tid = defaultSelector;\n\n\t  view = plate.GetViewById(id);\n\t}\n\n\t/// <summary>\n\t/// Sets the plate associated with this view reference.\n\t/// </summary>\n\t/// <param name=\"plate\">The plate to associate.</param>\n\tpublic void SetPlate(Plate plate)\n\t{\n\t  this.plate = plate;\n\t}\n\n\t/// <summary>\n\t/// Clears the parent plate and view reference.\n\t/// </summary>\n\tpublic void NoParent()\n\t{\n\t  this.view = null;\n\t  this.plate = null;\n\t}\n  }\n\n  [System.Obsolete(\"Use ViewRef instead\")]\n\n  /// <summary>\n  /// Component for referencing a view by ID within a plate.\n  /// Used to select and interact with views in the UI hierarchy.\n  /// </summary>\n  [DisallowMultipleComponent]\n  public class ViewHandle : MonoBehaviour\n  {\n\t#region ButtonAttribute\n#if ODIN_INSPECTOR\n\t[Sirenix.OdinInspector.ValueDropdown(\"GetViewsFromVisualTreeAsset\")]\n#endif\n\t#endregion\n\t/// <summary>\n\t/// The ID of the view to reference.\n\t/// </summary>\n\t[SerializeField]\n\tprotected string id;\n\t/// <summary>\n\t/// Gets the ID of the referenced view.\n\t/// </summary>\n\tpublic string Id => id;\n\n\t/// <summary>\n\t/// Returns a list of available view IDs from the parent plate's visual tree asset.\n\t/// </summary>\n\tpublic IEnumerable<string> GetViewsFromVisualTreeAsset()\n\t{\n\t  Plate plate = GetComponent<Plate>();\n\t  var root = plate.transform.parent.GetComponent<Plate>().VisualTreeAsset.CloneTree();\n\t  // Get views\n\t  return root?.Query<View>().ToList().Select(v => v.id);\n\t}\n  }\n}"
  },
  {
    "path": "src/Core/View/ViewHandle.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 6ebff670864e5cd4d87ede0a925c3a68\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {fileID: 2800000, guid: 779432a5e218db5438ca646cb9970341, type: 3}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core/View.meta",
    "content": "fileFormatVersion: 2\nguid: 6f3253f7286180045afee8f02c25b53b\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Core.meta",
    "content": "fileFormatVersion: 2\nguid: 3e44d045c365d3345a3368115362a35f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Editor/CustomDictionaryPropertyDrawers.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEditor;\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\nnamespace Graphene.Editor\n{\n  [CustomPropertyDrawer(typeof(SerializableDictionary<ControlType, VisualTreeAsset>))]\n  [CustomPropertyDrawer(typeof(ControlVisualTreeAssetMapping))]\n  public class AnySerializableDictionaryStoragePropertyDrawer : SerializableDictionaryPropertyDrawer { }\n}"
  },
  {
    "path": "src/Editor/CustomDictionaryPropertyDrawers.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 46100f4664e80f14dab215b9e06a4763\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Editor/Graphene.Editor.asmdef",
    "content": "{\n    \"name\": \"Graphene.Editor\",\n    \"references\": [\n        \"GUID:5182bff32a626fa409ad510e8c679a69\",\n        \"GUID:a9d2a6efafebd5e47b01684a70d95cc5\",\n        \"GUID:dd4a8158362adf24fa2558eb7adb4b12\",\n        \"GUID:478a2357cc57436488a56e564b08d223\"\n    ],\n    \"includePlatforms\": [\n        \"Editor\"\n    ],\n    \"excludePlatforms\": [],\n    \"allowUnsafeCode\": false,\n    \"overrideReferences\": false,\n    \"precompiledReferences\": [],\n    \"autoReferenced\": true,\n    \"defineConstraints\": [],\n    \"versionDefines\": [],\n    \"noEngineReferences\": false\n}"
  },
  {
    "path": "src/Editor/Graphene.Editor.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: 979c01504af7b11408532aa7eaf930d2\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Editor/GrapheneEditorUtilities.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEditor;\nusing Unity.EditorCoroutines.Editor;\n\nnamespace Graphene\n{\n  internal static class GrapheneEditorUtilities\n  {\n    public const string uuid = \"com.graphene.core\";\n    public const string gitUrlCore = \"https://github.com/LudiKha/Graphene.git?path=/src\";\n    public const string gitUrlComponents = \"https://github.com/LudiKha/Graphene-Components.git?path=/src\";\n\n    public class PackageRequest\n    {\n    }\n\n    [MenuItem(\"Window/Graphene/Check for updates/Graphene Core\")]\n    static void CheckForUpdates()\n    {\n      var owner = new PackageRequest();\n      EditorCoroutineUtility.StartCoroutine(MonitorPackageUpdate(owner, gitUrlCore, \"Graphene Core\"), owner);\n    }\n    [MenuItem(\"Window/Graphene/Check for updates/Graphene Components\")]\n    static void CheckForUpdatesComponents()\n    {\n      var owner = new PackageRequest();\n      EditorCoroutineUtility.StartCoroutine(MonitorPackageUpdate(owner, gitUrlComponents, \"Graphene Components\"), owner);\n    }\n    static IEnumerator MonitorPackageUpdate(PackageRequest owner, string gitUrl, string packageName)\n    {\n      Debug.Log($\"Checking for updates for {packageName}...\");\n\n      var request = UnityEditor.PackageManager.Client.Add(gitUrl);\n\n      while (!request.IsCompleted)\n      {\n        yield return null;\n      }\n\n      if(request.Error!= null)\n        Debug.LogError($\"Error code {request.Error.message}: {request.Error.message}\");\n\n      Debug.Log($\"Latest version: {request.Result.version}\");\n      yield break;\n    }\n  }\n}"
  },
  {
    "path": "src/Editor/GrapheneEditorUtilities.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 49bdd157a434fa944a0149e48ef81046\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Editor/ViewSelectorStringDrawer.cs",
    "content": "\n//#if ODIN_INSPECTOR\n//using System.Collections;\n//using System.Collections.Generic;\n//using UnityEngine;\n//using UnityEditor;\n//using Sirenix.OdinInspector.Editor;\n//using Sirenix.Utilities.Editor;\n\n//namespace Graphene.Editor\n//{\n//  public class ViewRefDrawer : OdinValueDrawer<ViewRef>\n//  {\n//    protected override void DrawPropertyLayout(GUIContent label)\n//    {\n//      base.DrawPropertyLayout(label);\n//      return;\n//      var value = this.ValueEntry.SmartValue;\n//      SirenixEditorGUI.BeginBox();\n//      SirenixEditorGUI.BeginBoxHeader();\n//      SirenixEditorGUI.EndBoxHeader();\n\n//      SirenixEditorGUI.BeginVerticalMenuList(\"Views\");\n//      SirenixEditorGUI.BeginListItem();\n//      //int selected = SirenixEditorFields.Dropdown(0, value.pl)\n//      SirenixEditorGUI.EndListItem();\n//      SirenixEditorGUI.EndVerticalMenuList();\n//      SirenixEditorGUI.EndBox();\n      \n//    }\n//  }\n//    }\n//#endif"
  },
  {
    "path": "src/Editor/ViewSelectorStringDrawer.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 5ae8cf884934ad44b9796dc46b098012\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Editor.meta",
    "content": "fileFormatVersion: 2\nguid: 199242626b096f64a80e9a0629ed98dc\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Graphene.Core.asmdef",
    "content": "{\n    \"name\": \"Graphene.Core\",\n    \"rootNamespace\": \"\",\n    \"references\": [\n        \"GUID:a9d2a6efafebd5e47b01684a70d95cc5\",\n        \"GUID:0b2cc0b34570a2247ae360ab103f281d\",\n        \"GUID:67b49b9f9092f8a46950e54a0344fbb8\"\n    ],\n    \"includePlatforms\": [],\n    \"excludePlatforms\": [],\n    \"allowUnsafeCode\": false,\n    \"overrideReferences\": false,\n    \"precompiledReferences\": [],\n    \"autoReferenced\": true,\n    \"defineConstraints\": [],\n    \"versionDefines\": [],\n    \"noEngineReferences\": false\n}"
  },
  {
    "path": "src/Graphene.Core.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: 5182bff32a626fa409ad510e8c679a69\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/DragManipulator/DragManipulator.cs",
    "content": "/* Original code[1] Copyright (c) 2022 Shane Celis[2]\n   Licensed under the MIT License[3]\n\n   [1]: https://gist.github.com/shanecelis/b6fb3fe8ed5356be1a3aeeb9e7d2c145\n   [2]: https://twitter.com/shanecelis\n   [3]: https://opensource.org/licenses/MIT\n*/\n\nusing UnityEngine;\nusing UnityEngine.UIElements;\n\n/** This manipulator makes a visual element draggable at runtime. Unity's\n    UIToolkit also has a [drag-and-drop system][1] but it is only appropriate\n    for use within its editor.\n\n    ## Usage\n\n    ```\n    element.AddManipulator(new DragManipulator());\n    element.RegisterCallback<DropEvent>(evt =>\n      Debug.Log($\"{evt.target} dropped on {evt.droppable}\");\n    ```\n\n    OR\n\n    ```\n    foreach (var element in root.Query(className: \"draggable\").Build()) {\n      element.AddManipulator(new DragManipulator());\n    }\n    root.RegisterCallback<DropEvent>(evt =>\n      Debug.Log($\"{evt.target} dropped on {evt.droppable}\");\n    ```\n\n    ### Styling\n\n    When dragging, one should be able to style the participating elements.\n    Coupled with Unity Style Sheet (USS) transitions, one can provide automatic\n    tweens.\n\n    | USS Selectors        | Description                                   |\n    |----------------------+-----------------------------------------------|\n    | .draggable           | Present on any element with a DragManipulator |\n    | .draggable--dragging | Present while dragging                        |\n    | .draggable--can-drop | Present while dragging over a droppable       |\n    | .droppable           | Identifies a droppable element (editable)     |\n    | .droppable--can-drop | Present while a draggable is hovering         |\n\n    A custom property also allows one to disable dragging via the style sheet.\n\n    | USS Properties      | Description                                    |\n    |---------------------+------------------------------------------------|\n    | --draggable-enabled | When set to false, dragging is disabled        |\n\n    ## Requirements\n\n    - Unity 2020.3 or later\n\n    ## Dragging\n\n    Clicking and dragging on the draggable element will cause it to move. The\n    USS class \"draggable--dragging\" will be present during\n    the duration.\n\n    ### Remove USS Class on Drag\n\n    One can remove a USS class while dragging by setting the following\n    parameter at initialization:\n\n    ```\n    var dragger = new DragManipulator { removeClassOnDrag = \"transitions\" };\n    ```\n\n    Usage: If one has translation USS transitions set, dragging may look wrong\n    and may not be smooth. Placing transitions into a special class and removing\n    that class during the drag fixed that problem.\n\n    ## Dropping\n\n    Elements that have a \"droppable\" USS class will be considered droppable.\n    When dragging and hovering over a droppable element, the USS class\n    \"droppable--can-drop\" will be added; the draggable element will have\n    \"draggable--can-drop\" added to it.\n\n    If the draggable element is dropped on a non-droppable element, the\n    draggable element's position is reset. It is suggested that one turn on USS\n    transitions if one wants the draggable to tween back into its original\n    place.\n\n    ### Distinct Droppables\n\n    If one has distinct droppable objects, one set the `droppableId` on the\n    `DragManipulator` to something other than \"droppable\".\n\n    ```\n    var dragger = new DragManipulator { droppableId = \"discard-pile\" };\n    ```\n\n    ## Handling Events\n\n    When a draggable element is released on a droppable element or its child, a\n    `DropEvent` is emitted. The position of the element is not reset\n    automatically in that case. If the dropped object is supposed to return to\n    its original position, one ought to do that in the callback code.\n\n    ```\n    void OnDrag(DropEvent evt) {\n      evt.target.transform.position = Vector3.zero;\n      // OR\n      // evt.dragger.ResetPosition();\n    }\n    ```\n\n    ## Limitations\n\n    This manipulator changes the `transform.position` of the target element\n    while dragging. If one's styling is making use of that, the behavior is\n    undefined.\n\n    ## Notes\n\n    The drop event bubbles up, so the callback can be placed on the parent or\n    root element.\n\n    Acknowledgments to Crayz[2] and Stacey[3] for their inspiring code.\n\n    [1]: https://forum.unity.com/threads/visualelement-drag-and-drop-during-runtime.930000/#post-6373881\n    [2]: https://forum.unity.com/threads/creating-draggable-visualelement-and-clamping-it-to-screen.1017715/\n    [3]: https://gamedev-resources.com/create-an-in-game-inventory-ui-with-ui-toolkit/\n*/\npublic class DragManipulator : IManipulator {\n\n  private VisualElement _target;\n\n  public VisualElement target {\n    get => _target;\n    set {\n      if (_target != null) {\n        if (_target == value)\n          return;\n        _target.UnregisterCallback<PointerDownEvent>(DragBegin);\n        _target.UnregisterCallback<PointerUpEvent>(DragEnd);\n        _target.UnregisterCallback<PointerMoveEvent>(PointerMove);\n        _target.UnregisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);\n        _target.RemoveFromClassList(\"draggable\");\n        lastDroppable?.RemoveFromClassList(\"droppable--can-drop\");\n        lastDroppable = null;\n      }\n      _target = value;\n      \n      _target.RegisterCallback<PointerDownEvent>(DragBegin);\n      _target.RegisterCallback<PointerUpEvent>(DragEnd);\n      _target.RegisterCallback<PointerMoveEvent>(PointerMove);\n      _target.RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);\n      _target.AddToClassList(\"draggable\");\n    }\n  }\n  protected static readonly CustomStyleProperty<bool> draggableEnabledProperty\n    = new CustomStyleProperty<bool>(\"--draggable-enabled\");\n  protected Vector3 offset;\n  private bool isDragging = false;\n  private VisualElement lastDroppable = null;\n  private string _droppableId = \"droppable\";\n  /** This is the USS class that is determines whether the target can be dropped\n      on it. It is \"droppable\" by default. */\n  public string droppableId {\n    get => _droppableId;\n    init => _droppableId = value;\n  }\n  /** This manipulator can be disabled. */\n  public bool enabled { get; set; } = true;\n  private PickingMode lastPickingMode;\n  private string _removeClassOnDrag;\n  /** Optional. Remove the given class from the target element during the drag.\n      If removed, replace when drag ends. */\n  public string removeClassOnDrag {\n    get => _removeClassOnDrag;\n    init => _removeClassOnDrag = value;\n  }\n  private bool removedClass = false;\n\n  private void OnCustomStyleResolved(CustomStyleResolvedEvent e) {\n    if (e.customStyle.TryGetValue(draggableEnabledProperty, out bool got))\n      enabled = got;\n  }\n\n  private void DragBegin(PointerDownEvent ev) {\n    if (! enabled)\n      return;\n    target.AddToClassList(\"draggable--dragging\");\n\n    if (removeClassOnDrag != null) {\n      removedClass = target.ClassListContains(removeClassOnDrag);\n      if (removedClass)\n        target.RemoveFromClassList(removeClassOnDrag);\n    }\n\n    lastPickingMode = target.pickingMode;\n    target.pickingMode = PickingMode.Ignore;\n    isDragging = true;\n    offset = ev.localPosition;\n    target.CapturePointer(ev.pointerId);\n  }\n\n  private void DragEnd(IPointerEvent ev) {\n    if (! isDragging)\n      return;\n    VisualElement droppable;\n    bool canDrop = CanDrop(ev.position, out droppable);\n    //Debug.Log($\"droppable {droppable}\");\n    if (canDrop)\n      droppable.RemoveFromClassList(\"droppable--can-drop\");\n    target.RemoveFromClassList(\"draggable--dragging\");\n    target.RemoveFromClassList(\"draggable--can-drop\");\n    lastDroppable?.RemoveFromClassList(\"droppable--can-drop\");\n    lastDroppable = null;\n    target.ReleasePointer(ev.pointerId);\n    target.pickingMode = lastPickingMode;\n    isDragging = false;\n    if (canDrop)\n      Drop(droppable);\n    else\n      ResetPosition();\n    if (removeClassOnDrag != null && removedClass)\n      target.AddToClassList(removeClassOnDrag);\n  }\n\n  protected virtual void Drop(VisualElement droppable) {\n    var e = DropEvent.GetPooled(this, droppable);\n    e.target = this.target;\n    // We send the event one tick later so that our changes to the class list\n    // will take effect.\n    this.target.schedule.Execute(() => e.target.SendEvent(e));\n  }\n\n  /** Change parent while preserving position via `transform.position`.\n\n      Usage: While dragging-and-dropping an element, if the dropped element were\n      to change its parent in the hierarchy, but preserve its position on\n      screen, which can be done with `transform.position`. Then one can lerp\n      that position to zero for a nice clean transition.\n\n      Notes: The algorithm isn't difficult. It's find position wrt new parent,\n      zero out the `transform.position`, add it to the parent, find position wrt\n      new parent, set `transform.position` such that its screen position will be\n      the same as before.\n\n      The tricky part is when you add this element to a newParent, you can't\n      query for its position (at least not in a way I could find). You have to\n      wait a beat. Then whatever was necessary to update will update.\n   */\n  public static IVisualElementScheduledItem ChangeParent(VisualElement target,\n                                                         VisualElement newParent) {\n    var position_parent = target.ChangeCoordinatesTo(newParent, Vector2.zero);\n    target.RemoveFromHierarchy();\n    target.transform.position = Vector3.zero;\n    newParent.Add(target);\n    // ChangeCoordinatesTo will not be correct unless you wait a tick. #hardwon\n    // target.transform.position = position_parent - target.ChangeCoordinatesTo(newParent,\n    //                                                                      Vector2.zero);\n    return target.schedule.Execute(() => {\n      var newPosition = position_parent - target.ChangeCoordinatesTo(newParent,\n                                                                     Vector2.zero);\n      target.RemoveFromHierarchy();\n      target.transform.position = newPosition;\n\n      newParent.Add(target);\n    });\n  }\n\n  /** Reset the target's position to zero.\n\n      Note: Schedules the change so that the USS classes will be restored when\n      run. (Helps when a \"transitions\" USS class is used.)\n   */\n  public virtual void ResetPosition() {\n    target.transform.position = Vector3.zero;\n  }\n\n  protected virtual bool CanDrop(Vector3 position, out VisualElement droppable) {\n    droppable = target.panel.Pick(position);\n    var element = droppable;\n    // Walk up parent elements to see if any are droppable.\n    while (element != null && ! element.ClassListContains(droppableId))\n      element = element.parent;\n    if (element != null) {\n      droppable = element;\n      return true;\n    }\n    return false;\n  }\n\n  private void PointerMove(PointerMoveEvent ev) {\n    if (! isDragging)\n      return;\n    if (! enabled) {\n      DragEnd(ev);\n      return;\n    }\n    Vector3 delta = ev.localPosition - (Vector3) offset;\n    target.transform.position += delta;\n    if (CanDrop(ev.position, out var droppable)) {\n      target.AddToClassList(\"draggable--can-drop\");\n      droppable.AddToClassList(\"droppable--can-drop\");\n      if (lastDroppable != droppable)\n        lastDroppable?.RemoveFromClassList(\"droppable--can-drop\");\n      lastDroppable = droppable;\n    } else {\n      target.RemoveFromClassList(\"draggable--can-drop\");\n      lastDroppable?.RemoveFromClassList(\"droppable--can-drop\");\n      lastDroppable = null;\n    }\n  }\n}\n\n/** This event represents a runtime drag and drop event. */\npublic class DropEvent : EventBase<DropEvent> {\n  public DragManipulator dragger { get; protected set; }\n  public VisualElement droppable { get; protected set; }\n\n  protected override void Init() {\n    base.Init();\n    this.LocalInit();\n  }\n\n  private void LocalInit() {\n    this.bubbles = true;\n    this.tricklesDown = false;\n  }\n\n  public static DropEvent GetPooled(DragManipulator dragger, VisualElement droppable) {\n    DropEvent pooled = EventBase<DropEvent>.GetPooled();\n    pooled.dragger = dragger;\n    pooled.droppable = droppable;\n    return pooled;\n  }\n\n  public DropEvent() => this.LocalInit();\n}\n\n// This hack allows us to use init properties in earlier versions of Unity.\n#if UNITY_5_3_OR_NEWER && ! UNITY_2021_OR_NEWER\n// https://stackoverflow.com/a/62656145\nnamespace System.Runtime.CompilerServices {\n  using System.ComponentModel;\n  [EditorBrowsable(EditorBrowsableState.Never)]\n  internal class IsExternalInit{}\n}\n#endif\n"
  },
  {
    "path": "src/Lib/DragManipulator/DragManipulator.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 4ed0bf7b15ca4544e9c8ca59068db3c1\nMonoImporter:\n  externalObjects: {}\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/DragManipulator.meta",
    "content": "fileFormatVersion: 2\nguid: 04fced0da9d8a4c4bb8d1a6e2af0ecff\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary/Editor/SerializableDictionary.Editor.asmdef",
    "content": "{\n    \"name\": \"SerializableDictionary.Editor\",\n    \"references\": [\n        \"GUID:a9d2a6efafebd5e47b01684a70d95cc5\"\n    ],\n    \"includePlatforms\": [\n        \"Editor\"\n    ],\n    \"excludePlatforms\": [],\n    \"allowUnsafeCode\": false,\n    \"overrideReferences\": false,\n    \"precompiledReferences\": [],\n    \"autoReferenced\": true,\n    \"defineConstraints\": [],\n    \"versionDefines\": [],\n    \"noEngineReferences\": false\n}"
  },
  {
    "path": "src/Lib/SerializableDictionary/Editor/SerializableDictionary.Editor.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: dd4a8158362adf24fa2558eb7adb4b12\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs",
    "content": "﻿using System.Collections;\nusing System.Collections.Generic;\nusing UnityEngine;\nusing UnityEditor;\nusing System.Reflection;\nusing System;\n\npublic class SerializableDictionaryPropertyDrawer : PropertyDrawer\n{\n\tconst string KeysFieldName = \"m_keys\";\n\tconst string ValuesFieldName = \"m_values\";\n\tprotected const float IndentWidth = 15f;\n\n\tstatic GUIContent s_iconPlus = IconContent (\"Toolbar Plus\", \"Add entry\");\n\tstatic GUIContent s_iconMinus = IconContent (\"Toolbar Minus\", \"Remove entry\");\n\tstatic GUIContent s_warningIconConflict = IconContent (\"console.warnicon.sml\", \"Conflicting key, this entry will be lost\");\n\tstatic GUIContent s_warningIconOther = IconContent (\"console.infoicon.sml\", \"Conflicting key\");\n\tstatic GUIContent s_warningIconNull = IconContent (\"console.warnicon.sml\", \"Null key, this entry will be lost\");\n\tstatic GUIStyle s_buttonStyle = GUIStyle.none;\n\tstatic GUIContent s_tempContent = new GUIContent();\n\n\n\tclass ConflictState\n\t{\n\t\tpublic object conflictKey = null;\n\t\tpublic object conflictValue = null;\n\t\tpublic int conflictIndex = -1 ;\n\t\tpublic int conflictOtherIndex = -1 ;\n\t\tpublic bool conflictKeyPropertyExpanded = false;\n\t\tpublic bool conflictValuePropertyExpanded = false;\n\t\tpublic float conflictLineHeight = 0f;\n\t}\n\n\tstruct PropertyIdentity\n\t{\n\t\tpublic PropertyIdentity(SerializedProperty property)\n\t\t{\n\t\t\tthis.instance = property.serializedObject.targetObject;\n\t\t\tthis.propertyPath = property.propertyPath;\n\t\t}\n\n\t\tpublic UnityEngine.Object instance;\n\t\tpublic string propertyPath;\n\t}\n\n\tstatic Dictionary<PropertyIdentity, ConflictState> s_conflictStateDict = new Dictionary<PropertyIdentity, ConflictState>();\n\n\tenum Action\n\t{\n\t\tNone,\n\t\tAdd,\n\t\tRemove\n\t}\n\n\tpublic override void OnGUI(Rect position, SerializedProperty property, GUIContent label)\n\t{\n\t\tlabel = EditorGUI.BeginProperty(position, label, property);\n\n\t\tAction buttonAction = Action.None;\n\t\tint buttonActionIndex = 0;\n\n\t\tvar keyArrayProperty = property.FindPropertyRelative(KeysFieldName);\n\t\tvar valueArrayProperty = property.FindPropertyRelative(ValuesFieldName);\n\n\t\tConflictState conflictState = GetConflictState(property);\n\n\t\tif(conflictState.conflictIndex != -1)\n\t\t{\n\t\t\tkeyArrayProperty.InsertArrayElementAtIndex(conflictState.conflictIndex);\n\t\t\tvar keyProperty = keyArrayProperty.GetArrayElementAtIndex(conflictState.conflictIndex);\n\t\t\tSetPropertyValue(keyProperty, conflictState.conflictKey);\n\t\t\tkeyProperty.isExpanded = conflictState.conflictKeyPropertyExpanded;\n\n\t\t\tvalueArrayProperty.InsertArrayElementAtIndex(conflictState.conflictIndex);\n\t\t\tvar valueProperty = valueArrayProperty.GetArrayElementAtIndex(conflictState.conflictIndex);\n\t\t\tSetPropertyValue(valueProperty, conflictState.conflictValue);\n\t\t\tvalueProperty.isExpanded = conflictState.conflictValuePropertyExpanded;\n\t\t}\n\n\t\tvar buttonWidth = s_buttonStyle.CalcSize(s_iconPlus).x;\n\n\t\tvar labelPosition = position;\n\t\tlabelPosition.height = EditorGUIUtility.singleLineHeight;\n\t\tif (property.isExpanded)\n\t\t\tlabelPosition.xMax -= s_buttonStyle.CalcSize(s_iconPlus).x;\n\n\t\tEditorGUI.PropertyField(labelPosition, property, label, false);\n\t\t// property.isExpanded = EditorGUI.Foldout(labelPosition, property.isExpanded, label);\n\t\tif (property.isExpanded)\n\t\t{\n\t\t\tvar buttonPosition = position;\n\t\t\tbuttonPosition.xMin = buttonPosition.xMax - buttonWidth;\n\t\t\tbuttonPosition.height = EditorGUIUtility.singleLineHeight;\n\t\t\tEditorGUI.BeginDisabledGroup(conflictState.conflictIndex != -1);\n\t\t\tif(GUI.Button(buttonPosition, s_iconPlus, s_buttonStyle))\n\t\t\t{\n\t\t\t\tbuttonAction = Action.Add;\n\t\t\t\tbuttonActionIndex = keyArrayProperty.arraySize;\n\t\t\t}\n\t\t\tEditorGUI.EndDisabledGroup();\n\n\t\t\tEditorGUI.indentLevel++;\n\t\t\tvar linePosition = position;\n\t\t\tlinePosition.y += EditorGUIUtility.singleLineHeight;\n\t\t\tlinePosition.xMax -= buttonWidth;\n\n\t\t\tforeach(var entry in EnumerateEntries(keyArrayProperty, valueArrayProperty))\n\t\t\t{\n\t\t\t\tvar keyProperty = entry.keyProperty;\n\t\t\t\tvar valueProperty = entry.valueProperty;\n\t\t\t\tint i = entry.index;\n\n\t\t\t\tfloat lineHeight = DrawKeyValueLine(keyProperty, valueProperty, linePosition, i);\n\n\t\t\t\tbuttonPosition = linePosition;\n\t\t\t\tbuttonPosition.x = linePosition.xMax;\n\t\t\t\tbuttonPosition.height = EditorGUIUtility.singleLineHeight;\n\t\t\t\tif(GUI.Button(buttonPosition, s_iconMinus, s_buttonStyle))\n\t\t\t\t{\n\t\t\t\t\tbuttonAction = Action.Remove;\n\t\t\t\t\tbuttonActionIndex = i;\n\t\t\t\t}\n\n\t\t\t\tif(i == conflictState.conflictIndex && conflictState.conflictOtherIndex == -1)\n\t\t\t\t{\n\t\t\t\t\tvar iconPosition = linePosition;\n\t\t\t\t\ticonPosition.size =  s_buttonStyle.CalcSize(s_warningIconNull);\n\t\t\t\t\tGUI.Label(iconPosition, s_warningIconNull);\n\t\t\t\t}\n\t\t\t\telse if(i == conflictState.conflictIndex)\n\t\t\t\t{\n\t\t\t\t\tvar iconPosition = linePosition;\n\t\t\t\t\ticonPosition.size =  s_buttonStyle.CalcSize(s_warningIconConflict);\n\t\t\t\t\tGUI.Label(iconPosition, s_warningIconConflict);\n\t\t\t\t}\n\t\t\t\telse if(i == conflictState.conflictOtherIndex)\n\t\t\t\t{\n\t\t\t\t\tvar iconPosition = linePosition;\n\t\t\t\t\ticonPosition.size =  s_buttonStyle.CalcSize(s_warningIconOther);\n\t\t\t\t\tGUI.Label(iconPosition, s_warningIconOther);\n\t\t\t\t}\n\n\n\t\t\t\tlinePosition.y += lineHeight;\n\t\t\t}\n\n\t\t\tEditorGUI.indentLevel--;\n\t\t}\n\n\t\tif(buttonAction == Action.Add)\n\t\t{\n\t\t\tkeyArrayProperty.InsertArrayElementAtIndex(buttonActionIndex);\n\t\t\tvalueArrayProperty.InsertArrayElementAtIndex(buttonActionIndex);\n\t\t}\n\t\telse if(buttonAction == Action.Remove)\n\t\t{\n\t\t\tDeleteArrayElementAtIndex(keyArrayProperty, buttonActionIndex);\n\t\t\tDeleteArrayElementAtIndex(valueArrayProperty, buttonActionIndex);\n\t\t}\n\n\t\tconflictState.conflictKey = null;\n\t\tconflictState.conflictValue = null;\n\t\tconflictState.conflictIndex = -1;\n\t\tconflictState.conflictOtherIndex = -1;\n\t\tconflictState.conflictLineHeight = 0f;\n\t\tconflictState.conflictKeyPropertyExpanded = false;\n\t\tconflictState.conflictValuePropertyExpanded = false;\n\n\t\tforeach(var entry1 in EnumerateEntries(keyArrayProperty, valueArrayProperty))\n\t\t{\n\t\t\tvar keyProperty1 = entry1.keyProperty;\n\t\t\tint i = entry1.index;\n\t\t\tobject keyProperty1Value = GetPropertyValue(keyProperty1);\n\n\t\t\tif(keyProperty1Value == null)\n\t\t\t{\n\t\t\t\tvar valueProperty1 = entry1.valueProperty;\n\t\t\t\tSaveProperty(keyProperty1, valueProperty1, i, -1, conflictState);\n\t\t\t\tDeleteArrayElementAtIndex(valueArrayProperty, i);\n\t\t\t\tDeleteArrayElementAtIndex(keyArrayProperty, i);\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\n\t\t\tforeach(var entry2 in EnumerateEntries(keyArrayProperty, valueArrayProperty, i + 1))\n\t\t\t{\n\t\t\t\tvar keyProperty2 = entry2.keyProperty;\n\t\t\t\tint j = entry2.index;\n\t\t\t\tobject keyProperty2Value = GetPropertyValue(keyProperty2);\n\n\t\t\t\tif(ComparePropertyValues(keyProperty1Value, keyProperty2Value))\n\t\t\t\t{\n\t\t\t\t\tvar valueProperty2 = entry2.valueProperty;\n\t\t\t\t\tSaveProperty(keyProperty2, valueProperty2, j, i, conflictState);\n\t\t\t\t\tDeleteArrayElementAtIndex(keyArrayProperty, j);\n\t\t\t\t\tDeleteArrayElementAtIndex(valueArrayProperty, j);\n\n\t\t\t\t\tgoto breakLoops;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreakLoops:\n\n\t\tEditorGUI.EndProperty();\n\t}\n\n\tstatic float DrawKeyValueLine(SerializedProperty keyProperty, SerializedProperty valueProperty, Rect linePosition, int index)\n\t{\n\t\tbool keyCanBeExpanded = CanPropertyBeExpanded(keyProperty);\n\t\tbool valueCanBeExpanded = CanPropertyBeExpanded(valueProperty);\n\n\t\tif(!keyCanBeExpanded && valueCanBeExpanded)\n\t\t{\n\t\t\treturn DrawKeyValueLineExpand(keyProperty, valueProperty, linePosition);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvar keyLabel = keyCanBeExpanded ? (\"Key \" + index.ToString()) : \"\";\n\t\t\tvar valueLabel = valueCanBeExpanded ? (\"Value \" + index.ToString()) : \"\";\n\t\t\treturn DrawKeyValueLineSimple(keyProperty, valueProperty, keyLabel, valueLabel, linePosition);\n\t\t}\n\t}\n\n\tstatic float DrawKeyValueLineSimple(SerializedProperty keyProperty, SerializedProperty valueProperty, string keyLabel, string valueLabel, Rect linePosition)\n\t{\n\t\tfloat labelWidth = EditorGUIUtility.labelWidth;\n\t\tfloat labelWidthRelative = labelWidth / linePosition.width;\n\n\t\tfloat keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);\n\t\tvar keyPosition = linePosition;\n\t\tkeyPosition.height = keyPropertyHeight;\n\t\tkeyPosition.width = labelWidth - IndentWidth;\n\t\tEditorGUIUtility.labelWidth = keyPosition.width * labelWidthRelative;\n\t\tEditorGUI.PropertyField(keyPosition, keyProperty, TempContent(keyLabel), true);\n\n\t\tfloat valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);\n\t\tvar valuePosition = linePosition;\n\t\tvaluePosition.height = valuePropertyHeight;\n\t\tvaluePosition.xMin += labelWidth;\n\t\tEditorGUIUtility.labelWidth = valuePosition.width * labelWidthRelative;\n\t\tEditorGUI.indentLevel--;\n\t\tEditorGUI.PropertyField(valuePosition, valueProperty, TempContent(valueLabel), true);\n\t\tEditorGUI.indentLevel++;\n\n\t\tEditorGUIUtility.labelWidth = labelWidth;\n\n\t\treturn Mathf.Max(keyPropertyHeight, valuePropertyHeight);\n\t}\n\n\tstatic float DrawKeyValueLineExpand(SerializedProperty keyProperty, SerializedProperty valueProperty, Rect linePosition)\n\t{\n\t\tfloat labelWidth = EditorGUIUtility.labelWidth;\n\n\t\tfloat keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);\n\t\tvar keyPosition = linePosition;\n\t\tkeyPosition.height = keyPropertyHeight;\n\t\tkeyPosition.width = labelWidth - IndentWidth;\n\t\tEditorGUI.PropertyField(keyPosition, keyProperty, GUIContent.none, true);\n\n\t\tfloat valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);\n\t\tvar valuePosition = linePosition;\n\t\tvaluePosition.height = valuePropertyHeight;\n\t\tEditorGUI.PropertyField(valuePosition, valueProperty, GUIContent.none, true);\n\n\t\tEditorGUIUtility.labelWidth = labelWidth;\n\n\t\treturn Mathf.Max(keyPropertyHeight, valuePropertyHeight);\n\t}\n\n\tstatic bool CanPropertyBeExpanded(SerializedProperty property)\n\t{\n\t\tswitch(property.propertyType)\n\t\t{\n\t\tcase SerializedPropertyType.Generic:\n\t\tcase SerializedPropertyType.Vector4:\n\t\tcase SerializedPropertyType.Quaternion:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tstatic void SaveProperty(SerializedProperty keyProperty, SerializedProperty valueProperty, int index, int otherIndex, ConflictState conflictState)\n\t{\n\t\tconflictState.conflictKey = GetPropertyValue(keyProperty);\n\t\tconflictState.conflictValue = GetPropertyValue(valueProperty);\n\t\tfloat keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);\n\t\tfloat valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);\n\t\tfloat lineHeight = Mathf.Max(keyPropertyHeight, valuePropertyHeight);\n\t\tconflictState.conflictLineHeight = lineHeight;\n\t\tconflictState.conflictIndex = index;\n\t\tconflictState.conflictOtherIndex = otherIndex;\n\t\tconflictState.conflictKeyPropertyExpanded = keyProperty.isExpanded;\n\t\tconflictState.conflictValuePropertyExpanded = valueProperty.isExpanded;\n\t}\n\n\tpublic override float GetPropertyHeight(SerializedProperty property, GUIContent label)\n\t{\n\t\tfloat propertyHeight = EditorGUIUtility.singleLineHeight;\n\n\t\tif (property.isExpanded)\n\t\t{\n\t\t\tvar keysProperty = property.FindPropertyRelative(KeysFieldName);\n\t\t\tvar valuesProperty = property.FindPropertyRelative(ValuesFieldName);\n\n\t\t\tforeach(var entry in EnumerateEntries(keysProperty, valuesProperty))\n\t\t\t{\n\t\t\t\tvar keyProperty = entry.keyProperty;\n\t\t\t\tvar valueProperty = entry.valueProperty;\n\t\t\t\tfloat keyPropertyHeight = EditorGUI.GetPropertyHeight(keyProperty);\n\t\t\t\tfloat valuePropertyHeight = EditorGUI.GetPropertyHeight(valueProperty);\n\t\t\t\tfloat lineHeight = Mathf.Max(keyPropertyHeight, valuePropertyHeight);\n\t\t\t\tpropertyHeight += lineHeight;\n\t\t\t}\n\n\t\t\tConflictState conflictState = GetConflictState(property);\n\n\t\t\tif(conflictState.conflictIndex != -1)\n\t\t\t{\n\t\t\t\tpropertyHeight += conflictState.conflictLineHeight;\n\t\t\t}\n\t\t}\n\n\t\treturn propertyHeight;\n\t}\n\n\tstatic ConflictState GetConflictState(SerializedProperty property)\n\t{\n\t\tConflictState conflictState;\n\t\tPropertyIdentity propId = new PropertyIdentity(property);\n\t\tif(!s_conflictStateDict.TryGetValue(propId, out conflictState))\n\t\t{\n\t\t\tconflictState = new ConflictState();\n\t\t\ts_conflictStateDict.Add(propId, conflictState);\n\t\t}\n\t\treturn conflictState;\n\t}\n\n\tstatic Dictionary<SerializedPropertyType, PropertyInfo> s_serializedPropertyValueAccessorsDict;\n\n\tstatic SerializableDictionaryPropertyDrawer()\n\t{\n\t\tDictionary<SerializedPropertyType, string> serializedPropertyValueAccessorsNameDict = new Dictionary<SerializedPropertyType, string>() {\n\t\t\t{ SerializedPropertyType.Integer, \"intValue\" },\n\t\t\t{ SerializedPropertyType.Boolean, \"boolValue\" },\n\t\t\t{ SerializedPropertyType.Float, \"floatValue\" },\n\t\t\t{ SerializedPropertyType.String, \"stringValue\" },\n\t\t\t{ SerializedPropertyType.Color, \"colorValue\" },\n\t\t\t{ SerializedPropertyType.ObjectReference, \"objectReferenceValue\" },\n\t\t\t{ SerializedPropertyType.LayerMask, \"intValue\" },\n\t\t\t{ SerializedPropertyType.Enum, \"intValue\" },\n\t\t\t{ SerializedPropertyType.Vector2, \"vector2Value\" },\n\t\t\t{ SerializedPropertyType.Vector3, \"vector3Value\" },\n\t\t\t{ SerializedPropertyType.Vector4, \"vector4Value\" },\n\t\t\t{ SerializedPropertyType.Rect, \"rectValue\" },\n\t\t\t{ SerializedPropertyType.ArraySize, \"intValue\" },\n\t\t\t{ SerializedPropertyType.Character, \"intValue\" },\n\t\t\t{ SerializedPropertyType.AnimationCurve, \"animationCurveValue\" },\n\t\t\t{ SerializedPropertyType.Bounds, \"boundsValue\" },\n\t\t\t{ SerializedPropertyType.Quaternion, \"quaternionValue\" },\n\t\t};\n\t\tType serializedPropertyType = typeof(SerializedProperty);\n\n\t\ts_serializedPropertyValueAccessorsDict\t= new Dictionary<SerializedPropertyType, PropertyInfo>();\n\t\tBindingFlags flags = BindingFlags.Instance | BindingFlags.Public;\n\n\t\tforeach(var kvp in serializedPropertyValueAccessorsNameDict)\n\t\t{\n\t\t\tPropertyInfo propertyInfo = serializedPropertyType.GetProperty(kvp.Value, flags);\n\t\t\ts_serializedPropertyValueAccessorsDict.Add(kvp.Key, propertyInfo);\n\t\t}\n\t}\n\n\tstatic GUIContent IconContent(string name, string tooltip)\n\t{\n\t\tvar builtinIcon = EditorGUIUtility.IconContent (name);\n\t\treturn new GUIContent(builtinIcon.image, tooltip);\n\t}\n\n\tstatic GUIContent TempContent(string text)\n\t{\n\t\ts_tempContent.text = text;\n\t\treturn s_tempContent;\n\t}\n\n\tstatic void DeleteArrayElementAtIndex(SerializedProperty arrayProperty, int index)\n\t{\n\t\tvar property = arrayProperty.GetArrayElementAtIndex(index);\n\t\t// if(arrayProperty.arrayElementType.StartsWith(\"PPtr<$\"))\n\t\tif(property.propertyType == SerializedPropertyType.ObjectReference)\n\t\t{\n\t\t\tproperty.objectReferenceValue = null;\n\t\t}\n\n\t\tarrayProperty.DeleteArrayElementAtIndex(index);\n\t}\n\n\tpublic static object GetPropertyValue(SerializedProperty p)\n\t{\n\t\tPropertyInfo propertyInfo;\n\t\tif(s_serializedPropertyValueAccessorsDict.TryGetValue(p.propertyType, out propertyInfo))\n\t\t{\n\t\t\treturn propertyInfo.GetValue(p, null);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(p.isArray)\n\t\t\t\treturn GetPropertyValueArray(p);\n\t\t\telse\n\t\t\t\treturn GetPropertyValueGeneric(p);\n\t\t}\n\t}\n\n\tstatic void SetPropertyValue(SerializedProperty p, object v)\n\t{\n\t\tPropertyInfo propertyInfo;\n\t\tif(s_serializedPropertyValueAccessorsDict.TryGetValue(p.propertyType, out propertyInfo))\n\t\t{\n\t\t\tpropertyInfo.SetValue(p, v, null);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tif(p.isArray)\n\t\t\t\tSetPropertyValueArray(p, v);\n\t\t\telse\n\t\t\t\tSetPropertyValueGeneric(p, v);\n\t\t}\n\t}\n\n\tstatic object GetPropertyValueArray(SerializedProperty property)\n\t{\n\t\tobject[] array = new object[property.arraySize];\n\t\tfor(int i = 0; i < property.arraySize; i++)\n\t\t{\n\t\t\tSerializedProperty item = property.GetArrayElementAtIndex(i);\n\t\t\tarray[i] = GetPropertyValue(item);\n\t\t}\n\t\treturn array;\n\t}\n\n\tstatic object GetPropertyValueGeneric(SerializedProperty property)\n\t{\n\t\tDictionary<string, object> dict = new Dictionary<string, object>();\n\t\tvar iterator = property.Copy();\n\t\tif(iterator.Next(true))\n\t\t{\n\t\t\tvar end = property.GetEndProperty();\n\t\t\tdo\n\t\t\t{\n\t\t\t\tstring name = iterator.name;\n\t\t\t\tobject value = GetPropertyValue(iterator);\n\t\t\t\tdict.Add(name, value);\n\t\t\t} while(iterator.Next(false) && iterator.propertyPath != end.propertyPath);\n\t\t}\n\t\treturn dict;\n\t}\n\n\tstatic void SetPropertyValueArray(SerializedProperty property, object v)\n\t{\n\t\tobject[] array = (object[]) v;\n\t\tproperty.arraySize = array.Length;\n\t\tfor(int i = 0; i < property.arraySize; i++)\n\t\t{\n\t\t\tSerializedProperty item = property.GetArrayElementAtIndex(i);\n\t\t\tSetPropertyValue(item, array[i]);\n\t\t}\n\t}\n\n\tstatic void SetPropertyValueGeneric(SerializedProperty property, object v)\n\t{\n\t\tDictionary<string, object> dict = (Dictionary<string, object>) v;\n\t\tvar iterator = property.Copy();\n\t\tif(iterator.Next(true))\n\t\t{\n\t\t\tvar end = property.GetEndProperty();\n\t\t\tdo\n\t\t\t{\n\t\t\t\tstring name = iterator.name;\n\t\t\t\tSetPropertyValue(iterator, dict[name]);\n\t\t\t} while(iterator.Next(false) && iterator.propertyPath != end.propertyPath);\n\t\t}\n\t}\n\n\tstatic bool ComparePropertyValues(object value1, object value2)\n\t{\n\t\tif(value1 is Dictionary<string, object> && value2 is Dictionary<string, object>)\n\t\t{\n\t\t\tvar dict1 = (Dictionary<string, object>)value1;\n\t\t\tvar dict2 = (Dictionary<string, object>)value2;\n\t\t\treturn CompareDictionaries(dict1, dict2);\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturn object.Equals(value1, value2);\n\t\t}\n\t}\n\n\tstatic bool CompareDictionaries(Dictionary<string, object> dict1, Dictionary<string, object> dict2)\n\t{\n\t\tif(dict1.Count != dict2.Count)\n\t\t\treturn false;\n\n\t\tforeach(var kvp1 in dict1)\n\t\t{\n\t\t\tvar key1 = kvp1.Key;\n\t\t\tobject value1 = kvp1.Value;\n\n\t\t\tobject value2;\n\t\t\tif(!dict2.TryGetValue(key1, out value2))\n\t\t\t\treturn false;\n\n\t\t\tif(!ComparePropertyValues(value1, value2))\n\t\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn true;\n\t}\n\n\tstruct EnumerationEntry\n\t{\n\t\tpublic SerializedProperty keyProperty;\n\t\tpublic SerializedProperty valueProperty;\n\t\tpublic int index;\n\n\t\tpublic EnumerationEntry(SerializedProperty keyProperty, SerializedProperty valueProperty, int index)\n\t\t{\n\t\t\tthis.keyProperty = keyProperty;\n\t\t\tthis.valueProperty = valueProperty;\n\t\t\tthis.index = index;\n\t\t}\n\t}\n\n\tstatic IEnumerable<EnumerationEntry> EnumerateEntries(SerializedProperty keyArrayProperty, SerializedProperty valueArrayProperty, int startIndex = 0)\n\t{\n\t\tif(keyArrayProperty.arraySize > startIndex)\n\t\t{\n\t\t\tint index = startIndex;\n\t\t\tvar keyProperty = keyArrayProperty.GetArrayElementAtIndex(startIndex);\n\t\t\tvar valueProperty = valueArrayProperty.GetArrayElementAtIndex(startIndex);\n\t\t\tvar endProperty = keyArrayProperty.GetEndProperty();\n\n\t\t\tdo\n\t\t\t{\n\t\t\t\tyield return new EnumerationEntry(keyProperty, valueProperty, index);\n\t\t\t\tindex++;\n\t\t\t} while(keyProperty.Next(false) && valueProperty.Next(false) && !SerializedProperty.EqualContents(keyProperty, endProperty));\n\t\t}\n\t}\n}\n\npublic class SerializableDictionaryStoragePropertyDrawer : PropertyDrawer\n{\n\tpublic override void OnGUI(Rect position, SerializedProperty property, GUIContent label)\n\t{\n\t\tproperty.Next(true);\n\t\tEditorGUI.PropertyField(position, property, label, true);\n\t}\n\n\tpublic override float GetPropertyHeight(SerializedProperty property, GUIContent label)\n\t{\n\t\tproperty.Next(true);\n\t\treturn EditorGUI.GetPropertyHeight(property);\n\t}\n}\n"
  },
  {
    "path": "src/Lib/SerializableDictionary/Editor/SerializableDictionaryPropertyDrawer.cs.meta",
    "content": "fileFormatVersion: 2\nguid: 91da51d02ab9ebc459d80d5965d40d19\ntimeCreated: 1492869349\nlicenseType: Store\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary/Editor.meta",
    "content": "fileFormatVersion: 2\nguid: 38f5d7794faaa21468f705ca2a23149f\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary/SerializableDictionary.Runtime.asmdef",
    "content": "﻿{\n\t\"name\": \"SerializableDictionary.Runtime\"\n}\n"
  },
  {
    "path": "src/Lib/SerializableDictionary/SerializableDictionary.Runtime.asmdef.meta",
    "content": "fileFormatVersion: 2\nguid: a9d2a6efafebd5e47b01684a70d95cc5\nAssemblyDefinitionImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary/SerializableDictionary.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Runtime.Serialization;\nusing UnityEngine;\n\npublic abstract class SerializableDictionaryBase<TKey, TValue, TValueStorage> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver\n{\n\t[SerializeField]\n\tTKey[] m_keys;\n\t[SerializeField]\n\tTValueStorage[] m_values;\n\n\tpublic SerializableDictionaryBase()\n\t{\n\t}\n\n\tpublic SerializableDictionaryBase(IDictionary<TKey, TValue> dict) : base(dict.Count)\n\t{\n\t\tforeach (var kvp in dict)\n\t\t{\n\t\t\tthis[kvp.Key] = kvp.Value;\n\t\t}\n\t}\n\t\n\tprotected SerializableDictionaryBase(SerializationInfo info, StreamingContext context) : base(info,context){}\n\n\tprotected abstract void SetValue(TValueStorage[] storage, int i, TValue value);\n\tprotected abstract TValue GetValue(TValueStorage[] storage, int i);\n\n\tpublic void CopyFrom(IDictionary<TKey, TValue> dict)\n\t{\n\t\tthis.Clear();\n\t\tforeach (var kvp in dict)\n\t\t{\n\t\t\tthis[kvp.Key] = kvp.Value;\n\t\t}\n\t}\n\n\tpublic void OnAfterDeserialize()\n\t{\n\t\tif(m_keys != null && m_values != null && m_keys.Length == m_values.Length)\n\t\t{\n\t\t\tthis.Clear();\n\t\t\tint n = m_keys.Length;\n\t\t\tfor(int i = 0; i < n; ++i)\n\t\t\t{\n\t\t\t\tthis[m_keys[i]] = GetValue(m_values, i);\n\t\t\t}\n\n\t\t\tm_keys = null;\n\t\t\tm_values = null;\n\t\t}\n\n\t}\n\n\tpublic void OnBeforeSerialize()\n\t{\n\t\tint n = this.Count;\n\t\tm_keys = new TKey[n];\n\t\tm_values = new TValueStorage[n];\n\n\t\tint i = 0;\n\t\tforeach(var kvp in this)\n\t\t{\n\t\t\tm_keys[i] = kvp.Key;\n\t\t\tSetValue(m_values, i, kvp.Value);\n\t\t\t++i;\n\t\t}\n\t}\n}\n\npublic class SerializableDictionary<TKey, TValue> : SerializableDictionaryBase<TKey, TValue, TValue>\n{\n\tpublic SerializableDictionary()\n\t{\n\t}\n\n\tpublic SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict)\n\t{\n\t}\n\n\tprotected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info,context){}\n\n\tprotected override TValue GetValue(TValue[] storage, int i)\n\t{\n\t\treturn storage[i];\n\t}\n\n\tprotected override void SetValue(TValue[] storage, int i, TValue value)\n\t{\n\t\tstorage[i] = value;\n\t}\n}\n\npublic static class SerializableDictionary\n{\n\tpublic class Storage<T>\n\t{\n\t\tpublic T data;\n\t}\n}\n\npublic class SerializableDictionary<TKey, TValue, TValueStorage> : SerializableDictionaryBase<TKey, TValue, TValueStorage> where TValueStorage : SerializableDictionary.Storage<TValue>, new()\n{\n\tpublic SerializableDictionary()\n\t{\n\t}\n\n\tpublic SerializableDictionary(IDictionary<TKey, TValue> dict) : base(dict)\n\t{\n\t}\n\n\tprotected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info,context){}\n\n\tprotected override TValue GetValue(TValueStorage[] storage, int i)\n\t{\n\t\treturn storage[i].data;\n\t}\n\n\tprotected override void SetValue(TValueStorage[] storage, int i, TValue value)\n\t{\n\t\tstorage[i] = new TValueStorage();\n\t\tstorage[i].data = value;\n\t}\n}\n"
  },
  {
    "path": "src/Lib/SerializableDictionary/SerializableDictionary.cs.meta",
    "content": "fileFormatVersion: 2\nguid: e7be1c9624387604fba4005ccf7dbd5a\ntimeCreated: 1492868176\nlicenseType: Store\nMonoImporter:\n  serializedVersion: 2\n  defaultReferences: []\n  executionOrder: 0\n  icon: {instanceID: 0}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib/SerializableDictionary.meta",
    "content": "fileFormatVersion: 2\nguid: ec22cd43b8631bf4dbb28130429205be\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Lib.meta",
    "content": "fileFormatVersion: 2\nguid: 896708c62f5edba45b8a132d08ee914d\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/atom.png.meta",
    "content": "fileFormatVersion: 2\nguid: d17647a721ae9b6418d60aabba5be55f\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/form.png.meta",
    "content": "fileFormatVersion: 2\nguid: 806f5574c9706cd4e812686596744160\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/graphene.png.meta",
    "content": "fileFormatVersion: 2\nguid: db72b2a2b6f47b544a0458d9aa7b0e69\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/injector.png.meta",
    "content": "fileFormatVersion: 2\nguid: d249967811185734b8114d4b8977f433\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/layout.png.meta",
    "content": "fileFormatVersion: 2\nguid: 779432a5e218db5438ca646cb9970341\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 11\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 1\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: -1\n    aniso: -1\n    mipBias: -100\n    wrapU: -1\n    wrapV: -1\n    wrapW: -1\n  nPOTScale: 1\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 0\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 1\n  alphaUsage: 1\n  alphaIsTransparency: 0\n  spriteTessellationDetail: -1\n  textureType: 0\n  textureShape: 1\n  singleChannelComponent: 0\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: \n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/molecule.png.meta",
    "content": "fileFormatVersion: 2\nguid: 2a950971ac44a1f4bbec1a1338d2f483\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/plate.png.meta",
    "content": "fileFormatVersion: 2\nguid: d5720deaef5568642aae7621cd85c1d4\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/quantum.png.meta",
    "content": "fileFormatVersion: 2\nguid: ab20f1c106c55d84b82bc7d5a640819b\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/renderer.png.meta",
    "content": "fileFormatVersion: 2\nguid: 9d85d8a741327e143b28987b5a69ef93\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/router.png.meta",
    "content": "fileFormatVersion: 2\nguid: 3fa61ff64b7c4e64e8b944ba5d308365\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/state.png.meta",
    "content": "fileFormatVersion: 2\nguid: 724b44860269a9f4abed118244135936\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/template.png.meta",
    "content": "fileFormatVersion: 2\nguid: 9c6112e092c235c4388c5cc406349194\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 0\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons/theme.png.meta",
    "content": "fileFormatVersion: 2\nguid: 73d27894540ca4a47ba97356fee56092\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 1\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Icons.meta",
    "content": "fileFormatVersion: 2\nguid: 0df9eec597c9c3f4db1681e862dd42b9\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Logo/graphene-logo-full.png.meta",
    "content": "fileFormatVersion: 2\nguid: 4ba3b61aa9eee63418d9a0c7be4901f8\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 1\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Logo/graphene-logo-white.png.meta",
    "content": "fileFormatVersion: 2\nguid: 79495ad5c6a23a54e9a080b692eca7b1\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 1\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: WebGL\n    maxTextureSize: 2048\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Logo/graphene-logo.png.meta",
    "content": "fileFormatVersion: 2\nguid: a7567e94e4334dd4bb5bed2d09495114\nTextureImporter:\n  internalIDToNameTable: []\n  externalObjects: {}\n  serializedVersion: 12\n  mipmaps:\n    mipMapMode: 0\n    enableMipMap: 0\n    sRGBTexture: 1\n    linearTexture: 0\n    fadeOut: 0\n    borderMipMap: 0\n    mipMapsPreserveCoverage: 0\n    alphaTestReferenceValue: 0.5\n    mipMapFadeDistanceStart: 1\n    mipMapFadeDistanceEnd: 3\n  bumpmap:\n    convertToNormalMap: 0\n    externalNormalMap: 0\n    heightScale: 0.25\n    normalMapFilter: 0\n  isReadable: 0\n  streamingMipmaps: 0\n  streamingMipmapsPriority: 0\n  vTOnly: 0\n  ignoreMasterTextureLimit: 0\n  grayScaleToAlpha: 0\n  generateCubemap: 6\n  cubemapConvolution: 0\n  seamlessCubemap: 0\n  textureFormat: 1\n  maxTextureSize: 2048\n  textureSettings:\n    serializedVersion: 2\n    filterMode: 1\n    aniso: 1\n    mipBias: 0\n    wrapU: 1\n    wrapV: 1\n    wrapW: 0\n  nPOTScale: 0\n  lightmap: 0\n  compressionQuality: 50\n  spriteMode: 1\n  spriteExtrude: 1\n  spriteMeshType: 1\n  alignment: 0\n  spritePivot: {x: 0.5, y: 0.5}\n  spritePixelsToUnits: 100\n  spriteBorder: {x: 0, y: 0, z: 0, w: 0}\n  spriteGenerateFallbackPhysicsShape: 0\n  alphaUsage: 1\n  alphaIsTransparency: 1\n  spriteTessellationDetail: -1\n  textureType: 8\n  textureShape: 1\n  singleChannelComponent: 0\n  flipbookRows: 1\n  flipbookColumns: 1\n  maxTextureSizeSet: 0\n  compressionQualitySet: 0\n  textureFormatSet: 0\n  ignorePngGamma: 0\n  applyGammaDecoding: 0\n  cookieLightType: 1\n  platformSettings:\n  - serializedVersion: 3\n    buildTarget: DefaultTexturePlatform\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  - serializedVersion: 3\n    buildTarget: Standalone\n    maxTextureSize: 512\n    resizeAlgorithm: 0\n    textureFormat: -1\n    textureCompression: 1\n    compressionQuality: 50\n    crunchedCompression: 0\n    allowsAlphaSplitting: 0\n    overridden: 0\n    androidETC2FallbackOverride: 0\n    forceMaximumCompressionQuality_BC6H_BC7: 0\n  spriteSheet:\n    serializedVersion: 2\n    sprites: []\n    outline: []\n    physicsShape: []\n    bones: []\n    spriteID: 5e97eb03825dee720800000000000000\n    internalID: 0\n    vertices: []\n    indices: \n    edges: []\n    weights: []\n    secondaryTextures: []\n    nameFileIdTable: {}\n  spritePackingTag: \n  pSDRemoveMatte: 0\n  pSDShowRemoveMatteOption: 0\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources/Logo.meta",
    "content": "fileFormatVersion: 2\nguid: 2a9a343a7c0ac0d4781c5c2075c9129c\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/Resources.meta",
    "content": "fileFormatVersion: 2\nguid: c57b36d4b8516cb49b133e196e2765ed\nfolderAsset: yes\nDefaultImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  },
  {
    "path": "src/package.json",
    "content": "{\n  \"name\": \"com.graphene.core\",\n  \"version\": \"0.1.4\",\n  \"displayName\": \"Graphene\",\n  \"description\": \"A lightweight & flexible open-source UI framework for Unity's UI Toolkit.\",\n  \"unity\": \"2018.3\",\n  \"dependencies\": {\n    \"com.kinstrife.core.reflection-helper\": \"file:/GameDev/Kinstrife/Packages/Core/ReflectionHelper\",\n    \"com.externals.fast-member\": \"file:/GameDev/Externals/FastMember\",\n    \"com.externals.odin\": \"file:/GameDev/Externals/Sirenix\",\n    \"com.unity.editorcoroutines\": \"1.0.0\"\n  },\n  \"keywords\": [],\n  \"author\": {\n    \"email\": \"info@kinstrife.com\",\n    \"name\": \"CupBearer\",\n    \"url\": \"https://www.cup-bearer.com\"\n  }\n}\n"
  },
  {
    "path": "src/package.json.meta",
    "content": "fileFormatVersion: 2\nguid: a7886040c573d5c4183352bdef617071\nPackageManifestImporter:\n  externalObjects: {}\n  userData: \n  assetBundleName: \n  assetBundleVariant: \n"
  }
]