diff --git a/.gitignore b/.gitignore
index e61209c..e69de29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,260 +0,0 @@
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-
-# User-specific files
-*.suo
-*.user
-*.userosscache
-*.sln.docstates
-
-# User-specific files (MonoDevelop/Xamarin Studio)
-*.userprefs
-
-# Build results
-[Dd]ebug/
-[Dd]ebugPublic/
-[Rr]elease/
-[Rr]eleases/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-bin/
-obj/
-
-# Visual Studio 2015 cache/options directory
-.vs/
-# Uncomment if you have tasks that create the project's static files in wwwroot
-#wwwroot/
-
-# MSTest test Results
-[Tt]est[Rr]esult*/
-[Bb]uild[Ll]og.*
-
-# NUNIT
-*.VisualState.xml
-TestResult.xml
-
-# Build Results of an ATL Project
-[Dd]ebugPS/
-[Rr]eleasePS/
-dlldata.c
-
-# DNX
-project.lock.json
-artifacts/
-
-*_i.c
-*_p.c
-*_i.h
-*.ilk
-*.meta
-*.obj
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.tmp_proj
-*.log
-*.vspscc
-*.vssscc
-.builds
-*.pidb
-*.svclog
-*.scc
-
-# Chutzpah Test files
-_Chutzpah*
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opendb
-*.opensdf
-*.sdf
-*.cachefile
-*.VC.db
-*.VC.VC.opendb
-
-# Visual Studio profiler
-*.psess
-*.vsp
-*.vspx
-*.sap
-
-# TFS 2012 Local Workspace
-$tf/
-
-# Guidance Automation Toolkit
-*.gpState
-
-# ReSharper is a .NET coding add-in
-_ReSharper*/
-*.[Rr]e[Ss]harper
-*.DotSettings.user
-
-# JustCode is a .NET coding add-in
-.JustCode
-
-# TeamCity is a build add-in
-_TeamCity*
-
-# DotCover is a Code Coverage Tool
-*.dotCover
-
-# NCrunch
-_NCrunch_*
-.*crunch*.local.xml
-nCrunchTemp_*
-
-# MightyMoose
-*.mm.*
-AutoTest.Net/
-
-# Web workbench (sass)
-.sass-cache/
-
-# Installshield output folder
-[Ee]xpress/
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish/
-
-# Publish Web Output
-*.[Pp]ublish.xml
-*.azurePubxml
-# TODO: Comment the next line if you want to checkin your web deploy settings
-# but database connection strings (with potential passwords) will be unencrypted
-*.pubxml
-*.publishproj
-
-# Microsoft Azure Web App publish settings. Comment the next line if you want to
-# checkin your Azure Web App publish settings, but sensitive information contained
-# in these scripts will be unencrypted
-PublishScripts/
-
-# NuGet Packages
-*.nupkg
-# The packages folder can be ignored because of Package Restore
-**/packages/*
-# except build/, which is used as an MSBuild target.
-!**/packages/build/
-# Uncomment if necessary however generally it will be regenerated when needed
-#!**/packages/repositories.config
-# NuGet v3's project.json files produces more ignoreable files
-*.nuget.props
-*.nuget.targets
-
-# Microsoft Azure Build Output
-csx/
-*.build.csdef
-
-# Microsoft Azure Emulator
-ecf/
-rcf/
-
-# Windows Store app package directories and files
-AppPackages/
-BundleArtifacts/
-Package.StoreAssociation.xml
-_pkginfo.txt
-
-# Visual Studio cache files
-# files ending in .cache can be ignored
-*.[Cc]ache
-# but keep track of directories ending in .cache
-!*.[Cc]ache/
-
-# Others
-ClientBin/
-~$*
-*~
-*.dbmdl
-*.dbproj.schemaview
-*.pfx
-*.publishsettings
-node_modules/
-orleans.codegen.cs
-
-# Since there are multiple workflows, uncomment next line to ignore bower_components
-# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
-#bower_components/
-
-# RIA/Silverlight projects
-Generated_Code/
-
-# Backup & report files from converting an old project file
-# to a newer Visual Studio version. Backup files are not needed,
-# because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-UpgradeLog*.htm
-
-# SQL Server files
-*.mdf
-*.ldf
-
-# Business Intelligence projects
-*.rdl.data
-*.bim.layout
-*.bim_*.settings
-
-# Microsoft Fakes
-FakesAssemblies/
-
-# GhostDoc plugin setting file
-*.GhostDoc.xml
-
-# Node.js Tools for Visual Studio
-.ntvs_analysis.dat
-
-# Visual Studio 6 build log
-*.plg
-
-# Visual Studio 6 workspace options file
-*.opt
-
-# Visual Studio LightSwitch build output
-**/*.HTMLClient/GeneratedArtifacts
-**/*.DesktopClient/GeneratedArtifacts
-**/*.DesktopClient/ModelManifest.xml
-**/*.Server/GeneratedArtifacts
-**/*.Server/ModelManifest.xml
-_Pvt_Extensions
-
-# Paket dependency manager
-.paket/paket.exe
-paket-files/
-
-# FAKE - F# Make
-.fake/
-
-# JetBrains Rider
-.idea/
-*.sln.iml
-
-# General
-*.orig
-*.bak
-
-dll/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 80a7021..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-language: csharp
-solution: Unity-Weld.sln
-
-script:
- - msbuild /p:Configuration=Debug Unity-Weld.sln
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 74cdcb0..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# How to contribute to Unity Weld
-
-👍🎉 First off, thanks for taking the time to contribute! 🎉👍
-
-
-## Reporting bugs
-
-Any bug reports are useful, although there are a few things you can do that will
-make it easier for maintainers and other users to understand your report,
-reproduce the behaviour, and find related reports.
-
- - **Use a clear and descriptive title** for the issue to identify the problem.
- - **Describe the exact steps which reproduce the problem** in as many details
- as possible. Code examples or links to repos to reproduce the issue are
- always helpful.
- - **Describe the behaviour you observed after following the steps** and point
- out what exactly the problem is with that behaviour.
- - **Explain which behaviour you expected to see instead and why.** This makes
- it easier to work out what went wrong and whether the behaviour was in fact
- a bug or was by design.
- - **Check the existing open issues on GitHub** to see if someone else has had
- the same problem as you.
-
-Make sure to file bugs as [GitHub issues](https://github.com/Real-Serious-Games/Unity-Weld/issues),
-since that way everyone working on the library can see it and potentially help.
-
-
-## Pull requests
-
-All new code committed to the repository must be reviewed by at least one other
-person on the Unity Weld team before it can be merged.
-
-Before we merge pull requests there are a few things we look for to make sure
-the code is maintainable and up the same standard as the rest of the library.
-
- - Make sure that the [examples project](https://github.com/Real-Serious-Games/Unity-Weld-Examples)
- still works correctly with the new changes.
- - Check that your code conforms to the same style as existing code. Ensure that
- your editor is set up to read the [.editorconfig](http://editorconfig.org/)
- file for consistent spacing and line endings. We also try to keep our code
- style consistent with the [Microsoft C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions)
- and [Framework Design Guidelines](https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/index).
- - Make sure that the [Travis CI build](https://travis-ci.org/Real-Serious-Games/Unity-Weld)
- succeeds. This should run automatically when you create a pull request, but
- should have the same result as building the solution and running all the
- tests locally. We will not accept any pull requests that fail to build or
- contain failing tests.
- - If you have added a new feature, add a section to README.md describing the
- feature and how to use it.
-
-In addition, if your pull request breaks any existing functionality you'll need
-to provide a good justification for why it's worth breaking backwards
-compatibility.
\ No newline at end of file
diff --git a/Editor.meta b/Editor.meta
new file mode 100644
index 0000000..67ad180
--- /dev/null
+++ b/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 72f2efac3eed8eb4f98c8797f42bf210
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/AnimatorParameterBindingEditor.cs b/Editor/AnimatorParameterBindingEditor.cs
similarity index 90%
rename from UnityWeld_Editor/AnimatorParameterBindingEditor.cs
rename to Editor/AnimatorParameterBindingEditor.cs
index d4c2e01..8120a88 100644
--- a/UnityWeld_Editor/AnimatorParameterBindingEditor.cs
+++ b/Editor/AnimatorParameterBindingEditor.cs
@@ -21,17 +21,12 @@ public class AnimatorParameterBindingEditor : BaseBindingEditor
private bool viewModelPropertyPrefabModified;
private bool viewPropertyPrefabModified;
- private void OnEnable()
+ protected override void OnEnabled()
{
// Initialise reference to target script
targetScript = (AnimatorParameterBinding)target;
- Type adapterType;
-
- viewAdapterOptionsFade = new AnimBool(
- ShouldShowAdapterOptions(targetScript.ViewAdapterTypeName, out adapterType)
- );
-
+ viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
}
@@ -40,17 +35,11 @@ private void OnDisable()
viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
}
- public override void OnInspectorGUI()
+ protected override void OnInspector()
{
- if(CannotModifyInPlayMode())
- {
- return;
- }
-
UpdatePrefabModifiedProperties();
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = viewPropertyPrefabModified ? FontStyle.Bold : defaultLabelStyle;
+ EditorStyles.label.fontStyle = viewPropertyPrefabModified ? FontStyle.Bold : DefaultFontStyle;
var animatorParameters = GetAnimatorParameters();
@@ -60,7 +49,6 @@ public override void OnInspectorGUI()
return;
}
- Type viewPropertyType;
ShowAnimatorParametersMenu(
new GUIContent("View property", "Property on the view to bind to"),
updatedValue =>
@@ -70,7 +58,7 @@ public override void OnInspectorGUI()
},
new AnimatorParameterTypeAndName(targetScript.AnimatorParameterName, targetScript.AnimatorParameterType),
animatorParameters,
- out viewPropertyType
+ out var viewPropertyType
);
// Don't let the user set anything else until they've chosen a view property.
@@ -80,39 +68,36 @@ out viewPropertyType
GUI.enabled = false;
}
- var viewAdapterTypeNames = GetAdapterTypeNames(
- type => viewPropertyType == null ||
- TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType
- );
+ var viewAdapterTypeNames = TypeResolver.GetAdapterIds(
+ adapterInfo => viewPropertyType == null || adapterInfo.OutType == viewPropertyType);
- EditorStyles.label.fontStyle = viewAdapterPrefabModified ? FontStyle.Bold : defaultLabelStyle;
+ EditorStyles.label.fontStyle = viewAdapterPrefabModified ? FontStyle.Bold : DefaultFontStyle;
ShowAdapterMenu(
new GUIContent("View adapter", "Adapter that converts values sent from the view-model to the view."),
viewAdapterTypeNames,
- targetScript.ViewAdapterTypeName,
+ targetScript.ViewAdapterId,
newValue =>
{
// Get rid of old adapter options if we changed the type of the adapter.
- if (newValue != targetScript.ViewAdapterTypeName)
+ if (newValue != targetScript.ViewAdapterId)
{
Undo.RecordObject(targetScript, "Set view adapter options");
targetScript.ViewAdapterOptions = null;
}
UpdateProperty(
- updatedValue => targetScript.ViewAdapterTypeName = updatedValue,
- targetScript.ViewAdapterTypeName,
+ updatedValue => targetScript.ViewAdapterId = updatedValue,
+ targetScript.ViewAdapterId,
newValue,
"Set view adapter"
);
}
);
- Type adapterType;
- viewAdapterOptionsFade.target = ShouldShowAdapterOptions(targetScript.ViewAdapterTypeName, out adapterType);
+ viewAdapterOptionsFade.target = ShouldShowAdapterOptions(targetScript.ViewAdapterId, out var adapterType);
- EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified ? FontStyle.Bold : defaultLabelStyle;
+ EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified ? FontStyle.Bold : DefaultFontStyle;
ShowAdapterOptionsMenu(
"View adapter options",
@@ -124,9 +109,9 @@ out viewPropertyType
EditorGUILayout.Space();
- EditorStyles.label.fontStyle = viewModelPropertyPrefabModified ? FontStyle.Bold : defaultLabelStyle;
+ EditorStyles.label.fontStyle = viewModelPropertyPrefabModified ? FontStyle.Bold : DefaultFontStyle;
- var adaptedViewPropertyType = AdaptTypeBackward(viewPropertyType, targetScript.ViewAdapterTypeName);
+ var adaptedViewPropertyType = AdaptTypeBackward(viewPropertyType, targetScript.ViewAdapterId);
ShowViewModelPropertyMenu(
new GUIContent("View-model property", "Property on the view-model to bind to."),
TypeResolver.FindBindableProperties(targetScript),
@@ -137,8 +122,6 @@ out viewPropertyType
GUI.enabled = guiPreviouslyEnabled;
- EditorStyles.label.fontStyle = defaultLabelStyle;
-
EditorGUILayout.Space();
}
diff --git a/Editor/AnimatorParameterBindingEditor.cs.meta b/Editor/AnimatorParameterBindingEditor.cs.meta
new file mode 100644
index 0000000..bade969
--- /dev/null
+++ b/Editor/AnimatorParameterBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 922b7495c089af14c907aceb395b0cba
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/BaseBindingEditor.cs b/Editor/BaseBindingEditor.cs
similarity index 85%
rename from UnityWeld_Editor/BaseBindingEditor.cs
rename to Editor/BaseBindingEditor.cs
index 1339474..e335f31 100644
--- a/UnityWeld_Editor/BaseBindingEditor.cs
+++ b/Editor/BaseBindingEditor.cs
@@ -1,390 +1,410 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using UnityEditor;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- ///
- /// A base editor for Unity-Weld bindings.
- ///
- public class BaseBindingEditor : Editor
- {
- ///
- /// Sets the specified value and sets dirty to true if it doesn't match the old value.
- ///
- protected void UpdateProperty(Action setter, TValue oldValue, TValue newValue, string undoActionName)
- where TValue : class
- {
- if (newValue == oldValue)
- {
- return;
- }
-
- Undo.RecordObject(target, undoActionName);
-
- setter(newValue);
-
- InspectorUtils.MarkSceneDirty(((Component)target).gameObject);
- }
-
- ///
- /// Display the adapters popup menu.
- ///
- protected static void ShowAdapterMenu(
- GUIContent label,
- string[] adapterTypeNames,
- string curValue,
- Action valueUpdated
- )
- {
- var adapterMenu = new[] { "None" }
- .Concat(adapterTypeNames)
- .Select(typeName => new GUIContent(typeName))
- .ToArray();
-
- var curSelectionIndex = Array.IndexOf(adapterTypeNames, curValue) + 1; // +1 to account for 'None'.
- var newSelectionIndex = EditorGUILayout.Popup(
- label,
- curSelectionIndex,
- adapterMenu
- );
-
- if (newSelectionIndex == curSelectionIndex)
- {
- return;
- }
-
- if (newSelectionIndex == 0)
- {
- valueUpdated(null); // No adapter selected.
- }
- else
- {
- valueUpdated(adapterTypeNames[newSelectionIndex - 1]); // -1 to account for 'None'.
- }
- }
-
- ///
- /// Display a popup menu for selecting a property from a view-model.
- ///
- protected void ShowViewModelPropertyMenu(
- GUIContent label,
- BindableMember[] bindableProperties,
- Action propertyValueSetter,
- string curPropertyValue,
- Func menuEnabled
- )
- {
- InspectorUtils.DoPopup(
- new GUIContent(curPropertyValue),
- label,
- prop => string.Concat(prop.ViewModelType, "/", prop.MemberName, " : ", prop.Member.PropertyType.Name),
- prop => menuEnabled(prop.Member),
- prop => prop.ToString() == curPropertyValue,
- prop =>
- {
- UpdateProperty(
- propertyValueSetter,
- curPropertyValue,
- prop.ToString(),
- "Set view-model property"
- );
- },
- bindableProperties
- .OrderBy(property => property.ViewModelTypeName)
- .ThenBy(property => property.MemberName)
- .ToArray()
- );
- }
-
- ///
- /// Class used to wrap property infos
- ///
- private class OptionInfo
- {
- public OptionInfo(string menuName, BindableMember property)
- {
- this.MenuName = menuName;
- this.Property = property;
- }
-
- public string MenuName { get; private set; }
-
- public BindableMember Property { get; private set; }
- }
-
- ///
- /// The string used to show that no option is selected in the property menu.
- ///
- private static readonly string NoneOptionString = "None";
-
- ///
- /// Display a popup menu for selecting a property from a view-model.
- ///
- protected void ShowViewModelPropertyMenuWithNone(
- GUIContent label,
- BindableMember[] bindableProperties,
- Action propertyValueSetter,
- string curPropertyValue,
- Func menuEnabled
- )
- {
- var options = bindableProperties
- .Select(prop => new OptionInfo(
- string.Concat(prop.ViewModelType, "/", prop.MemberName, " : ", prop.Member.PropertyType.Name),
- prop
- ))
- .OrderBy(option => option.Property.ViewModelTypeName)
- .ThenBy(option => option.Property.MemberName);
-
- var noneOption = new OptionInfo(NoneOptionString, null);
-
- InspectorUtils.DoPopup(
- new GUIContent(string.IsNullOrEmpty(curPropertyValue) ? NoneOptionString : curPropertyValue),
- label,
- option => option.MenuName,
- option => option.MenuName == NoneOptionString ? true : menuEnabled(option.Property.Member),
- option =>
- {
- if (option == noneOption)
- {
- return string.IsNullOrEmpty(curPropertyValue);
- }
-
- return option.ToString() == curPropertyValue;
- },
- option => UpdateProperty(
- propertyValueSetter,
- curPropertyValue,
- option.Property == null ? string.Empty : option.ToString(),
- "Set view-model property"
- ),
- new[] { noneOption }
- .Concat(options)
- .ToArray()
- );
- }
-
- ///
- /// Shows a dropdown for selecting a property in the UI to bind to.
- ///
- protected void ShowViewPropertyMenu(
- GUIContent label,
- BindableMember[] properties,
- Action propertyValueSetter,
- string curPropertyValue,
- out Type selectedPropertyType
- )
- {
- var propertyNames = properties
- .Select(m => m.ToString())
- .ToArray();
- var selectedIndex = Array.IndexOf(propertyNames, curPropertyValue);
- var content = properties.Select(prop => new GUIContent(string.Concat(
- prop.ViewModelTypeName,
- "/",
- prop.MemberName,
- " : ",
- prop.Member.PropertyType.Name
- )))
- .ToArray();
-
- var newSelectedIndex = EditorGUILayout.Popup(label, selectedIndex, content);
- if (newSelectedIndex != selectedIndex)
- {
- var newSelectedProperty = properties[newSelectedIndex];
-
- UpdateProperty(
- propertyValueSetter,
- curPropertyValue,
- newSelectedProperty.ToString(),
- "Set view property"
- );
-
- selectedPropertyType = newSelectedProperty.Member.PropertyType;
- }
- else
- {
- if (selectedIndex < 0)
- {
- selectedPropertyType = null;
- return;
- }
-
- selectedPropertyType = properties[selectedIndex].Member.PropertyType;
- }
- }
-
- ///
- /// Show dropdown for selecting a UnityEvent to bind to.
- ///
- protected void ShowEventMenu(
- BindableEvent[] events,
- Action propertyValueSetter,
- string curPropertyValue
- )
- {
- var eventNames = events
- .Select(BindableEventToString)
- .ToArray();
- var selectedIndex = Array.IndexOf(eventNames, curPropertyValue);
- var content = events
- .Select(evt => new GUIContent(evt.ComponentType.Name + "." + evt.Name))
- .ToArray();
-
- var newSelectedIndex = EditorGUILayout.Popup(
- new GUIContent("View event", "Event on the view to bind to."),
- selectedIndex,
- content
- );
-
- if (newSelectedIndex == selectedIndex)
- {
- return;
- }
-
- var selectedEvent = events[newSelectedIndex];
- UpdateProperty(
- propertyValueSetter,
- curPropertyValue,
- BindableEventToString(selectedEvent),
- "Set bound event"
- );
- }
-
- ///
- /// Returns whether or not we should show an adapter options selector for the specified
- /// adapter type and finds the type for the specified type name.
- ///
- protected static bool ShouldShowAdapterOptions(string adapterTypeName, out Type adapterType)
- {
- // Don't show selector until an adapter has been selected.
- if (string.IsNullOrEmpty(adapterTypeName))
- {
- adapterType = null;
- return false;
- }
-
- var adapterAttribute = FindAdapterAttribute(adapterTypeName);
- if (adapterAttribute == null)
- {
- adapterType = null;
- return false;
- }
-
- adapterType = adapterAttribute.OptionsType;
-
- // Don't show selector unless the current adapter has its own overridden
- // adapter options type.
- return adapterType != typeof(AdapterOptions);
- }
-
- ///
- /// Show a field for selecting an AdapterOptions object matching the specified type of adapter.
- ///
- protected void ShowAdapterOptionsMenu(
- string label,
- Type adapterOptionsType,
- Action propertyValueSetter,
- AdapterOptions currentPropertyValue,
- float fadeAmount
- )
- {
- if (EditorGUILayout.BeginFadeGroup(fadeAmount))
- {
- EditorGUI.indentLevel++;
-
- var newAdapterOptions = (AdapterOptions)EditorGUILayout.ObjectField(
- label,
- currentPropertyValue,
- adapterOptionsType,
- false
- );
-
- EditorGUI.indentLevel--;
-
- UpdateProperty(
- propertyValueSetter,
- currentPropertyValue,
- newAdapterOptions,
- "Set adapter options"
- );
- }
- EditorGUILayout.EndFadeGroup();
- }
-
- ///
- /// Displays helpbox in inspector if the editor is playing, and returns the same thing
- ///
- protected static bool CannotModifyInPlayMode()
- {
- if (EditorApplication.isPlaying)
- {
- EditorGUILayout.HelpBox("Exit play mode to make changes.", MessageType.Info);
- return true;
- }
- return false;
- }
-
- ///
- /// Find the adapter attribute for a named adapter type.
- ///
- protected static AdapterAttribute FindAdapterAttribute(string adapterName)
- {
- if (!string.IsNullOrEmpty(adapterName))
- {
- var adapterType = TypeResolver.FindAdapterType(adapterName);
- if (adapterType != null)
- {
- return TypeResolver.FindAdapterAttribute(adapterType);
- }
- }
-
- return null;
- }
-
- ///
- /// Pass a type through an adapter and get the result.
- ///
- protected static Type AdaptTypeBackward(Type inputType, string adapterName)
- {
- var adapterAttribute = FindAdapterAttribute(adapterName);
-
- return adapterAttribute != null ? adapterAttribute.InputType : inputType;
- }
-
- ///
- /// Pass a type through an adapter and get the result.
- ///
- protected static Type AdaptTypeForward(Type inputType, string adapterName)
- {
- var adapterAttribute = FindAdapterAttribute(adapterName);
-
- return adapterAttribute != null ? adapterAttribute.OutputType : inputType;
- }
-
- ///
- /// Convert a BindableEvent to a uniquely identifiable string.
- ///
- private static string BindableEventToString(BindableEvent evt)
- {
- return string.Concat(evt.ComponentType.ToString(), ".", evt.Name);
- }
-
- ///
- /// Returns an array of all the names of adapter types that match the
- /// provided prediate function.
- ///
- protected static string[] GetAdapterTypeNames(Func adapterSelectionPredicate)
- {
- return TypeResolver.TypesWithAdapterAttribute
- .Where(adapterSelectionPredicate)
- .Select(type => type.ToString())
- .ToArray();
- }
- }
-}
+using System;
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ ///
+ /// A base editor for Unity-Weld bindings.
+ ///
+ public abstract class BaseBindingEditor : Editor
+ {
+ protected FontStyle DefaultFontStyle;
+ private SerializedProperty _autoConnectionProperty;
+
+ ///
+ /// Sets the specified value and sets dirty to true if it doesn't match the old value.
+ ///
+ protected void UpdateProperty(Action setter, TValue oldValue, TValue newValue, string undoActionName)
+ where TValue : class
+ {
+ if (newValue == oldValue)
+ {
+ return;
+ }
+
+ Undo.RecordObject(target, undoActionName);
+
+ setter(newValue);
+
+ InspectorUtils.MarkSceneDirty(((Component)target).gameObject);
+ }
+
+ ///
+ /// Display the adapters popup menu.
+ ///
+ protected static void ShowAdapterMenu(
+ GUIContent label,
+ string[] adapterTypeNames,
+ string curValue,
+ Action valueUpdated
+ )
+ {
+ var adapterMenu = new[] { "None" }
+ .Concat(adapterTypeNames)
+ .Select(typeName => new GUIContent(typeName))
+ .ToArray();
+
+ var curSelectionIndex = Array.IndexOf(adapterTypeNames, curValue) + 1; // +1 to account for 'None'.
+ var newSelectionIndex = EditorGUILayout.Popup(
+ label,
+ curSelectionIndex,
+ adapterMenu
+ );
+
+ if (newSelectionIndex == curSelectionIndex)
+ {
+ return;
+ }
+
+ if (newSelectionIndex == 0)
+ {
+ valueUpdated(null); // No adapter selected.
+ }
+ else
+ {
+ valueUpdated(adapterTypeNames[newSelectionIndex - 1]); // -1 to account for 'None'.
+ }
+ }
+
+ protected abstract void OnEnabled();
+ protected abstract void OnInspector();
+
+ private void OnEnable()
+ {
+ _autoConnectionProperty = serializedObject.FindProperty("_isAutoConnection");
+ OnEnabled();
+ }
+
+ public sealed override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ if (CannotModifyInPlayMode())
+ {
+ GUI.enabled = false;
+ }
+
+ DefaultFontStyle = EditorStyles.label.fontStyle;
+
+ if (_autoConnectionProperty != null)
+ {
+ EditorGUILayout.PropertyField(_autoConnectionProperty);
+ }
+
+ OnInspector();
+
+ serializedObject.ApplyModifiedProperties();
+
+ EditorStyles.label.fontStyle = DefaultFontStyle;
+ }
+
+ protected void ShowAutoConnection(bool currentValue, Action valueSetter)
+ {
+ var newValue = EditorGUILayout.Toggle("Auto Connection", currentValue);
+ if (newValue != currentValue)
+ {
+ valueSetter(newValue);
+ }
+ }
+
+ ///
+ /// Display a popup menu for selecting a property from a view-model.
+ ///
+ protected void ShowViewModelPropertyMenu(
+ GUIContent label,
+ BindableMember[] bindableProperties,
+ Action propertyValueSetter,
+ string curPropertyValue,
+ Func menuEnabled
+ )
+ {
+ InspectorUtils.DoPopup(
+ new GUIContent(curPropertyValue),
+ label,
+ prop => string.Concat(prop.ViewModelType, "/", prop.MemberName, " : ", prop.Member.PropertyType.Name),
+ prop => menuEnabled(prop.Member),
+ prop => prop.ToString() == curPropertyValue,
+ prop =>
+ {
+ UpdateProperty(
+ propertyValueSetter,
+ curPropertyValue,
+ prop.ToString(),
+ "Set view-model property"
+ );
+ },
+ bindableProperties
+ .OrderBy(property => property.ViewModelTypeName)
+ .ThenBy(property => property.MemberName)
+ .ToArray()
+ );
+ }
+
+ ///
+ /// Class used to wrap property infos
+ ///
+ private class OptionInfo
+ {
+ public OptionInfo(string menuName, BindableMember property)
+ {
+ this.MenuName = menuName;
+ this.Property = property;
+ }
+
+ public string MenuName { get; private set; }
+
+ public BindableMember Property { get; private set; }
+ }
+
+ ///
+ /// The string used to show that no option is selected in the property menu.
+ ///
+ private static readonly string NoneOptionString = "None";
+
+ ///
+ /// Display a popup menu for selecting a property from a view-model.
+ ///
+ protected void ShowViewModelPropertyMenuWithNone(
+ GUIContent label,
+ BindableMember[] bindableProperties,
+ Action propertyValueSetter,
+ string curPropertyValue,
+ Func menuEnabled
+ )
+ {
+ var options = bindableProperties
+ .Select(prop => new OptionInfo(
+ string.Concat(prop.ViewModelType, "/", prop.MemberName, " : ", prop.Member.PropertyType.Name),
+ prop
+ ))
+ .OrderBy(option => option.Property.ViewModelTypeName)
+ .ThenBy(option => option.Property.MemberName);
+
+ var noneOption = new OptionInfo(NoneOptionString, null);
+
+ InspectorUtils.DoPopup(
+ new GUIContent(string.IsNullOrEmpty(curPropertyValue) ? NoneOptionString : curPropertyValue),
+ label,
+ option => option.MenuName,
+ option => option.MenuName == NoneOptionString ? true : menuEnabled(option.Property.Member),
+ option =>
+ {
+ if (option == noneOption)
+ {
+ return string.IsNullOrEmpty(curPropertyValue);
+ }
+
+ return option.ToString() == curPropertyValue;
+ },
+ option => UpdateProperty(
+ propertyValueSetter,
+ curPropertyValue,
+ option.Property == null ? string.Empty : option.ToString(),
+ "Set view-model property"
+ ),
+ new[] { noneOption }
+ .Concat(options)
+ .ToArray()
+ );
+ }
+
+ ///
+ /// Shows a dropdown for selecting a property in the UI to bind to.
+ ///
+ protected void ShowViewPropertyMenu(
+ GUIContent label,
+ BindableMember[] properties,
+ Action propertyValueSetter,
+ string curPropertyValue,
+ out Type selectedPropertyType
+ )
+ {
+ var propertyNames = properties
+ .Select(m => m.ToString())
+ .ToArray();
+ var selectedIndex = Array.IndexOf(propertyNames, curPropertyValue);
+ var content = properties.Select(prop => new GUIContent(string.Concat(
+ prop.ViewModelTypeName,
+ "/",
+ prop.MemberName,
+ " : ",
+ prop.Member.PropertyType.Name
+ )))
+ .ToArray();
+
+ var newSelectedIndex = EditorGUILayout.Popup(label, selectedIndex, content);
+ if (newSelectedIndex != selectedIndex)
+ {
+ var newSelectedProperty = properties[newSelectedIndex];
+
+ UpdateProperty(
+ propertyValueSetter,
+ curPropertyValue,
+ newSelectedProperty.ToString(),
+ "Set view property"
+ );
+
+ selectedPropertyType = newSelectedProperty.Member.PropertyType;
+ }
+ else
+ {
+ if (selectedIndex < 0)
+ {
+ selectedPropertyType = null;
+ return;
+ }
+
+ selectedPropertyType = properties[selectedIndex].Member.PropertyType;
+ }
+ }
+
+ ///
+ /// Show dropdown for selecting a UnityEvent to bind to.
+ ///
+ protected void ShowEventMenu(
+ BindableEvent[] events,
+ Action propertyValueSetter,
+ string curPropertyValue
+ )
+ {
+ var eventNames = events
+ .Select(BindableEventToString)
+ .ToArray();
+ var selectedIndex = Array.IndexOf(eventNames, curPropertyValue);
+ var content = events
+ .Select(evt => new GUIContent(evt.ComponentType.Name + "." + evt.Name))
+ .ToArray();
+
+ var newSelectedIndex = EditorGUILayout.Popup(
+ new GUIContent("View event", "Event on the view to bind to."),
+ selectedIndex,
+ content
+ );
+
+ if (newSelectedIndex == selectedIndex)
+ {
+ return;
+ }
+
+ var selectedEvent = events[newSelectedIndex];
+ UpdateProperty(
+ propertyValueSetter,
+ curPropertyValue,
+ BindableEventToString(selectedEvent),
+ "Set bound event"
+ );
+ }
+
+ ///
+ /// Returns whether or not we should show an adapter options selector for the specified
+ /// adapter type and finds the type for the specified type name.
+ ///
+ protected static bool ShouldShowAdapterOptions(string adapterId, out Type adapterType)
+ {
+ adapterType = null;
+
+ // Don't show selector until an adapter has been selected.
+ if (string.IsNullOrEmpty(adapterId))
+ {
+ return false;
+ }
+
+ if (!TypeResolver.TryGetAdapter(adapterId, out var adapterInfo))
+ {
+ return false;
+ }
+
+ adapterType = adapterInfo.OptionsType;
+
+ // Don't show selector unless the current adapter has its own overridden
+ // adapter options type.
+ return adapterType != typeof(AdapterOptions);
+ }
+
+ ///
+ /// Show a field for selecting an AdapterOptions object matching the specified type of adapter.
+ ///
+ protected void ShowAdapterOptionsMenu(
+ string label,
+ Type adapterOptionsType,
+ Action propertyValueSetter,
+ AdapterOptions currentPropertyValue,
+ float fadeAmount
+ )
+ {
+ if (EditorGUILayout.BeginFadeGroup(fadeAmount))
+ {
+ EditorGUI.indentLevel++;
+
+ var newAdapterOptions = (AdapterOptions)EditorGUILayout.ObjectField(
+ label,
+ currentPropertyValue,
+ adapterOptionsType,
+ false
+ );
+
+ EditorGUI.indentLevel--;
+
+ UpdateProperty(
+ propertyValueSetter,
+ currentPropertyValue,
+ newAdapterOptions,
+ "Set adapter options"
+ );
+ }
+ EditorGUILayout.EndFadeGroup();
+ }
+
+ ///
+ /// Displays helpbox in inspector if the editor is playing, and returns the same thing
+ ///
+ protected static bool CannotModifyInPlayMode()
+ {
+ if (EditorApplication.isPlaying)
+ {
+ EditorGUILayout.HelpBox("Exit play mode to make changes.", MessageType.Info);
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Pass a type through an adapter and get the result.
+ ///
+ protected static Type AdaptTypeBackward(Type inputType, string adapterId)
+ {
+ if (!TypeResolver.TryGetAdapter(adapterId, out var adapterInfo))
+ {
+ return inputType;
+ }
+
+ return adapterInfo.InType;
+ }
+
+ ///
+ /// Pass a type through an adapter and get the result.
+ ///
+ protected static Type AdaptTypeForward(Type inputType, string adapterId)
+ {
+ if (!TypeResolver.TryGetAdapter(adapterId, out var adapterInfo))
+ {
+ return inputType;
+ }
+
+ return adapterInfo.OutType;
+ }
+
+ ///
+ /// Convert a BindableEvent to a uniquely identifiable string.
+ ///
+ private static string BindableEventToString(BindableEvent evt)
+ {
+ return string.Concat(evt.ComponentType.ToString(), ".", evt.Name);
+ }
+ }
+}
diff --git a/Editor/BaseBindingEditor.cs.meta b/Editor/BaseBindingEditor.cs.meta
new file mode 100644
index 0000000..c134762
--- /dev/null
+++ b/Editor/BaseBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b2109485ee1df45439d88eef4c5b03a5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/CollectionBindingEditor.cs b/Editor/CollectionBindingEditor.cs
new file mode 100644
index 0000000..4462a3c
--- /dev/null
+++ b/Editor/CollectionBindingEditor.cs
@@ -0,0 +1,66 @@
+using UnityEditor;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(CollectionBinding))]
+ class CollectionBindingEditor : BaseBindingEditor
+ {
+ private CollectionBinding _targetScript;
+ private SerializedProperty _templateInitialPoolCountProperty;
+ private SerializedProperty _itemsContainerProperty;
+ private SerializedProperty _templatesProperty;
+
+ private bool _viewModelPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ // Initialise everything
+ _targetScript = (CollectionBinding)target;
+ _templateInitialPoolCountProperty = serializedObject.FindProperty("_templateInitialPoolCount");
+ _itemsContainerProperty = serializedObject.FindProperty("_itemsContainer");
+ _templatesProperty = serializedObject.FindProperty("_templates");
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ EditorGUILayout.PropertyField(_templateInitialPoolCountProperty);
+ EditorGUILayout.PropertyField(_itemsContainerProperty);
+ EditorGUILayout.PropertyField(_templatesProperty, true);
+
+ EditorStyles.label.fontStyle = _viewModelPrefabModified ? FontStyle.Bold : DefaultFontStyle;
+ ShowViewModelPropertyMenu(
+ new GUIContent("View-model property", "Property on the view-model to bind to."),
+ TypeResolver.FindBindableCollectionProperties(_targetScript),
+ updatedValue => _targetScript.ViewModelPropertyName = updatedValue,
+ _targetScript.ViewModelPropertyName,
+ property => true
+ );
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewModelPropertyName":
+ _viewModelPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/CollectionBindingEditor.cs.meta b/Editor/CollectionBindingEditor.cs.meta
new file mode 100644
index 0000000..ee4f03c
--- /dev/null
+++ b/Editor/CollectionBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5a455a8f5047aca4fa9c7c67ef994596
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/EventBindingEditor.cs b/Editor/EventBindingEditor.cs
similarity index 87%
rename from UnityWeld_Editor/EventBindingEditor.cs
rename to Editor/EventBindingEditor.cs
index 49fd5f1..808432b 100644
--- a/UnityWeld_Editor/EventBindingEditor.cs
+++ b/Editor/EventBindingEditor.cs
@@ -1,110 +1,102 @@
-using System.Linq;
-using System.Reflection;
-using UnityEditor;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- [CustomEditor(typeof(EventBinding))]
- public class EventBindingEditor : BaseBindingEditor
- {
- private EventBinding targetScript;
-
- // Whether or not the values on our target match its prefab.
- private bool viewEventPrefabModified;
- private bool viewModelMethodPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (EventBinding)target;
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = viewEventPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowEventMenu(
- UnityEventWatcher.GetBindableEvents(targetScript.gameObject)
- .OrderBy(evt => evt.Name)
- .ToArray(),
- updatedValue => targetScript.ViewEventName = updatedValue,
- targetScript.ViewEventName
- );
-
- EditorStyles.label.fontStyle = viewModelMethodPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowMethodMenu(targetScript, TypeResolver.FindBindableMethods(targetScript));
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
- }
-
- ///
- /// Draws the dropdown for selecting a method from bindableViewModelMethods
- ///
- private void ShowMethodMenu(
- EventBinding targetScript,
- BindableMember[] bindableMethods
- )
- {
- var tooltip = "Method on the view-model to bind to.";
-
- InspectorUtils.DoPopup(
- new GUIContent(targetScript.ViewModelMethodName),
- new GUIContent("View-model method", tooltip),
- m => m.ViewModelType + "/" + m.MemberName,
- m => true,
- m => m.ToString() == targetScript.ViewModelMethodName,
- m => UpdateProperty(
- updatedValue => targetScript.ViewModelMethodName = updatedValue,
- targetScript.ViewModelMethodName,
- m.ToString(),
- "Set bound view-model method"
- ),
- bindableMethods
- .OrderBy(m => m.ViewModelTypeName)
- .ThenBy(m => m.MemberName)
- .ToArray()
- );
- }
-
- ///
- /// Check whether each of the properties on the object have been changed
- /// from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it,
- // Next(false) will iterate through the properties.
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewEventName":
- viewEventPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelMethodName":
- viewModelMethodPrefabModified = property.prefabOverride;
- break;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(EventBinding))]
+ public class EventBindingEditor : BaseBindingEditor
+ {
+ private EventBinding targetScript;
+
+ // Whether or not the values on our target match its prefab.
+ private bool viewEventPrefabModified;
+ private bool viewModelMethodPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (EventBinding)target;
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ EditorStyles.label.fontStyle = viewEventPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowEventMenu(
+ UnityEventWatcher.GetBindableEvents(targetScript.gameObject)
+ .OrderBy(evt => evt.Name)
+ .ToArray(),
+ updatedValue => targetScript.ViewEventName = updatedValue,
+ targetScript.ViewEventName
+ );
+
+ EditorStyles.label.fontStyle = viewModelMethodPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowMethodMenu(targetScript, TypeResolver.FindBindableMethods(targetScript));
+ }
+
+ ///
+ /// Draws the dropdown for selecting a method from bindableViewModelMethods
+ ///
+ private void ShowMethodMenu(
+ EventBinding targetScript,
+ BindableMember[] bindableMethods
+ )
+ {
+ var tooltip = "Method on the view-model to bind to.";
+
+ InspectorUtils.DoPopup(
+ new GUIContent(targetScript.ViewModelMethodName),
+ new GUIContent("View-model method", tooltip),
+ m => m.ViewModelType + "/" + m.MemberName,
+ m => true,
+ m => m.ToString() == targetScript.ViewModelMethodName,
+ m => UpdateProperty(
+ updatedValue => targetScript.ViewModelMethodName = updatedValue,
+ targetScript.ViewModelMethodName,
+ m.ToString(),
+ "Set bound view-model method"
+ ),
+ bindableMethods
+ .OrderBy(m => m.ViewModelTypeName)
+ .ThenBy(m => m.MemberName)
+ .ToArray()
+ );
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed
+ /// from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it,
+ // Next(false) will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewEventName":
+ viewEventPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelMethodName":
+ viewModelMethodPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/EventBindingEditor.cs.meta b/Editor/EventBindingEditor.cs.meta
new file mode 100644
index 0000000..2347c6c
--- /dev/null
+++ b/Editor/EventBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 375353919d68d544fadc2369d1a012d5
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/InspectorUtils.cs b/Editor/InspectorUtils.cs
similarity index 97%
rename from UnityWeld_Editor/InspectorUtils.cs
rename to Editor/InspectorUtils.cs
index 5d005f7..bb993dd 100644
--- a/UnityWeld_Editor/InspectorUtils.cs
+++ b/Editor/InspectorUtils.cs
@@ -1,141 +1,141 @@
-using System;
-using UnityEditor;
-using UnityEditor.SceneManagement;
-using UnityEngine;
-
-namespace UnityWeld_Editor
-{
- ///
- /// Common utilities for custom inspectors.
- ///
- internal class InspectorUtils
- {
- ///
- /// Show a popup menu with some items disabled and a label to its left.
- ///
- public static void DoPopup(
- GUIContent content,
- GUIContent label,
- Func menuName,
- Func menuEnabled,
- Func isSelected,
- Action callback,
- T[] items)
- {
- var labelRect = EditorGUILayout.GetControlRect(false, 16f, EditorStyles.popup);
- var controlId = GUIUtility.GetControlID(FocusType.Keyboard, labelRect);
-
- var buttonRect = EditorGUI.PrefixLabel(labelRect, controlId, label);
-
- ShowPopupButton(
- buttonRect,
- labelRect,
- controlId,
- content,
- () => ShowMenu(menuName, menuEnabled, isSelected, callback, items, buttonRect)
- );
- }
-
- ///
- /// Shows the button for a popup/dropdown control, with a label.
- ///
- private static void ShowPopupButton(Rect buttonRect, Rect labelRect, int controlId, GUIContent currentlySelected, Action popup)
- {
- var currentEvent = Event.current;
- var eventType = currentEvent.type;
- var style = EditorStyles.popup;
-
- switch (eventType)
- {
- case EventType.KeyDown:
- if (MainActionKeyForControl(currentEvent, controlId))
- {
- popup();
- currentEvent.Use();
- }
- break;
-
- case EventType.Repaint:
- style.Draw(buttonRect, currentlySelected, controlId, false);
- break;
-
- case EventType.MouseDown:
- if (currentEvent.button != 0)
- {
- return;
- }
-
- if (buttonRect.Contains(currentEvent.mousePosition))
- {
- popup();
- GUIUtility.keyboardControl = controlId;
- currentEvent.Use();
- }
- else if (labelRect.Contains(currentEvent.mousePosition))
- {
- GUIUtility.keyboardControl = controlId;
- currentEvent.Use();
- }
- break;
- }
- }
-
- ///
- /// Returns whether the specified control has been activated by a key press.
- ///
- private static bool MainActionKeyForControl(Event evt, int controlId)
- {
- if (GUIUtility.keyboardControl != controlId)
- {
- return false;
- }
- bool modifierPressed = evt.alt || evt.shift || evt.command || evt.control;
- if (!modifierPressed && evt.type == EventType.KeyDown && evt.character == ' ')
- {
- evt.Use();
- return false;
- }
- return evt.type == EventType.KeyDown
- && (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
- && !modifierPressed;
- }
-
- ///
- /// Show a menu with some items disabled. Has a callback that will be called when an item is selected with the index of the selected item.
- /// Takes a dictionary of options and whether or not they should be enabled.
- ///
- private static void ShowMenu(Func menuName, Func menuEnabled, Func isSelected, Action callback, T[] items, Rect position)
- {
- var menu = new GenericMenu();
-
- for (var i = 0; i < items.Length; i++)
- {
- // Need to cache index so that it doesn't get passed through to the callback by reference.
- int index = i;
- var item = items[index];
-
- var content = new GUIContent(menuName(item));
-
- if (menuEnabled(item))
- {
- menu.AddItem(content, isSelected(item), () => callback(item));
- }
- else
- {
- menu.AddDisabledItem(content);
- }
- }
-
- menu.DropDown(position);
- }
-
- ///
- /// Tell Unity that a change has been made to a specified object and we have to save the scene.
- ///
- public static void MarkSceneDirty(GameObject gameObject)
- {
- // TODO: Undo.RecordObject also marks the scene dirty, so this will no longer be necessary once undo support is added.
- EditorSceneManager.MarkSceneDirty(gameObject.scene);
- }
- }
-}
+using System;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+
+namespace UnityWeld_Editor
+{
+ ///
+ /// Common utilities for custom inspectors.
+ ///
+ internal class InspectorUtils
+ {
+ ///
+ /// Show a popup menu with some items disabled and a label to its left.
+ ///
+ public static void DoPopup(
+ GUIContent content,
+ GUIContent label,
+ Func menuName,
+ Func menuEnabled,
+ Func isSelected,
+ Action callback,
+ T[] items)
+ {
+ var labelRect = EditorGUILayout.GetControlRect(false, 16f, EditorStyles.popup);
+ var controlId = GUIUtility.GetControlID(FocusType.Keyboard, labelRect);
+
+ var buttonRect = EditorGUI.PrefixLabel(labelRect, controlId, label);
+
+ ShowPopupButton(
+ buttonRect,
+ labelRect,
+ controlId,
+ content,
+ () => ShowMenu(menuName, menuEnabled, isSelected, callback, items, buttonRect)
+ );
+ }
+
+ ///
+ /// Shows the button for a popup/dropdown control, with a label.
+ ///
+ private static void ShowPopupButton(Rect buttonRect, Rect labelRect, int controlId, GUIContent currentlySelected, Action popup)
+ {
+ var currentEvent = Event.current;
+ var eventType = currentEvent.type;
+ var style = EditorStyles.popup;
+
+ switch (eventType)
+ {
+ case EventType.KeyDown:
+ if (MainActionKeyForControl(currentEvent, controlId))
+ {
+ popup();
+ currentEvent.Use();
+ }
+ break;
+
+ case EventType.Repaint:
+ style.Draw(buttonRect, currentlySelected, controlId, false);
+ break;
+
+ case EventType.MouseDown:
+ if (currentEvent.button != 0)
+ {
+ return;
+ }
+
+ if (buttonRect.Contains(currentEvent.mousePosition))
+ {
+ popup();
+ GUIUtility.keyboardControl = controlId;
+ currentEvent.Use();
+ }
+ else if (labelRect.Contains(currentEvent.mousePosition))
+ {
+ GUIUtility.keyboardControl = controlId;
+ currentEvent.Use();
+ }
+ break;
+ }
+ }
+
+ ///
+ /// Returns whether the specified control has been activated by a key press.
+ ///
+ private static bool MainActionKeyForControl(Event evt, int controlId)
+ {
+ if (GUIUtility.keyboardControl != controlId)
+ {
+ return false;
+ }
+ bool modifierPressed = evt.alt || evt.shift || evt.command || evt.control;
+ if (!modifierPressed && evt.type == EventType.KeyDown && evt.character == ' ')
+ {
+ evt.Use();
+ return false;
+ }
+ return evt.type == EventType.KeyDown
+ && (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
+ && !modifierPressed;
+ }
+
+ ///
+ /// Show a menu with some items disabled. Has a callback that will be called when an item is selected with the index of the selected item.
+ /// Takes a dictionary of options and whether or not they should be enabled.
+ ///
+ private static void ShowMenu(Func menuName, Func menuEnabled, Func isSelected, Action callback, T[] items, Rect position)
+ {
+ var menu = new GenericMenu();
+
+ for (var i = 0; i < items.Length; i++)
+ {
+ // Need to cache index so that it doesn't get passed through to the callback by reference.
+ int index = i;
+ var item = items[index];
+
+ var content = new GUIContent(menuName(item));
+
+ if (menuEnabled(item))
+ {
+ menu.AddItem(content, isSelected(item), () => callback(item));
+ }
+ else
+ {
+ menu.AddDisabledItem(content);
+ }
+ }
+
+ menu.DropDown(position);
+ }
+
+ ///
+ /// Tell Unity that a change has been made to a specified object and we have to save the scene.
+ ///
+ public static void MarkSceneDirty(GameObject gameObject)
+ {
+ // TODO: Undo.RecordObject also marks the scene dirty, so this will no longer be necessary once undo support is added.
+ EditorSceneManager.MarkSceneDirty(gameObject.scene);
+ }
+ }
+}
diff --git a/Editor/InspectorUtils.cs.meta b/Editor/InspectorUtils.cs.meta
new file mode 100644
index 0000000..723ab92
--- /dev/null
+++ b/Editor/InspectorUtils.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d7b4b21f4dc5f004689c8d4d822d8a09
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/OneWayPropertyBindingEditor.cs b/Editor/OneWayPropertyBindingEditor.cs
similarity index 82%
rename from UnityWeld_Editor/OneWayPropertyBindingEditor.cs
rename to Editor/OneWayPropertyBindingEditor.cs
index 910ac2c..23634db 100644
--- a/UnityWeld_Editor/OneWayPropertyBindingEditor.cs
+++ b/Editor/OneWayPropertyBindingEditor.cs
@@ -1,188 +1,173 @@
-using System;
-using System.Linq;
-using UnityEditor;
-using UnityEditor.AnimatedValues;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- [CustomEditor(typeof(OneWayPropertyBinding))]
- class OneWayPropertyBindingEditor : BaseBindingEditor
- {
- private OneWayPropertyBinding targetScript;
-
- private AnimBool viewAdapterOptionsFade;
-
- // Whether each property in the target differs from the prefab it uses.
- private bool viewAdapterPrefabModified;
- private bool viewAdapterOptionsPrefabModified;
- private bool viewModelPropertyPrefabModified;
- private bool viewPropertyPrefabModified;
-
- private void OnEnable()
- {
- // Initialise reference to target script
- targetScript = (OneWayPropertyBinding)target;
-
- Type adapterType;
-
- viewAdapterOptionsFade = new AnimBool(
- ShouldShowAdapterOptions(targetScript.ViewAdapterTypeName, out adapterType)
- );
-
- viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
- }
-
- private void OnDisable()
- {
- viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = viewPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- Type viewPropertyType;
- ShowViewPropertyMenu(
- new GUIContent("View property", "Property on the view to bind to"),
- PropertyFinder.GetBindableProperties(targetScript.gameObject)
- .OrderBy(prop => prop.ViewModelTypeName)
- .ThenBy(prop => prop.MemberName)
- .ToArray(),
- updatedValue => targetScript.ViewPropertyName = updatedValue,
- targetScript.ViewPropertyName,
- out viewPropertyType
- );
-
- // Don't let the user set anything else until they've chosen a view property.
- var guiPreviouslyEnabled = GUI.enabled;
- if (string.IsNullOrEmpty(targetScript.ViewPropertyName))
- {
- GUI.enabled = false;
- }
-
- var viewAdapterTypeNames = GetAdapterTypeNames(
- type => viewPropertyType == null ||
- TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType
- );
-
- EditorStyles.label.fontStyle = viewAdapterPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterMenu(
- new GUIContent(
- "View adapter",
- "Adapter that converts values sent from the view-model to the view."
- ),
- viewAdapterTypeNames,
- targetScript.ViewAdapterTypeName,
- newValue =>
- {
- // Get rid of old adapter options if we changed the type of the adapter.
- if (newValue != targetScript.ViewAdapterTypeName)
- {
- Undo.RecordObject(targetScript, "Set view adapter options");
- targetScript.ViewAdapterOptions = null;
- }
-
- UpdateProperty(
- updatedValue => targetScript.ViewAdapterTypeName = updatedValue,
- targetScript.ViewAdapterTypeName,
- newValue,
- "Set view adapter"
- );
- }
- );
-
- Type adapterType;
- viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
- targetScript.ViewAdapterTypeName,
- out adapterType
- );
-
- EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterOptionsMenu(
- "View adapter options",
- adapterType,
- options => targetScript.ViewAdapterOptions = options,
- targetScript.ViewAdapterOptions,
- viewAdapterOptionsFade.faded
- );
-
- EditorGUILayout.Space();
-
- EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- var adaptedViewPropertyType = AdaptTypeBackward(
- viewPropertyType,
- targetScript.ViewAdapterTypeName
- );
- ShowViewModelPropertyMenu(
- new GUIContent(
- "View-model property",
- "Property on the view-model to bind to."
- ),
- TypeResolver.FindBindableProperties(targetScript),
- updatedValue => targetScript.ViewModelPropertyName = updatedValue,
- targetScript.ViewModelPropertyName,
- property => property.PropertyType == adaptedViewPropertyType
- );
-
- GUI.enabled = guiPreviouslyEnabled;
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
- }
-
- ///
- /// Check whether each of the properties on the object have been changed
- /// from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewAdapterTypeName":
- viewAdapterPrefabModified = property.prefabOverride;
- break;
-
- case "viewAdapterOptions":
- viewAdapterOptionsPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelPropertyName":
- viewModelPropertyPrefabModified = property.prefabOverride;
- break;
-
- case "viewPropertyName":
- viewPropertyPrefabModified = property.prefabOverride;
- break;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEditor.AnimatedValues;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(OneWayPropertyBinding))]
+ class OneWayPropertyBindingEditor : BaseBindingEditor
+ {
+ private OneWayPropertyBinding targetScript;
+
+ private AnimBool viewAdapterOptionsFade;
+
+ // Whether each property in the target differs from the prefab it uses.
+ private bool viewAdapterPrefabModified;
+ private bool viewAdapterOptionsPrefabModified;
+ private bool viewModelPropertyPrefabModified;
+ private bool viewPropertyPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ // Initialise reference to target script
+ targetScript = (OneWayPropertyBinding)target;
+
+ viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
+ viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
+ }
+
+ private void OnDisable()
+ {
+ viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ var defaultLabelStyle = EditorStyles.label.fontStyle;
+ EditorStyles.label.fontStyle = viewPropertyPrefabModified
+ ? FontStyle.Bold
+ : defaultLabelStyle;
+
+ Type viewPropertyType;
+ ShowViewPropertyMenu(
+ new GUIContent("View property", "Property on the view to bind to"),
+ PropertyFinder.GetBindableProperties(targetScript.gameObject),
+ updatedValue => targetScript.ViewPropertyName = updatedValue,
+ targetScript.ViewPropertyName,
+ out viewPropertyType
+ );
+
+ // Don't let the user set anything else until they've chosen a view property.
+ var guiPreviouslyEnabled = GUI.enabled;
+ if (string.IsNullOrEmpty(targetScript.ViewPropertyName))
+ {
+ GUI.enabled = false;
+ }
+
+ var viewAdapterTypeNames = TypeResolver.GetAdapterIds(
+ o => viewPropertyType == null || o.OutType == viewPropertyType);
+
+ EditorStyles.label.fontStyle = viewAdapterPrefabModified
+ ? FontStyle.Bold
+ : defaultLabelStyle;
+
+ ShowAdapterMenu(
+ new GUIContent(
+ "View adapter",
+ "Adapter that converts values sent from the view-model to the view."
+ ),
+ viewAdapterTypeNames,
+ targetScript.ViewAdapterId,
+ newValue =>
+ {
+ // Get rid of old adapter options if we changed the type of the adapter.
+ if (newValue != targetScript.ViewAdapterId)
+ {
+ Undo.RecordObject(targetScript, "Set view adapter options");
+ targetScript.ViewAdapterOptions = null;
+ }
+
+ UpdateProperty(
+ updatedValue => targetScript.ViewAdapterId = updatedValue,
+ targetScript.ViewAdapterId,
+ newValue,
+ "Set view adapter"
+ );
+ }
+ );
+
+ Type adapterType;
+ viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
+ targetScript.ViewAdapterId,
+ out adapterType
+ );
+
+ EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
+ ? FontStyle.Bold
+ : defaultLabelStyle;
+
+ ShowAdapterOptionsMenu(
+ "View adapter options",
+ adapterType,
+ options => targetScript.ViewAdapterOptions = options,
+ targetScript.ViewAdapterOptions,
+ viewAdapterOptionsFade.faded
+ );
+
+ EditorGUILayout.Space();
+
+ EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
+ ? FontStyle.Bold
+ : defaultLabelStyle;
+
+ var adaptedViewPropertyType = AdaptTypeBackward(
+ viewPropertyType,
+ targetScript.ViewAdapterId
+ );
+ ShowViewModelPropertyMenu(
+ new GUIContent(
+ "View-model property",
+ "Property on the view-model to bind to."
+ ),
+ TypeResolver.FindBindableProperties(targetScript),
+ updatedValue => targetScript.ViewModelPropertyName = updatedValue,
+ targetScript.ViewModelPropertyName,
+ property => property.PropertyType == adaptedViewPropertyType
+ );
+
+ GUI.enabled = guiPreviouslyEnabled;
+
+ EditorStyles.label.fontStyle = defaultLabelStyle;
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed
+ /// from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewAdapterTypeName":
+ viewAdapterPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewAdapterOptions":
+ viewAdapterOptionsPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelPropertyName":
+ viewModelPropertyPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewPropertyName":
+ viewPropertyPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/OneWayPropertyBindingEditor.cs.meta b/Editor/OneWayPropertyBindingEditor.cs.meta
new file mode 100644
index 0000000..72d5068
--- /dev/null
+++ b/Editor/OneWayPropertyBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 362976f353b49a545856b39257e9eb02
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/SubViewModelBindingEditor.cs b/Editor/SubViewModelBindingEditor.cs
similarity index 81%
rename from UnityWeld_Editor/SubViewModelBindingEditor.cs
rename to Editor/SubViewModelBindingEditor.cs
index 62108e4..c0a0751 100644
--- a/UnityWeld_Editor/SubViewModelBindingEditor.cs
+++ b/Editor/SubViewModelBindingEditor.cs
@@ -1,101 +1,91 @@
-using UnityEngine;
-using UnityEditor;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-using System.Linq;
-using System.Reflection;
-
-namespace UnityWeld_Editor
-{
- ///
- /// Inspector window for SubViewModelBinding
- ///
- [CustomEditor(typeof(SubViewModelBinding))]
- public class SubViewModelBindingEditor : BaseBindingEditor
- {
- private SubViewModelBinding targetScript;
-
- ///
- /// Whether or not the value on our target matches its prefab.
- ///
- private bool propertyPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (SubViewModelBinding)target;
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var bindableProperties = FindBindableProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = propertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowViewModelPropertyMenu(
- new GUIContent(
- "Sub view-model property",
- "The property on the top level view model containing the sub view-model"
- ),
- bindableProperties,
- updatedValue =>
- {
- targetScript.ViewModelPropertyName = updatedValue;
-
- targetScript.ViewModelTypeName = bindableProperties
- .Single(prop => prop.ToString() == updatedValue)
- .Member.PropertyType.ToString();
- },
- targetScript.ViewModelPropertyName,
- p => true
- );
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
- }
-
- private BindableMember[] FindBindableProperties()
- {
- return TypeResolver.FindBindableProperties(targetScript)
- .Where(prop => prop.Member.PropertyType
- .GetCustomAttributes(typeof(BindingAttribute), false)
- .Any()
- )
- .ToArray();
- }
-
- ///
- /// Check whether each of the properties on the object have been changed
- /// from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
-
- propertyPrefabModified = false;
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewModelPropertyName":
- case "viewModelTypeName":
- propertyPrefabModified = property.prefabOverride
- || propertyPrefabModified;
- break;
- }
- }
- while (property.Next(false));
- }
- }
+using UnityEngine;
+using UnityEditor;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+using System.Linq;
+using System.Reflection;
+
+namespace UnityWeld_Editor
+{
+ ///
+ /// Inspector window for SubViewModelBinding
+ ///
+ [CustomEditor(typeof(SubViewModelBinding))]
+ public class SubViewModelBindingEditor : BaseBindingEditor
+ {
+ private SubViewModelBinding targetScript;
+
+ ///
+ /// Whether or not the value on our target matches its prefab.
+ ///
+ private bool propertyPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (SubViewModelBinding)target;
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ var bindableProperties = FindBindableProperties();
+
+ EditorStyles.label.fontStyle = propertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowViewModelPropertyMenu(
+ new GUIContent(
+ "Sub view-model property",
+ "The property on the top level view model containing the sub view-model"
+ ),
+ bindableProperties,
+ updatedValue =>
+ {
+ targetScript.ViewModelPropertyName = updatedValue;
+
+ targetScript.ViewModelTypeName = bindableProperties
+ .Single(prop => prop.ToString() == updatedValue)
+ .Member.PropertyType.ToString();
+ },
+ targetScript.ViewModelPropertyName,
+ p => true
+ );
+ }
+
+ private BindableMember[] FindBindableProperties()
+ {
+ return TypeResolver.FindBindableProperties(targetScript)
+ .Where(prop => prop.Member.PropertyType.HasBindingAttribute()
+ )
+ .ToArray();
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed
+ /// from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+
+ propertyPrefabModified = false;
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewModelPropertyName":
+ case "viewModelTypeName":
+ propertyPrefabModified = property.prefabOverride
+ || propertyPrefabModified;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
}
\ No newline at end of file
diff --git a/Editor/SubViewModelBindingEditor.cs.meta b/Editor/SubViewModelBindingEditor.cs.meta
new file mode 100644
index 0000000..b22cf19
--- /dev/null
+++ b/Editor/SubViewModelBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a40c8489f721eb24991cefbc29d4e6fa
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/TemplateBindingEditor.cs b/Editor/TemplateBindingEditor.cs
similarity index 56%
rename from UnityWeld_Editor/TemplateBindingEditor.cs
rename to Editor/TemplateBindingEditor.cs
index 1b8ca3d..221d4be 100644
--- a/UnityWeld_Editor/TemplateBindingEditor.cs
+++ b/Editor/TemplateBindingEditor.cs
@@ -1,94 +1,66 @@
-using UnityEditor;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- [CustomEditor(typeof(TemplateBinding))]
- class TemplateBindingEditor : BaseBindingEditor
- {
- private TemplateBinding targetScript;
-
- private bool viewModelPrefabModified;
- private bool templatesRootPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (TemplateBinding)target;
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = viewModelPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowViewModelPropertyMenu(
- new GUIContent(
- "Template property",
- "Property on the view model to use for selecting templates."
- ),
- TypeResolver.FindBindableProperties(targetScript),
- updatedValue => targetScript.ViewModelPropertyName = updatedValue,
- targetScript.ViewModelPropertyName,
- property => true
- );
-
- EditorStyles.label.fontStyle = templatesRootPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- UpdateProperty(
- updatedValue => targetScript.TemplatesRoot = updatedValue,
- targetScript.TemplatesRoot,
- (GameObject)EditorGUILayout.ObjectField(
- new GUIContent(
- "Templates root object",
- "Parent object to the objects we want to use as templates."
- ),
- targetScript.TemplatesRoot,
- typeof(GameObject),
- true
- ),
- "Set template binding root object"
- );
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
- }
-
- ///
- /// Check whether each of the properties on the object have been changed
- /// from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewModelPropertyName":
- viewModelPrefabModified = property.prefabOverride;
- break;
-
- case "templatesRoot":
- templatesRootPrefabModified = property.prefabOverride;
- break;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using UnityEditor;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(TemplateBinding))]
+ class TemplateBindingEditor : BaseBindingEditor
+ {
+ private TemplateBinding targetScript;
+
+ private bool viewModelPrefabModified;
+ private SerializedProperty _templatesProperty;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (TemplateBinding)target;
+ _templatesProperty = serializedObject.FindProperty("_templates");
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ EditorStyles.label.fontStyle = viewModelPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowViewModelPropertyMenu(
+ new GUIContent(
+ "Template property",
+ "Property on the view model to use for selecting templates."
+ ),
+ TypeResolver.FindBindableProperties(targetScript),
+ updatedValue => targetScript.ViewModelPropertyName = updatedValue,
+ targetScript.ViewModelPropertyName,
+ property => true
+ );
+
+ EditorGUILayout.PropertyField(_templatesProperty, true);
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed
+ /// from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewModelPropertyName":
+ viewModelPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/TemplateBindingEditor.cs.meta b/Editor/TemplateBindingEditor.cs.meta
new file mode 100644
index 0000000..1fe8975
--- /dev/null
+++ b/Editor/TemplateBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b4ca5578372f3924fb0e72cdea97397d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/TemplateEditor.cs b/Editor/TemplateEditor.cs
similarity index 86%
rename from UnityWeld_Editor/TemplateEditor.cs
rename to Editor/TemplateEditor.cs
index cd8abef..106773c 100644
--- a/UnityWeld_Editor/TemplateEditor.cs
+++ b/Editor/TemplateEditor.cs
@@ -1,96 +1,90 @@
-using System;
-using System.Linq;
-using UnityEditor;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- ///
- /// Editor for template bindings with a dropdown for selecting what view model
- /// to bind to.
- ///
- [CustomEditor(typeof(Template))]
- public class TemplateEditor : BaseBindingEditor
- {
- private Template targetScript;
-
- ///
- /// Whether the value on our target matches its prefab.
- ///
- private bool propertyPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (Template)target;
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var availableViewModels = TypeResolver.TypesWithBindingAttribute
- .Select(type => type.ToString())
- .OrderBy(name => name)
- .ToArray();
-
- var selectedIndex = Array.IndexOf(
- availableViewModels,
- targetScript.ViewModelTypeName
- );
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
- EditorStyles.label.fontStyle = propertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- var newSelectedIndex = EditorGUILayout.Popup(
- new GUIContent(
- "Template view model",
- "Type of the view model that this template will be bound to when it is instantiated."
- ),
- selectedIndex,
- availableViewModels
- .Select(viewModel => new GUIContent(viewModel))
- .ToArray()
- );
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
-
- UpdateProperty(newValue => targetScript.ViewModelTypeName = newValue,
- selectedIndex < 0
- ? string.Empty
- : availableViewModels[selectedIndex],
- newSelectedIndex < 0
- ? string.Empty
- : availableViewModels[newSelectedIndex],
- "Set bound view-model for template"
- );
- }
-
- ///
- /// Check whether each of the properties on the object have been changed from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
- property.Next(true);
- do
- {
- if (property.name == "viewModelTypeName")
- {
- propertyPrefabModified = property.prefabOverride;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ ///
+ /// Editor for template bindings with a dropdown for selecting what view model
+ /// to bind to.
+ ///
+ [CustomEditor(typeof(Template))]
+ public class TemplateEditor : BaseBindingEditor
+ {
+ private Template targetScript;
+
+ ///
+ /// Whether the value on our target matches its prefab.
+ ///
+ private bool propertyPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (Template)target;
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ var availableViewModels = TypeResolver.TypesWithBindingAttribute
+ .Select(type => type.ToString())
+ .OrderBy(name => name)
+ .ToArray();
+
+ var selectedIndex = Array.IndexOf(
+ availableViewModels,
+ targetScript.ViewModelTypeName
+ );
+
+ EditorStyles.label.fontStyle = propertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ var newSelectedIndex = EditorGUILayout.Popup(
+ new GUIContent(
+ "Template view model",
+ "Type of the view model that this template will be bound to when it is instantiated."
+ ),
+ selectedIndex,
+ availableViewModels
+ .Select(viewModel => new GUIContent(viewModel))
+ .ToArray()
+ );
+
+ EditorStyles.label.fontStyle = DefaultFontStyle;
+
+ UpdateProperty(newValue => targetScript.ViewModelTypeName = newValue,
+ selectedIndex < 0
+ ? string.Empty
+ : availableViewModels[selectedIndex],
+ newSelectedIndex < 0
+ ? string.Empty
+ : availableViewModels[newSelectedIndex],
+ "Set bound view-model for template"
+ );
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ if (property.name == "viewModelTypeName")
+ {
+ propertyPrefabModified = property.prefabOverride;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/TemplateEditor.cs.meta b/Editor/TemplateEditor.cs.meta
new file mode 100644
index 0000000..641fe8d
--- /dev/null
+++ b/Editor/TemplateEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 41e9680918dbe374abf0efb0d8d9884d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/ToggleActiveBindingEditor.cs b/Editor/ToggleActiveBindingEditor.cs
similarity index 76%
rename from UnityWeld_Editor/ToggleActiveBindingEditor.cs
rename to Editor/ToggleActiveBindingEditor.cs
index 91847e1..61485f2 100644
--- a/UnityWeld_Editor/ToggleActiveBindingEditor.cs
+++ b/Editor/ToggleActiveBindingEditor.cs
@@ -1,153 +1,137 @@
-using System;
-using UnityEditor;
-using UnityEditor.AnimatedValues;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- [CustomEditor(typeof(ToggleActiveBinding))]
- public class ToggleActiveBindingEditor : BaseBindingEditor
- {
- private ToggleActiveBinding targetScript;
-
- private AnimBool viewAdapterOptionsFade;
-
- private bool viewAdapterPrefabModified;
- private bool viewAdapterOptionsPrefabModified;
- private bool viewModelPropertyPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (ToggleActiveBinding)target;
-
- Type adapterType;
-
- viewAdapterOptionsFade = new AnimBool(
- ShouldShowAdapterOptions(targetScript.ViewAdapterTypeName, out adapterType)
- );
-
- viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
- }
-
- private void OnDisable()
- {
- viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
-
- var viewPropertyType = typeof(bool);
-
- var viewAdapterTypeNames = GetAdapterTypeNames(
- type => TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType
- );
-
- EditorStyles.label.fontStyle = viewAdapterPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterMenu(
- new GUIContent(
- "View adapter",
- "Adapter that converts values sent from the view-model to the view."
- ),
- viewAdapterTypeNames,
- targetScript.ViewAdapterTypeName,
- newValue =>
- {
- // Get rid of old adapter options if we changed the type of the adapter.
- if (newValue != targetScript.ViewAdapterTypeName)
- {
- Undo.RecordObject(targetScript, "Set view adapter options");
- targetScript.ViewAdapterOptions = null;
- }
-
- UpdateProperty(
- updatedValue => targetScript.ViewAdapterTypeName = updatedValue,
- targetScript.ViewAdapterTypeName,
- newValue,
- "Set view adapter"
- );
- }
- );
-
- Type adapterType;
- viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
- targetScript.ViewAdapterTypeName,
- out adapterType
- );
-
- EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterOptionsMenu(
- "View adapter options",
- adapterType,
- options => targetScript.ViewAdapterOptions = options,
- targetScript.ViewAdapterOptions,
- viewAdapterOptionsFade.faded
- );
-
- EditorGUILayout.Space();
-
- EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- var adaptedViewPropertyType = AdaptTypeBackward(
- viewPropertyType,
- targetScript.ViewAdapterTypeName
- );
- ShowViewModelPropertyMenu(
- new GUIContent(
- "View-model property",
- "Property on the view-model to bind to."
- ),
- TypeResolver.FindBindableProperties(targetScript),
- updatedValue => targetScript.ViewModelPropertyName = updatedValue,
- targetScript.ViewModelPropertyName,
- property => property.PropertyType == adaptedViewPropertyType
- );
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
- }
-
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewAdapterTypeName":
- viewAdapterPrefabModified = property.prefabOverride;
- break;
-
- case "viewAdapterOptions":
- viewAdapterOptionsPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelPropertyName":
- viewModelPropertyPrefabModified = property.prefabOverride;
- break;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using System;
+using UnityEditor;
+using UnityEditor.AnimatedValues;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(ToggleActiveBinding))]
+ public class ToggleActiveBindingEditor : BaseBindingEditor
+ {
+ private ToggleActiveBinding targetScript;
+
+ private AnimBool viewAdapterOptionsFade;
+
+ private bool viewAdapterPrefabModified;
+ private bool viewAdapterOptionsPrefabModified;
+ private bool viewModelPropertyPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (ToggleActiveBinding)target;
+
+ viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
+ viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
+ }
+
+ private void OnDisable()
+ {
+ viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ var viewPropertyType = typeof(bool);
+
+ var viewAdapterTypeNames = TypeResolver.GetAdapterIds(o => o.OutType == viewPropertyType);
+
+ EditorStyles.label.fontStyle = viewAdapterPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowAdapterMenu(
+ new GUIContent(
+ "View adapter",
+ "Adapter that converts values sent from the view-model to the view."
+ ),
+ viewAdapterTypeNames,
+ targetScript.ViewAdapterId,
+ newValue =>
+ {
+ // Get rid of old adapter options if we changed the type of the adapter.
+ if (newValue != targetScript.ViewAdapterId)
+ {
+ Undo.RecordObject(targetScript, "Set view adapter options");
+ targetScript.ViewAdapterOptions = null;
+ }
+
+ UpdateProperty(
+ updatedValue => targetScript.ViewAdapterId = updatedValue,
+ targetScript.ViewAdapterId,
+ newValue,
+ "Set view adapter"
+ );
+ }
+ );
+
+ Type adapterType;
+ viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
+ targetScript.ViewAdapterId,
+ out adapterType
+ );
+
+ EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowAdapterOptionsMenu(
+ "View adapter options",
+ adapterType,
+ options => targetScript.ViewAdapterOptions = options,
+ targetScript.ViewAdapterOptions,
+ viewAdapterOptionsFade.faded
+ );
+
+ EditorGUILayout.Space();
+
+ EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ var adaptedViewPropertyType = AdaptTypeBackward(
+ viewPropertyType,
+ targetScript.ViewAdapterId
+ );
+ ShowViewModelPropertyMenu(
+ new GUIContent(
+ "View-model property",
+ "Property on the view-model to bind to."
+ ),
+ TypeResolver.FindBindableProperties(targetScript),
+ updatedValue => targetScript.ViewModelPropertyName = updatedValue,
+ targetScript.ViewModelPropertyName,
+ property => property.PropertyType == adaptedViewPropertyType
+ );
+ }
+
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewAdapterTypeName":
+ viewAdapterPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewAdapterOptions":
+ viewAdapterOptionsPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelPropertyName":
+ viewModelPropertyPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/ToggleActiveBindingEditor.cs.meta b/Editor/ToggleActiveBindingEditor.cs.meta
new file mode 100644
index 0000000..61740f6
--- /dev/null
+++ b/Editor/ToggleActiveBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e2ae3e18ed11fbd46af8d4ae70099c0e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld_Editor/TwoWayPropertyBindingEditor.cs b/Editor/TwoWayPropertyBindingEditor.cs
similarity index 80%
rename from UnityWeld_Editor/TwoWayPropertyBindingEditor.cs
rename to Editor/TwoWayPropertyBindingEditor.cs
index 20c1f28..cd0c93e 100644
--- a/UnityWeld_Editor/TwoWayPropertyBindingEditor.cs
+++ b/Editor/TwoWayPropertyBindingEditor.cs
@@ -1,367 +1,340 @@
-using System;
-using System.Linq;
-using UnityEditor;
-using UnityEditor.AnimatedValues;
-using UnityEngine;
-using UnityWeld.Binding;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld_Editor
-{
- [CustomEditor(typeof(TwoWayPropertyBinding))]
- class TwoWayPropertyBindingEditor : BaseBindingEditor
- {
- private TwoWayPropertyBinding targetScript;
-
- private AnimBool viewAdapterOptionsFade;
- private AnimBool viewModelAdapterOptionsFade;
- private AnimBool exceptionAdapterOptionsFade;
-
- // Whether properties in the target script differ from the value in the prefab.
- // Needed to know which ones to display as bold in the inspector.
- private bool viewEventPrefabModified;
- private bool viewPropertyPrefabModified;
- private bool viewAdapterPrefabModified;
- private bool viewAdapterOptionsPrefabModified;
-
- private bool viewModelPropertyPrefabModified;
- private bool viewModelAdapterPrefabModified;
- private bool viewModelAdapterOptionsPrefabModified;
-
- private bool exceptionPropertyPrefabModified;
- private bool exceptionAdapterPrefabModified;
- private bool exceptionAdapterOptionsPrefabModified;
-
- private void OnEnable()
- {
- targetScript = (TwoWayPropertyBinding)target;
-
- Type adapterType;
- viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(
- targetScript.ViewAdapterTypeName,
- out adapterType
- ));
- viewModelAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(
- targetScript.ViewModelAdapterTypeName,
- out adapterType
- ));
- exceptionAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(
- targetScript.ExceptionAdapterTypeName,
- out adapterType
- ));
-
- viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
- viewModelAdapterOptionsFade.valueChanged.AddListener(Repaint);
- exceptionAdapterOptionsFade.valueChanged.AddListener(Repaint);
- }
-
- private void OnDisable()
- {
- viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
- viewModelAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
- exceptionAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
- }
-
- public override void OnInspectorGUI()
- {
- if (CannotModifyInPlayMode())
- {
- GUI.enabled = false;
- }
-
- UpdatePrefabModifiedProperties();
-
- var defaultLabelStyle = EditorStyles.label.fontStyle;
-
- EditorStyles.label.fontStyle = viewEventPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowEventMenu(
- UnityEventWatcher.GetBindableEvents(targetScript.gameObject)
- .OrderBy(evt => evt.Name)
- .ToArray(),
- updatedValue => targetScript.ViewEventName = updatedValue,
- targetScript.ViewEventName
- );
-
- EditorStyles.label.fontStyle = viewPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- Type viewPropertyType;
- ShowViewPropertyMenu(
- new GUIContent("View property", "Property on the view to bind to"),
- PropertyFinder.GetBindableProperties(targetScript.gameObject)
- .OrderBy(prop => prop.ViewModelTypeName)
- .ThenBy(prop => prop.MemberName)
- .ToArray(),
- updatedValue => targetScript.ViewPropertName = updatedValue,
- targetScript.ViewPropertName,
- out viewPropertyType
- );
-
- // Don't let the user set other options until they've set the event and view property.
- var guiPreviouslyEnabled = GUI.enabled;
- if (string.IsNullOrEmpty(targetScript.ViewEventName)
- || string.IsNullOrEmpty(targetScript.ViewPropertName))
- {
- GUI.enabled = false;
- }
-
- var viewAdapterTypeNames = GetAdapterTypeNames(
- type => viewPropertyType == null ||
- TypeResolver.FindAdapterAttribute(type).OutputType == viewPropertyType
- );
-
- EditorStyles.label.fontStyle = viewAdapterPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterMenu(
- new GUIContent(
- "View adapter",
- "Adapter that converts values sent from the view-model to the view."
- ),
- viewAdapterTypeNames,
- targetScript.ViewAdapterTypeName,
- newValue =>
- {
- // Get rid of old adapter options if we changed the type of the adapter.
- if (newValue != targetScript.ViewAdapterTypeName)
- {
- Undo.RecordObject(targetScript, "Set view adapter options");
- targetScript.ViewAdapterOptions = null;
- }
-
- UpdateProperty(
- updatedValue => targetScript.ViewAdapterTypeName = updatedValue,
- targetScript.ViewAdapterTypeName,
- newValue,
- "Set view adapter"
- );
- }
- );
-
- EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- Type viewAdapterType;
- viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
- targetScript.ViewAdapterTypeName,
- out viewAdapterType
- );
- ShowAdapterOptionsMenu(
- "View adapter options",
- viewAdapterType,
- options => targetScript.ViewAdapterOptions = options,
- targetScript.ViewAdapterOptions,
- viewAdapterOptionsFade.faded
- );
-
- EditorGUILayout.Space();
-
- EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- var adaptedViewPropertyType = AdaptTypeBackward(
- viewPropertyType,
- targetScript.ViewAdapterTypeName
- );
- ShowViewModelPropertyMenu(
- new GUIContent(
- "View-model property",
- "Property on the view-model to bind to."
- ),
- TypeResolver.FindBindableProperties(targetScript),
- updatedValue => targetScript.ViewModelPropertyName = updatedValue,
- targetScript.ViewModelPropertyName,
- prop => prop.PropertyType == adaptedViewPropertyType
- );
-
- var viewModelAdapterTypeNames = GetAdapterTypeNames(
- type => adaptedViewPropertyType == null ||
- TypeResolver.FindAdapterAttribute(type).OutputType == adaptedViewPropertyType
- );
-
- EditorStyles.label.fontStyle = viewModelAdapterPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterMenu(
- new GUIContent(
- "View-model adapter",
- "Adapter that converts from the view back to the view-model"
- ),
- viewModelAdapterTypeNames,
- targetScript.ViewModelAdapterTypeName,
- newValue =>
- {
- if (newValue != targetScript.ViewModelAdapterTypeName)
- {
- Undo.RecordObject(targetScript, "Set view-model adapter options");
- targetScript.ViewModelAdapterOptions = null;
- }
-
- UpdateProperty(
- updatedValue => targetScript.ViewModelAdapterTypeName = updatedValue,
- targetScript.ViewModelAdapterTypeName,
- newValue,
- "Set view-model adapter"
- );
- }
- );
-
- EditorStyles.label.fontStyle = viewModelAdapterOptionsPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- Type viewModelAdapterType;
- viewModelAdapterOptionsFade.target = ShouldShowAdapterOptions(
- targetScript.ViewModelAdapterTypeName,
- out viewModelAdapterType
- );
- ShowAdapterOptionsMenu(
- "View-model adapter options",
- viewModelAdapterType,
- options => targetScript.ViewModelAdapterOptions = options,
- targetScript.ViewModelAdapterOptions,
- viewModelAdapterOptionsFade.faded
- );
-
- EditorGUILayout.Space();
-
- var expectionAdapterTypeNames = GetAdapterTypeNames(
- type => TypeResolver.FindAdapterAttribute(type).InputType == typeof(Exception)
- );
-
- EditorStyles.label.fontStyle = exceptionPropertyPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- var adaptedExceptionPropertyType = AdaptTypeForward(
- typeof(Exception),
- targetScript.ExceptionAdapterTypeName
- );
- ShowViewModelPropertyMenuWithNone(
- new GUIContent(
- "Exception property",
- "Property on the view-model to bind the exception to."
- ),
- TypeResolver.FindBindableProperties(targetScript),
- updatedValue => targetScript.ExceptionPropertyName = updatedValue,
- targetScript.ExceptionPropertyName,
- prop => prop.PropertyType == adaptedExceptionPropertyType
- );
-
- EditorStyles.label.fontStyle = exceptionAdapterPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- ShowAdapterMenu(
- new GUIContent(
- "Exception adapter",
- "Adapter that handles exceptions thrown by the view-model adapter"
- ),
- expectionAdapterTypeNames,
- targetScript.ExceptionAdapterTypeName,
- newValue =>
- {
- if (newValue != targetScript.ExceptionAdapterTypeName)
- {
- Undo.RecordObject(targetScript, "Set exception adapter options");
- targetScript.ExceptionAdapterOptions = null;
- }
-
- UpdateProperty(
- updatedValue => targetScript.ExceptionAdapterTypeName = updatedValue,
- targetScript.ExceptionAdapterTypeName,
- newValue,
- "Set exception adapter"
- );
- }
- );
-
- EditorStyles.label.fontStyle = exceptionAdapterOptionsPrefabModified
- ? FontStyle.Bold
- : defaultLabelStyle;
-
- Type exceptionAdapterType;
- exceptionAdapterOptionsFade.target = ShouldShowAdapterOptions(
- targetScript.ExceptionAdapterTypeName,
- out exceptionAdapterType
- );
- ShowAdapterOptionsMenu(
- "Exception adapter options",
- exceptionAdapterType,
- options => targetScript.ExceptionAdapterOptions = options,
- targetScript.ExceptionAdapterOptions,
- exceptionAdapterOptionsFade.faded
- );
-
- EditorStyles.label.fontStyle = defaultLabelStyle;
-
- GUI.enabled = guiPreviouslyEnabled;
- }
-
- ///
- /// Check whether each of the properties on the object have been changed
- /// from the value in the prefab.
- ///
- private void UpdatePrefabModifiedProperties()
- {
- var property = serializedObject.GetIterator();
- // Need to call Next(true) to get the first child. Once we have it, Next(false)
- // will iterate through the properties.
- property.Next(true);
- do
- {
- switch (property.name)
- {
- case "viewEventName":
- viewEventPrefabModified = property.prefabOverride;
- break;
-
- case "viewPropertyName":
- viewPropertyPrefabModified = property.prefabOverride;
- break;
-
- case "viewAdapterTypeName":
- viewAdapterPrefabModified = property.prefabOverride;
- break;
-
- case "viewAdapterOptions":
- viewAdapterOptionsPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelPropertyName":
- viewModelPropertyPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelAdapterTypeName":
- viewModelAdapterPrefabModified = property.prefabOverride;
- break;
-
- case "viewModelAdapterOptions":
- viewModelAdapterOptionsPrefabModified = property.prefabOverride;
- break;
-
- case "exceptionPropertyName":
- exceptionPropertyPrefabModified = property.prefabOverride;
- break;
-
- case "exceptionAdapterTypeName":
- exceptionAdapterPrefabModified = property.prefabOverride;
- break;
-
- case "exceptionAdapterOptions":
- exceptionAdapterOptionsPrefabModified = property.prefabOverride;
- break;
- }
- }
- while (property.Next(false));
- }
- }
-}
+using System;
+using System.Linq;
+using UnityEditor;
+using UnityEditor.AnimatedValues;
+using UnityEngine;
+using UnityWeld.Binding;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld_Editor
+{
+ [CustomEditor(typeof(TwoWayPropertyBinding))]
+ class TwoWayPropertyBindingEditor : BaseBindingEditor
+ {
+ private TwoWayPropertyBinding targetScript;
+
+ private AnimBool viewAdapterOptionsFade;
+ private AnimBool viewModelAdapterOptionsFade;
+ private AnimBool exceptionAdapterOptionsFade;
+
+ // Whether properties in the target script differ from the value in the prefab.
+ // Needed to know which ones to display as bold in the inspector.
+ private bool viewEventPrefabModified;
+ private bool viewPropertyPrefabModified;
+ private bool viewAdapterPrefabModified;
+ private bool viewAdapterOptionsPrefabModified;
+
+ private bool viewModelPropertyPrefabModified;
+ private bool viewModelAdapterPrefabModified;
+ private bool viewModelAdapterOptionsPrefabModified;
+
+ private bool exceptionPropertyPrefabModified;
+ private bool exceptionAdapterPrefabModified;
+ private bool exceptionAdapterOptionsPrefabModified;
+
+ protected override void OnEnabled()
+ {
+ targetScript = (TwoWayPropertyBinding)target;
+
+ viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
+ viewModelAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewModelAdapterId, out _));
+ exceptionAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ExceptionAdapterTypeName, out _));
+
+ viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
+ viewModelAdapterOptionsFade.valueChanged.AddListener(Repaint);
+ exceptionAdapterOptionsFade.valueChanged.AddListener(Repaint);
+ }
+
+ private void OnDisable()
+ {
+ viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
+ viewModelAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
+ exceptionAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
+ }
+
+ protected override void OnInspector()
+ {
+ UpdatePrefabModifiedProperties();
+
+ EditorStyles.label.fontStyle = viewEventPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowEventMenu(
+ UnityEventWatcher.GetBindableEvents(targetScript.gameObject)
+ .OrderBy(evt => evt.Name)
+ .ToArray(),
+ updatedValue => targetScript.ViewEventName = updatedValue,
+ targetScript.ViewEventName
+ );
+
+ EditorStyles.label.fontStyle = viewPropertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ Type viewPropertyType;
+ ShowViewPropertyMenu(
+ new GUIContent("View property", "Property on the view to bind to"),
+ PropertyFinder.GetBindableProperties(targetScript.gameObject),
+ updatedValue => targetScript.ViewPropertyName = updatedValue,
+ targetScript.ViewPropertyName,
+ out viewPropertyType
+ );
+
+ // Don't let the user set other options until they've set the event and view property.
+ var guiPreviouslyEnabled = GUI.enabled;
+ if (string.IsNullOrEmpty(targetScript.ViewEventName)
+ || string.IsNullOrEmpty(targetScript.ViewPropertyName))
+ {
+ GUI.enabled = false;
+ }
+
+ var viewAdapterTypeNames = TypeResolver.GetAdapterIds(
+ o => viewPropertyType == null || o.OutType == viewPropertyType);
+
+ EditorStyles.label.fontStyle = viewAdapterPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowAdapterMenu(
+ new GUIContent(
+ "View adapter",
+ "Adapter that converts values sent from the view-model to the view."
+ ),
+ viewAdapterTypeNames,
+ targetScript.ViewAdapterId,
+ newValue =>
+ {
+ // Get rid of old adapter options if we changed the type of the adapter.
+ if (newValue != targetScript.ViewAdapterId)
+ {
+ Undo.RecordObject(targetScript, "Set view adapter options");
+ targetScript.ViewAdapterOptions = null;
+ }
+
+ UpdateProperty(
+ updatedValue => targetScript.ViewAdapterId = updatedValue,
+ targetScript.ViewAdapterId,
+ newValue,
+ "Set view adapter"
+ );
+ }
+ );
+
+ EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ Type viewAdapterType;
+ viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
+ targetScript.ViewAdapterId,
+ out viewAdapterType
+ );
+ ShowAdapterOptionsMenu(
+ "View adapter options",
+ viewAdapterType,
+ options => targetScript.ViewAdapterOptions = options,
+ targetScript.ViewAdapterOptions,
+ viewAdapterOptionsFade.faded
+ );
+
+ EditorGUILayout.Space();
+
+ EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ var adaptedViewPropertyType = AdaptTypeBackward(
+ viewPropertyType,
+ targetScript.ViewAdapterId
+ );
+ ShowViewModelPropertyMenu(
+ new GUIContent(
+ "View-model property",
+ "Property on the view-model to bind to."
+ ),
+ TypeResolver.FindBindableProperties(targetScript),
+ updatedValue => targetScript.ViewModelPropertyName = updatedValue,
+ targetScript.ViewModelPropertyName,
+ prop => prop.PropertyType == adaptedViewPropertyType
+ );
+
+ var viewModelAdapterTypeNames = TypeResolver.GetAdapterIds(
+ o => adaptedViewPropertyType == null || o.OutType == adaptedViewPropertyType);
+
+ EditorStyles.label.fontStyle = viewModelAdapterPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowAdapterMenu(
+ new GUIContent(
+ "View-model adapter",
+ "Adapter that converts from the view back to the view-model"
+ ),
+ viewModelAdapterTypeNames,
+ targetScript.ViewModelAdapterId,
+ newValue =>
+ {
+ if (newValue != targetScript.ViewModelAdapterId)
+ {
+ Undo.RecordObject(targetScript, "Set view-model adapter options");
+ targetScript.ViewModelAdapterOptions = null;
+ }
+
+ UpdateProperty(
+ updatedValue => targetScript.ViewModelAdapterId = updatedValue,
+ targetScript.ViewModelAdapterId,
+ newValue,
+ "Set view-model adapter"
+ );
+ }
+ );
+
+ EditorStyles.label.fontStyle = viewModelAdapterOptionsPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ Type viewModelAdapterType;
+ viewModelAdapterOptionsFade.target = ShouldShowAdapterOptions(
+ targetScript.ViewModelAdapterId,
+ out viewModelAdapterType
+ );
+ ShowAdapterOptionsMenu(
+ "View-model adapter options",
+ viewModelAdapterType,
+ options => targetScript.ViewModelAdapterOptions = options,
+ targetScript.ViewModelAdapterOptions,
+ viewModelAdapterOptionsFade.faded
+ );
+
+ EditorGUILayout.Space();
+
+ var expectionAdapterTypeNames = TypeResolver.GetAdapterIds(
+ o => o.InType == typeof(Exception));
+
+ EditorStyles.label.fontStyle = exceptionPropertyPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ var adaptedExceptionPropertyType = AdaptTypeForward(
+ typeof(Exception),
+ targetScript.ExceptionAdapterTypeName
+ );
+ ShowViewModelPropertyMenuWithNone(
+ new GUIContent(
+ "Exception property",
+ "Property on the view-model to bind the exception to."
+ ),
+ TypeResolver.FindBindableProperties(targetScript),
+ updatedValue => targetScript.ExceptionPropertyName = updatedValue,
+ targetScript.ExceptionPropertyName,
+ prop => prop.PropertyType == adaptedExceptionPropertyType
+ );
+
+ EditorStyles.label.fontStyle = exceptionAdapterPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ ShowAdapterMenu(
+ new GUIContent(
+ "Exception adapter",
+ "Adapter that handles exceptions thrown by the view-model adapter"
+ ),
+ expectionAdapterTypeNames,
+ targetScript.ExceptionAdapterTypeName,
+ newValue =>
+ {
+ if (newValue != targetScript.ExceptionAdapterTypeName)
+ {
+ Undo.RecordObject(targetScript, "Set exception adapter options");
+ targetScript.ExceptionAdapterOptions = null;
+ }
+
+ UpdateProperty(
+ updatedValue => targetScript.ExceptionAdapterTypeName = updatedValue,
+ targetScript.ExceptionAdapterTypeName,
+ newValue,
+ "Set exception adapter"
+ );
+ }
+ );
+
+ EditorStyles.label.fontStyle = exceptionAdapterOptionsPrefabModified
+ ? FontStyle.Bold
+ : DefaultFontStyle;
+
+ Type exceptionAdapterType;
+ exceptionAdapterOptionsFade.target = ShouldShowAdapterOptions(
+ targetScript.ExceptionAdapterTypeName,
+ out exceptionAdapterType
+ );
+ ShowAdapterOptionsMenu(
+ "Exception adapter options",
+ exceptionAdapterType,
+ options => targetScript.ExceptionAdapterOptions = options,
+ targetScript.ExceptionAdapterOptions,
+ exceptionAdapterOptionsFade.faded
+ );
+
+ GUI.enabled = guiPreviouslyEnabled;
+ }
+
+ ///
+ /// Check whether each of the properties on the object have been changed
+ /// from the value in the prefab.
+ ///
+ private void UpdatePrefabModifiedProperties()
+ {
+ var property = serializedObject.GetIterator();
+ // Need to call Next(true) to get the first child. Once we have it, Next(false)
+ // will iterate through the properties.
+ property.Next(true);
+ do
+ {
+ switch (property.name)
+ {
+ case "viewEventName":
+ viewEventPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewPropertyName":
+ viewPropertyPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewAdapterTypeName":
+ viewAdapterPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewAdapterOptions":
+ viewAdapterOptionsPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelPropertyName":
+ viewModelPropertyPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelAdapterTypeName":
+ viewModelAdapterPrefabModified = property.prefabOverride;
+ break;
+
+ case "viewModelAdapterOptions":
+ viewModelAdapterOptionsPrefabModified = property.prefabOverride;
+ break;
+
+ case "exceptionPropertyName":
+ exceptionPropertyPrefabModified = property.prefabOverride;
+ break;
+
+ case "exceptionAdapterTypeName":
+ exceptionAdapterPrefabModified = property.prefabOverride;
+ break;
+
+ case "exceptionAdapterOptions":
+ exceptionAdapterOptionsPrefabModified = property.prefabOverride;
+ break;
+ }
+ }
+ while (property.Next(false));
+ }
+ }
+}
diff --git a/Editor/TwoWayPropertyBindingEditor.cs.meta b/Editor/TwoWayPropertyBindingEditor.cs.meta
new file mode 100644
index 0000000..5945f49
--- /dev/null
+++ b/Editor/TwoWayPropertyBindingEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2c7ae872ed2100b46a600b9b54780bf0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Editor/UnityWeld.Editor.asmdef b/Editor/UnityWeld.Editor.asmdef
new file mode 100644
index 0000000..e1b44dc
--- /dev/null
+++ b/Editor/UnityWeld.Editor.asmdef
@@ -0,0 +1,18 @@
+{
+ "name": "UnityWeld.Editor",
+ "rootNamespace": "UnityWeld_Editor",
+ "references": [
+ "GUID:af0045edb0742c34c9f8aefb8323e52c"
+ ],
+ "includePlatforms": [
+ "Editor"
+ ],
+ "excludePlatforms": [],
+ "allowUnsafeCode": false,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Editor/UnityWeld.Editor.asmdef.meta b/Editor/UnityWeld.Editor.asmdef.meta
new file mode 100644
index 0000000..de9c452
--- /dev/null
+++ b/Editor/UnityWeld.Editor.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c29ab574f158e5945b4ec0492d3c47e7
+AssemblyDefinitionImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/LICENSE b/LICENSE.md
similarity index 100%
rename from LICENSE
rename to LICENSE.md
diff --git a/LICENSE.md.meta b/LICENSE.md.meta
new file mode 100644
index 0000000..5205a0d
--- /dev/null
+++ b/LICENSE.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 950ecacf92f7f7743b5e5d373d964b7f
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/README.md b/README.md
index e0d38fe..6bdae33 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,16 @@
# Unity-Weld
-[](https://www.nuget.org/packages/RSG.UnityWeld/)
-[](https://www.nuget.org/packages/RSG.UnityWeld/)
-[](https://travis-ci.org/Real-Serious-Games/Unity-Weld)
-
-
*[MVVM-style](https://msdn.microsoft.com/en-us/library/hh848246.aspx) data-binding system for Unity.*
-Unity-Weld is a library for Unity 5+ that enables two-way data binding between Unity UI widgets and game/business logic code. This reduces boiler-plate code that would otherwise be necessary for things like updating the UI when a property changes, removes the need for messy links between objects in the scene that can be broken easily, and allows easier unit testing of code by providing a layer of abstraction between the UI and your core logic code.
+Unity-Weld is a library for Unity 2019+ that enables two-way data binding between Unity UI widgets and game/business logic code. This reduces boiler-plate code that would otherwise be necessary for things like updating the UI when a property changes, removes the need for messy links between objects in the scene that can be broken easily, and allows easier unit testing of code by providing a layer of abstraction between the UI and your core logic code.
A series of articles on Unity Weld has been published on [What Could Possibly Go Wrong](http://www.what-could-possibly-go-wrong.com/bringing-mvvm-to-unity-part-1-about-mvvm-and-unity-weld).
-Example Unity project can be found here: [https://github.com/Real-Serious-Games/Unity-Weld-Examples](https://github.com/Real-Serious-Games/Unity-Weld-Examples).
+FOR ORIGINAL FORK: Example Unity project can be found here: [https://github.com/Real-Serious-Games/Unity-Weld-Examples](https://github.com/Real-Serious-Games/Unity-Weld-Examples).
## Installation
To install Unity-Weld in a new or existing Unity project:
- - Load `Unity-Weld.sln` in Visual Studio and build it
- - Copy `UnityWeld.dll` into your Unity project and place in any directory within `Assets`
- - Copy `UnityWeld_Editor.dll` into your Unity project and place it inside an `Editor` folder within `Assets`
-
-Alternatively, just copy the `UnityWeld/Binding` and `UnityWeld/Widgets` folders into your `Assets` directory in your Unity project, and copy all the .cs files in `UnityWeld_Editor` to a folder named `Editor` inside your `Assets` directory.
-
-
-## Getting started
-
-Check out the [Unity-Weld-Examples](https://github.com/Real-Serious-Games/Unity-Weld-Examples) repository for some examples of how to use Unity-Weld.
-
-[API docmentation](https://github.com/Real-Serious-Games/Unity-Weld/wiki) is on our wiki.
+ Option 1: use package.json to locally install as UPM package using UPM package mananger
+ Option 2: generate upm package using npm tool (npm pack), upload to your feed and add your feed to Unity Package Manager
-If you're interested in getting involved feel free to check out the [roadmap on Trello](https://trello.com/b/KVFUvGR0), or submit a pull request. Make sure to read our [contributing guide](CONTRIBUTING) first.
+Alternatively, just copy the `Editor` to `Scripts/UnityWeld/Editor` and `Runtime` to `Scripts/UnityWeld/Runtime` into your `Assets` directory in your Unity project.
diff --git a/README.md.meta b/README.md.meta
new file mode 100644
index 0000000..9776615
--- /dev/null
+++ b/README.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 43a15502f0f199743b3e22834c7408ee
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime.meta b/Runtime.meta
new file mode 100644
index 0000000..3d52be9
--- /dev/null
+++ b/Runtime.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7ece62dfb5dd94349a31b908b4e1355d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld/AOTOptimisationHelper.cs b/Runtime/AOTOptimisationHelper.cs
similarity index 97%
rename from UnityWeld/AOTOptimisationHelper.cs
rename to Runtime/AOTOptimisationHelper.cs
index d348c88..258b022 100644
--- a/UnityWeld/AOTOptimisationHelper.cs
+++ b/Runtime/AOTOptimisationHelper.cs
@@ -1,54 +1,54 @@
-using UnityEngine;
-using UnityEngine.EventSystems;
-using UnityWeld.Binding.Internal;
-// ReSharper disable UnusedMember.Global
-// ReSharper disable UnusedMember.Local
-// ReSharper disable UnusedVariable
-
-#pragma warning disable 219 // Disable warning that variable is never used
-
-namespace UnityWeld
-{
- ///
- /// In order for certain generic types to not be optimised-out by IL2CPP for
- /// platforms like Xbox One, iPhone and WebGL, we need to reference them at
- /// least once in code instead of just calling them via reflection.
- ///
- /// See this page for more details:
- /// https://docs.unity3d.com/Manual/TroubleShootingIPhone.html
- /// In the section "The game crashes with the error message “ExecutionEngineException:
- /// Attempting to JIT compile method ‘SometType`1<SomeValueType>:.ctor ()’ while
- /// running with –aot-only.”"
- ///
- internal class AOTOptimisationHelper
- {
- // Even though this method is never called, the fact that it exists will
- // ensure the compiler includes the types referenced in it so that we can
- // later refer to those via reflection.
- private void EnsureGenericTypes()
- {
- // Used by InputField
- var strEventBinder = new UnityEventBinder(null, null);
-
- // Used by Slider and Scrollbar
- var floatEventBinder = new UnityEventBinder(null, null);
-
- // Used by Toggle
- var boolEventBinder = new UnityEventBinder(null, null);
-
- // Used by Dropdown
- var intEventBinder = new UnityEventBinder(null, null);
-
- // Used by ScrollRect
- var vector2EventBinder = new UnityEventBinder(null, null);
-
- // Used by ColorTween
- var colorEventBinder = new UnityEventBinder(null, null);
-
- // Used by EventTrigger
- var baseEventDataEventBinder = new UnityEventBinder(null, null);
- }
- }
-}
-
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityWeld.Binding.Internal;
+// ReSharper disable UnusedMember.Global
+// ReSharper disable UnusedMember.Local
+// ReSharper disable UnusedVariable
+
+#pragma warning disable 219 // Disable warning that variable is never used
+
+namespace UnityWeld
+{
+ ///
+ /// In order for certain generic types to not be optimised-out by IL2CPP for
+ /// platforms like Xbox One, iPhone and WebGL, we need to reference them at
+ /// least once in code instead of just calling them via reflection.
+ ///
+ /// See this page for more details:
+ /// https://docs.unity3d.com/Manual/TroubleShootingIPhone.html
+ /// In the section "The game crashes with the error message “ExecutionEngineException:
+ /// Attempting to JIT compile method ‘SometType`1<SomeValueType>:.ctor ()’ while
+ /// running with –aot-only.”"
+ ///
+ internal class AOTOptimisationHelper
+ {
+ // Even though this method is never called, the fact that it exists will
+ // ensure the compiler includes the types referenced in it so that we can
+ // later refer to those via reflection.
+ private void EnsureGenericTypes()
+ {
+ // Used by InputField
+ var strEventBinder = new UnityEventBinder(null, null);
+
+ // Used by Slider and Scrollbar
+ var floatEventBinder = new UnityEventBinder(null, null);
+
+ // Used by Toggle
+ var boolEventBinder = new UnityEventBinder(null, null);
+
+ // Used by Dropdown
+ var intEventBinder = new UnityEventBinder(null, null);
+
+ // Used by ScrollRect
+ var vector2EventBinder = new UnityEventBinder(null, null);
+
+ // Used by ColorTween
+ var colorEventBinder = new UnityEventBinder(null, null);
+
+ // Used by EventTrigger
+ var baseEventDataEventBinder = new UnityEventBinder(null, null);
+ }
+ }
+}
+
#pragma warning restore 219
\ No newline at end of file
diff --git a/Runtime/AOTOptimisationHelper.cs.meta b/Runtime/AOTOptimisationHelper.cs.meta
new file mode 100644
index 0000000..a7e09c3
--- /dev/null
+++ b/Runtime/AOTOptimisationHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b067127a7e01f0408d7cb11797df5c0
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Binding.meta b/Runtime/Binding.meta
new file mode 100644
index 0000000..8f559c5
--- /dev/null
+++ b/Runtime/Binding.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1f7dff4136c1c10459fc630a239805ee
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/UnityWeld/Binding/AbstractMemberBinding.cs b/Runtime/Binding/AbstractMemberBinding.cs
similarity index 62%
rename from UnityWeld/Binding/AbstractMemberBinding.cs
rename to Runtime/Binding/AbstractMemberBinding.cs
index 11afd27..b3e9324 100644
--- a/UnityWeld/Binding/AbstractMemberBinding.cs
+++ b/Runtime/Binding/AbstractMemberBinding.cs
@@ -1,191 +1,197 @@
-using System.Linq;
-using UnityEngine;
-using UnityWeld.Binding.Exceptions;
-using UnityWeld.Binding.Internal;
-
-namespace UnityWeld.Binding
-{
- ///
- /// Base class for binders to Unity MonoBehaviours.
- ///
- [HelpURL("https://github.com/Real-Serious-Games/Unity-Weld")]
- public abstract class AbstractMemberBinding : MonoBehaviour, IMemberBinding
- {
- ///
- /// Initialise this binding. Used when we first start the scene.
- /// Detaches any attached view models, finds available view models afresh and then connects the binding.
- ///
- public virtual void Init()
- {
- Disconnect();
-
- Connect();
- }
-
- ///
- /// Scan up the hierarchy and find a view model that corresponds to the specified name.
- ///
- private object FindViewModel(string viewModelName)
- {
- var trans = transform;
- while (trans != null)
- {
- var components = trans.GetComponents();
- var monoBehaviourViewModel = components
- .FirstOrDefault(component => component.GetType().ToString() == viewModelName);
- if (monoBehaviourViewModel != null)
- {
- return monoBehaviourViewModel;
- }
-
- var providedViewModel = components
- .Select(component => component as IViewModelProvider)
- .Where(component => component != null)
- .FirstOrDefault(
- viewModelBinding => viewModelBinding.GetViewModelTypeName() == viewModelName &&
-#pragma warning disable 252,253 // Warning says unintended reference comparison, but we do want to compare references
- (object)viewModelBinding != this
-#pragma warning restore 252,253
- );
-
- if (providedViewModel != null)
- {
- return providedViewModel.GetViewModel();
- }
-
- trans = trans.parent;
- }
-
- throw new ViewModelNotFoundException(string.Format("Tried to get view model {0} but it could not be found on "
- + "object {1}. Check that a ViewModelBinding for that view model exists further up in "
- + "the scene hierarchy. ", viewModelName, gameObject.name)
- );
- }
-
- ///
- /// Find the type of the adapter with the specified name and create it.
- ///
- protected static IAdapter CreateAdapter(string adapterTypeName)
- {
- if (string.IsNullOrEmpty(adapterTypeName))
- {
- return null;
- }
-
- var adapterType = TypeResolver.FindAdapterType(adapterTypeName);
- if (adapterType == null)
- {
- throw new NoSuchAdapterException(adapterTypeName);
- }
-
- if (!typeof(IAdapter).IsAssignableFrom(adapterType))
- {
- throw new InvalidAdapterException(string.Format("Type '{0}' does not implement IAdapter and cannot be used as an adapter.", adapterTypeName));
- }
-
- return AdapterResolver.CreateAdapter(adapterType);
- }
-
- ///
- /// Make a property end point for a property on the view model.
- ///
- protected PropertyEndPoint MakeViewModelEndPoint(string viewModelPropertyName, string adapterTypeName, AdapterOptions adapterOptions)
- {
- string propertyName;
- object viewModel;
- ParseViewModelEndPointReference(viewModelPropertyName, out propertyName, out viewModel);
-
- var adapter = CreateAdapter(adapterTypeName);
-
- return new PropertyEndPoint(viewModel, propertyName, adapter, adapterOptions, "view-model", this);
- }
-
- ///
- /// Parse an end-point reference including a type name and member name separated by a period.
- ///
- protected static void ParseEndPointReference(string endPointReference, out string memberName, out string typeName)
- {
- var lastPeriodIndex = endPointReference.LastIndexOf('.');
- if (lastPeriodIndex == -1)
- {
- throw new InvalidEndPointException(
- "No period was found, expected end-point reference in the following format: .. " +
- "Provided end-point reference: " + endPointReference
- );
- }
-
- typeName = endPointReference.Substring(0, lastPeriodIndex);
- memberName = endPointReference.Substring(lastPeriodIndex + 1);
- //Due to (undocumented) unity behaviour, some of their components do not work with the namespace when using GetComponent(""), and all of them work without the namespace
- //So to be safe, we remove all namespaces from any component that starts with UnityEngine
- if (typeName.StartsWith("UnityEngine."))
- {
- typeName = typeName.Substring(typeName.LastIndexOf('.') + 1);
- }
- if (typeName.Length == 0 || memberName.Length == 0)
- {
- throw new InvalidEndPointException(
- "Bad format for end-point reference, expected the following format: .. " +
- "Provided end-point reference: " + endPointReference
- );
- }
- }
-
- ///
- /// Parse an end-point reference and search up the hierarchy for the named view-model.
- ///
- protected void ParseViewModelEndPointReference(string endPointReference, out string memberName, out object viewModel)
- {
- string viewModelName;
- ParseEndPointReference(endPointReference, out memberName, out viewModelName);
-
- viewModel = FindViewModel(viewModelName);
- if (viewModel == null)
- {
- throw new ViewModelNotFoundException("Failed to find view-model in hierarchy: " + viewModelName);
- }
- }
-
- ///
- /// Parse an end-point reference and get the component for the view.
- ///
- protected void ParseViewEndPointReference(string endPointReference, out string memberName, out Component view)
- {
- string boundComponentType;
- ParseEndPointReference(endPointReference, out memberName, out boundComponentType);
-
- view = GetComponent(boundComponentType);
- if (view == null)
- {
- throw new ComponentNotFoundException("Failed to find component on current game object: " + boundComponentType);
- }
- }
-
- ///
- /// Connect to all the attached view models
- ///
- public abstract void Connect();
-
- ///
- /// Disconnect from all attached view models.
- ///
- public abstract void Disconnect();
-
- ///
- /// Standard MonoBehaviour awake message, do not call this explicitly.
- /// Initialises the binding.
- ///
- protected void Awake()
- {
- Init();
- }
-
- ///
- /// Clean up when the game object is destroyed.
- ///
- public void OnDestroy()
- {
- Disconnect();
- }
- }
-}
+using System.Linq;
+using UnityEngine;
+using UnityWeld.Binding.Exceptions;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld.Binding
+{
+ ///
+ /// Base class for binders to Unity MonoBehaviours.
+ ///
+ [HelpURL("https://github.com/Real-Serious-Games/Unity-Weld")]
+ public abstract class AbstractMemberBinding : MonoBehaviour, IMemberBinding
+ {
+ private bool _isInitCalled;
+
+ [SerializeField, Header("Automatically bind once on \"OnEnable()\"")]
+ private bool _isAutoConnection;
+
+
+ ///
+ /// Initialise this binding. Used when we first start the scene.
+ /// Detaches any attached view models, finds available view models afresh and then connects the binding.
+ ///
+ public virtual void Init()
+ {
+ if(_isAutoConnection && !gameObject.activeInHierarchy)
+ {
+ return; //wait for enabling
+ }
+
+ if (_isInitCalled)
+ {
+ return; //avoid double connect
+ }
+
+ _isInitCalled = true;
+
+ Disconnect();
+ Connect();
+ }
+
+ ///
+ /// Scan up the hierarchy and find a view model that corresponds to the specified name.
+ ///
+ private object FindViewModel(string viewModelName)
+ {
+ var trans = transform;
+ while(trans != null)
+ {
+ using(var cache = trans.gameObject.GetComponentsWithCache(false))
+ {
+ var monoBehaviourViewModel = cache.Components
+ .FirstOrDefault(component => component.GetType().ToString() == viewModelName);
+ if(monoBehaviourViewModel != null)
+ {
+ return monoBehaviourViewModel;
+ }
+
+ var providedViewModel = cache.Components
+ .Select(component => component.GetViewModelData())
+ .Where(component => component != null)
+ .FirstOrDefault(viewModelData => viewModelData.TypeName == viewModelName);
+
+ if(providedViewModel != null)
+ {
+ return providedViewModel.Model;
+ }
+ }
+
+ trans = trans.parent;
+ }
+
+ throw new ViewModelNotFoundException(
+ $"Tried to get view model {viewModelName} but it could not be found on " +
+ $"object {gameObject.name}. Check that a ViewModelBinding for that view model exists further up in " +
+ "the scene hierarchy. "
+ );
+ }
+
+ ///
+ /// Make a property end point for a property on the view model.
+ ///
+ protected PropertyEndPoint MakeViewModelEndPoint(string viewModelPropertyName, string adapterId,
+ AdapterOptions adapterOptions)
+ {
+ string propertyName;
+ object viewModel;
+ ParseViewModelEndPointReference(viewModelPropertyName, out propertyName, out viewModel);
+
+ var adapter = TypeResolver.GetAdapter(adapterId);
+ return new PropertyEndPoint(viewModel, propertyName, adapter, adapterOptions, "view-model", this);
+ }
+
+ ///
+ /// Parse an end-point reference including a type name and member name separated by a period.
+ ///
+ protected static void ParseEndPointReference(string endPointReference, out string memberName,
+ out string typeName)
+ {
+ var lastPeriodIndex = endPointReference.LastIndexOf('.');
+ if(lastPeriodIndex == -1)
+ {
+ throw new InvalidEndPointException(
+ "No period was found, expected end-point reference in the following format: .. " +
+ "Provided end-point reference: " + endPointReference
+ );
+ }
+
+ typeName = endPointReference.Substring(0, lastPeriodIndex);
+ memberName = endPointReference.Substring(lastPeriodIndex + 1);
+ //Due to (undocumented) unity behaviour, some of their components do not work with the namespace when using GetComponent(""), and all of them work without the namespace
+ //So to be safe, we remove all namespaces from any component that starts with UnityEngine
+ if(typeName.StartsWith("UnityEngine."))
+ {
+ typeName = typeName.Substring(typeName.LastIndexOf('.') + 1);
+ }
+
+ if(typeName.Length == 0 || memberName.Length == 0)
+ {
+ throw new InvalidEndPointException(
+ "Bad format for end-point reference, expected the following format: .. " +
+ "Provided end-point reference: " + endPointReference
+ );
+ }
+ }
+
+ ///
+ /// Parse an end-point reference and search up the hierarchy for the named view-model.
+ ///
+ protected void ParseViewModelEndPointReference(string endPointReference, out string memberName,
+ out object viewModel)
+ {
+ string viewModelName;
+ ParseEndPointReference(endPointReference, out memberName, out viewModelName);
+
+ viewModel = FindViewModel(viewModelName);
+ if(viewModel == null)
+ {
+ throw new ViewModelNotFoundException("Failed to find view-model in hierarchy: " + viewModelName);
+ }
+ }
+
+ ///
+ /// Parse an end-point reference and get the component for the view.
+ ///
+ protected void ParseViewEndPointReference(string endPointReference, out string memberName, out Component view)
+ {
+ string boundComponentType;
+ ParseEndPointReference(endPointReference, out memberName, out boundComponentType);
+
+ view = GetComponent(boundComponentType);
+ if(view == null)
+ {
+ throw new ComponentNotFoundException("Failed to find component on current game object: " +
+ boundComponentType);
+ }
+ }
+
+ ///
+ /// Connect to all the attached view models
+ ///
+ public abstract void Connect();
+
+ ///
+ /// Disconnect from all attached view models.
+ ///
+ public abstract void Disconnect();
+
+ ///
+ /// Standard MonoBehaviour awake message, do not call this explicitly.
+ /// Initialises the binding.
+ ///
+ protected void OnEnable()
+ {
+ if (!_isAutoConnection || _isInitCalled)
+ {
+ return;
+ }
+
+ Init();
+ }
+
+ ///
+ /// Clean up when the game object is destroyed.
+ ///
+ public virtual void OnDestroy()
+ {
+ Disconnect();
+ }
+
+ public void ResetBinding()
+ {
+ _isInitCalled = false;
+ Disconnect();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Runtime/Binding/AbstractMemberBinding.cs.meta b/Runtime/Binding/AbstractMemberBinding.cs.meta
new file mode 100644
index 0000000..e2b33b9
--- /dev/null
+++ b/Runtime/Binding/AbstractMemberBinding.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cbecddc181c8f044dadab0f55a663934
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Runtime/Binding/AbstractTemplateSelector.cs b/Runtime/Binding/AbstractTemplateSelector.cs
new file mode 100644
index 0000000..49512b5
--- /dev/null
+++ b/Runtime/Binding/AbstractTemplateSelector.cs
@@ -0,0 +1,261 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Assertions;
+using UnityWeld.Binding.Exceptions;
+using UnityWeld.Binding.Internal;
+
+namespace UnityWeld.Binding
+{
+ public abstract class AbstractTemplateSelector : AbstractMemberBinding
+ {
+ [Header("Set templates for collection")]
+ [SerializeField] private Template[] _templates;
+ [SerializeField] private string viewModelPropertyName = string.Empty;
+
+ private IDictionary _availableTemplates;
+
+ ///
+ /// All the child objects that have been created, indexed by the view they are connected to.
+ ///
+ private readonly IDictionary