├── .editorconfig
├── .gitignore
├── Editor.meta
├── Editor
├── AnimatorParameterBindingEditor.cs
├── AnimatorParameterBindingEditor.cs.meta
├── BaseBindingEditor.cs
├── BaseBindingEditor.cs.meta
├── CollectionBindingEditor.cs
├── CollectionBindingEditor.cs.meta
├── EventBindingEditor.cs
├── EventBindingEditor.cs.meta
├── InspectorUtils.cs
├── InspectorUtils.cs.meta
├── OneWayPropertyBindingEditor.cs
├── OneWayPropertyBindingEditor.cs.meta
├── SubViewModelBindingEditor.cs
├── SubViewModelBindingEditor.cs.meta
├── TemplateBindingEditor.cs
├── TemplateBindingEditor.cs.meta
├── TemplateEditor.cs
├── TemplateEditor.cs.meta
├── ToggleActiveBindingEditor.cs
├── ToggleActiveBindingEditor.cs.meta
├── TwoWayPropertyBindingEditor.cs
├── TwoWayPropertyBindingEditor.cs.meta
├── UnityWeld.Editor.asmdef
└── UnityWeld.Editor.asmdef.meta
├── LICENSE.md
├── LICENSE.md.meta
├── README.md
├── README.md.meta
├── Runtime.meta
├── Runtime
├── AOTOptimisationHelper.cs
├── AOTOptimisationHelper.cs.meta
├── Binding.meta
├── Binding
│ ├── AbstractMemberBinding.cs
│ ├── AbstractMemberBinding.cs.meta
│ ├── AbstractTemplateSelector.cs
│ ├── AbstractTemplateSelector.cs.meta
│ ├── AdapterOptions.cs
│ ├── AdapterOptions.cs.meta
│ ├── Adapters.meta
│ ├── Adapters
│ │ ├── AdapterInfo.cs
│ │ ├── AdapterInfo.cs.meta
│ │ ├── BaseAdapters.cs
│ │ ├── BaseAdapters.cs.meta
│ │ ├── BoolToColorAdapterOptions.cs
│ │ ├── BoolToColorAdapterOptions.cs.meta
│ │ ├── BoolToColorBlockAdapterOptions.cs
│ │ ├── BoolToColorBlockAdapterOptions.cs.meta
│ │ ├── BoolToStringAdapterOptions.cs
│ │ ├── BoolToStringAdapterOptions.cs.meta
│ │ ├── ColorToColorBlockAdapterOptions.cs
│ │ ├── ColorToColorBlockAdapterOptions.cs.meta
│ │ ├── DateTimeToStringAdapterOptions.cs
│ │ ├── DateTimeToStringAdapterOptions.cs.meta
│ │ ├── FloatToStringAdapterOptions.cs
│ │ ├── FloatToStringAdapterOptions.cs.meta
│ │ ├── StringCultureToDateTimeAdapterOptions.cs
│ │ └── StringCultureToDateTimeAdapterOptions.cs.meta
│ ├── AnimatorParameterBinding.cs
│ ├── AnimatorParameterBinding.cs.meta
│ ├── AnimatorParameterTrigger.cs
│ ├── AnimatorParameterTrigger.cs.meta
│ ├── BindingAttribute.cs
│ ├── BindingAttribute.cs.meta
│ ├── BindingHelper.cs
│ ├── BindingHelper.cs.meta
│ ├── BoundObservableList.cs
│ ├── BoundObservableList.cs.meta
│ ├── CollectionBinding.cs
│ ├── CollectionBinding.cs.meta
│ ├── DropdownBinding.cs
│ ├── DropdownBinding.cs.meta
│ ├── EventBinding.cs
│ ├── EventBinding.cs.meta
│ ├── Exceptions.meta
│ ├── Exceptions
│ │ ├── AmbiguousTypeException.cs
│ │ ├── AmbiguousTypeException.cs.meta
│ │ ├── ComponentNotFoundException.cs
│ │ ├── ComponentNotFoundException.cs.meta
│ │ ├── InvalidAdapterException.cs
│ │ ├── InvalidAdapterException.cs.meta
│ │ ├── InvalidEndPointException.cs
│ │ ├── InvalidEndPointException.cs.meta
│ │ ├── InvalidEventException.cs
│ │ ├── InvalidEventException.cs.meta
│ │ ├── InvalidTypeException.cs
│ │ ├── InvalidTypeException.cs.meta
│ │ ├── MemberNotFoundException.cs
│ │ ├── MemberNotFoundException.cs.meta
│ │ ├── NoSuchAdapterException.cs
│ │ ├── NoSuchAdapterException.cs.meta
│ │ ├── PropertyNullException.cs
│ │ ├── PropertyNullException.cs.meta
│ │ ├── TemplateNotFoundException.cs
│ │ ├── TemplateNotFoundException.cs.meta
│ │ ├── ViewModelNotFoundException.cs
│ │ └── ViewModelNotFoundException.cs.meta
│ ├── IMemberBinding.cs
│ ├── IMemberBinding.cs.meta
│ ├── IViewModelProvider.cs
│ ├── IViewModelProvider.cs.meta
│ ├── Internal.meta
│ ├── Internal
│ │ ├── BindableMember.cs
│ │ ├── BindableMember.cs.meta
│ │ ├── PropertyEndPoint.cs
│ │ ├── PropertyEndPoint.cs.meta
│ │ ├── PropertyFinder.cs
│ │ ├── PropertyFinder.cs.meta
│ │ ├── PropertySync.cs
│ │ ├── PropertySync.cs.meta
│ │ ├── PropertyWatcher.cs
│ │ ├── PropertyWatcher.cs.meta
│ │ ├── TypeResolver.cs
│ │ ├── TypeResolver.cs.meta
│ │ ├── UnityEventBinder.cs
│ │ ├── UnityEventBinder.cs.meta
│ │ ├── UnityEventWatcher.cs
│ │ └── UnityEventWatcher.cs.meta
│ ├── OneWayPropertyBinding.cs
│ ├── OneWayPropertyBinding.cs.meta
│ ├── SubViewModelBinding.cs
│ ├── SubViewModelBinding.cs.meta
│ ├── Template.cs
│ ├── Template.cs.meta
│ ├── TemplateBinding.cs
│ ├── TemplateBinding.cs.meta
│ ├── ToggleActiveBinding.cs
│ ├── ToggleActiveBinding.cs.meta
│ ├── TwoWayPropertyBinding.cs
│ └── TwoWayPropertyBinding.cs.meta
├── UnityWeld.asmdef
├── UnityWeld.asmdef.meta
├── Widgets.meta
└── Widgets
│ ├── DropdownAdapter.cs
│ └── DropdownAdapter.cs.meta
├── package.json
└── package.json.meta
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | end_of_line = crlf
7 | insert_final_newline = false
8 | indent_style = space
9 | indent_size = 4
10 | charset = utf-8
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TimGameDev/Unity-Weld/6d154316ab83d7080e0a9cbdeda487e1b0b857a8/.gitignore
--------------------------------------------------------------------------------
/Editor.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 72f2efac3eed8eb4f98c8797f42bf210
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Editor/AnimatorParameterBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 922b7495c089af14c907aceb395b0cba
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/BaseBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b2109485ee1df45439d88eef4c5b03a5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/CollectionBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using UnityEngine;
3 | using UnityWeld.Binding;
4 | using UnityWeld.Binding.Internal;
5 |
6 | namespace UnityWeld_Editor
7 | {
8 | [CustomEditor(typeof(CollectionBinding))]
9 | class CollectionBindingEditor : BaseBindingEditor
10 | {
11 | private CollectionBinding _targetScript;
12 | private SerializedProperty _templateInitialPoolCountProperty;
13 | private SerializedProperty _itemsContainerProperty;
14 | private SerializedProperty _templatesProperty;
15 |
16 | private bool _viewModelPrefabModified;
17 |
18 | protected override void OnEnabled()
19 | {
20 | // Initialise everything
21 | _targetScript = (CollectionBinding)target;
22 | _templateInitialPoolCountProperty = serializedObject.FindProperty("_templateInitialPoolCount");
23 | _itemsContainerProperty = serializedObject.FindProperty("_itemsContainer");
24 | _templatesProperty = serializedObject.FindProperty("_templates");
25 | }
26 |
27 | protected override void OnInspector()
28 | {
29 | UpdatePrefabModifiedProperties();
30 |
31 | EditorGUILayout.PropertyField(_templateInitialPoolCountProperty);
32 | EditorGUILayout.PropertyField(_itemsContainerProperty);
33 | EditorGUILayout.PropertyField(_templatesProperty, true);
34 |
35 | EditorStyles.label.fontStyle = _viewModelPrefabModified ? FontStyle.Bold : DefaultFontStyle;
36 | ShowViewModelPropertyMenu(
37 | new GUIContent("View-model property", "Property on the view-model to bind to."),
38 | TypeResolver.FindBindableCollectionProperties(_targetScript),
39 | updatedValue => _targetScript.ViewModelPropertyName = updatedValue,
40 | _targetScript.ViewModelPropertyName,
41 | property => true
42 | );
43 | }
44 |
45 | ///
46 | /// Check whether each of the properties on the object have been changed from the value in the prefab.
47 | ///
48 | private void UpdatePrefabModifiedProperties()
49 | {
50 | var property = serializedObject.GetIterator();
51 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
52 | // will iterate through the properties.
53 | property.Next(true);
54 | do
55 | {
56 | switch (property.name)
57 | {
58 | case "viewModelPropertyName":
59 | _viewModelPrefabModified = property.prefabOverride;
60 | break;
61 | }
62 | }
63 | while (property.Next(false));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Editor/CollectionBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5a455a8f5047aca4fa9c7c67ef994596
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/EventBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Reflection;
3 | using UnityEditor;
4 | using UnityEngine;
5 | using UnityWeld.Binding;
6 | using UnityWeld.Binding.Internal;
7 |
8 | namespace UnityWeld_Editor
9 | {
10 | [CustomEditor(typeof(EventBinding))]
11 | public class EventBindingEditor : BaseBindingEditor
12 | {
13 | private EventBinding targetScript;
14 |
15 | // Whether or not the values on our target match its prefab.
16 | private bool viewEventPrefabModified;
17 | private bool viewModelMethodPrefabModified;
18 |
19 | protected override void OnEnabled()
20 | {
21 | targetScript = (EventBinding)target;
22 | }
23 |
24 | protected override void OnInspector()
25 | {
26 | UpdatePrefabModifiedProperties();
27 |
28 | EditorStyles.label.fontStyle = viewEventPrefabModified
29 | ? FontStyle.Bold
30 | : DefaultFontStyle;
31 |
32 | ShowEventMenu(
33 | UnityEventWatcher.GetBindableEvents(targetScript.gameObject)
34 | .OrderBy(evt => evt.Name)
35 | .ToArray(),
36 | updatedValue => targetScript.ViewEventName = updatedValue,
37 | targetScript.ViewEventName
38 | );
39 |
40 | EditorStyles.label.fontStyle = viewModelMethodPrefabModified
41 | ? FontStyle.Bold
42 | : DefaultFontStyle;
43 |
44 | ShowMethodMenu(targetScript, TypeResolver.FindBindableMethods(targetScript));
45 | }
46 |
47 | ///
48 | /// Draws the dropdown for selecting a method from bindableViewModelMethods
49 | ///
50 | private void ShowMethodMenu(
51 | EventBinding targetScript,
52 | BindableMember[] bindableMethods
53 | )
54 | {
55 | var tooltip = "Method on the view-model to bind to.";
56 |
57 | InspectorUtils.DoPopup(
58 | new GUIContent(targetScript.ViewModelMethodName),
59 | new GUIContent("View-model method", tooltip),
60 | m => m.ViewModelType + "/" + m.MemberName,
61 | m => true,
62 | m => m.ToString() == targetScript.ViewModelMethodName,
63 | m => UpdateProperty(
64 | updatedValue => targetScript.ViewModelMethodName = updatedValue,
65 | targetScript.ViewModelMethodName,
66 | m.ToString(),
67 | "Set bound view-model method"
68 | ),
69 | bindableMethods
70 | .OrderBy(m => m.ViewModelTypeName)
71 | .ThenBy(m => m.MemberName)
72 | .ToArray()
73 | );
74 | }
75 |
76 | ///
77 | /// Check whether each of the properties on the object have been changed
78 | /// from the value in the prefab.
79 | ///
80 | private void UpdatePrefabModifiedProperties()
81 | {
82 | var property = serializedObject.GetIterator();
83 | // Need to call Next(true) to get the first child. Once we have it,
84 | // Next(false) will iterate through the properties.
85 | property.Next(true);
86 | do
87 | {
88 | switch (property.name)
89 | {
90 | case "viewEventName":
91 | viewEventPrefabModified = property.prefabOverride;
92 | break;
93 |
94 | case "viewModelMethodName":
95 | viewModelMethodPrefabModified = property.prefabOverride;
96 | break;
97 | }
98 | }
99 | while (property.Next(false));
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Editor/EventBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 375353919d68d544fadc2369d1a012d5
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/InspectorUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEditor;
3 | using UnityEditor.SceneManagement;
4 | using UnityEngine;
5 |
6 | namespace UnityWeld_Editor
7 | {
8 | ///
9 | /// Common utilities for custom inspectors.
10 | ///
11 | internal class InspectorUtils
12 | {
13 | ///
14 | /// Show a popup menu with some items disabled and a label to its left.
15 | ///
16 | public static void DoPopup(
17 | GUIContent content,
18 | GUIContent label,
19 | Func menuName,
20 | Func menuEnabled,
21 | Func isSelected,
22 | Action callback,
23 | T[] items)
24 | {
25 | var labelRect = EditorGUILayout.GetControlRect(false, 16f, EditorStyles.popup);
26 | var controlId = GUIUtility.GetControlID(FocusType.Keyboard, labelRect);
27 |
28 | var buttonRect = EditorGUI.PrefixLabel(labelRect, controlId, label);
29 |
30 | ShowPopupButton(
31 | buttonRect,
32 | labelRect,
33 | controlId,
34 | content,
35 | () => ShowMenu(menuName, menuEnabled, isSelected, callback, items, buttonRect)
36 | );
37 | }
38 |
39 | ///
40 | /// Shows the button for a popup/dropdown control, with a label.
41 | ///
42 | private static void ShowPopupButton(Rect buttonRect, Rect labelRect, int controlId, GUIContent currentlySelected, Action popup)
43 | {
44 | var currentEvent = Event.current;
45 | var eventType = currentEvent.type;
46 | var style = EditorStyles.popup;
47 |
48 | switch (eventType)
49 | {
50 | case EventType.KeyDown:
51 | if (MainActionKeyForControl(currentEvent, controlId))
52 | {
53 | popup();
54 | currentEvent.Use();
55 | }
56 | break;
57 |
58 | case EventType.Repaint:
59 | style.Draw(buttonRect, currentlySelected, controlId, false);
60 | break;
61 |
62 | case EventType.MouseDown:
63 | if (currentEvent.button != 0)
64 | {
65 | return;
66 | }
67 |
68 | if (buttonRect.Contains(currentEvent.mousePosition))
69 | {
70 | popup();
71 | GUIUtility.keyboardControl = controlId;
72 | currentEvent.Use();
73 | }
74 | else if (labelRect.Contains(currentEvent.mousePosition))
75 | {
76 | GUIUtility.keyboardControl = controlId;
77 | currentEvent.Use();
78 | }
79 | break;
80 | }
81 | }
82 |
83 | ///
84 | /// Returns whether the specified control has been activated by a key press.
85 | ///
86 | private static bool MainActionKeyForControl(Event evt, int controlId)
87 | {
88 | if (GUIUtility.keyboardControl != controlId)
89 | {
90 | return false;
91 | }
92 | bool modifierPressed = evt.alt || evt.shift || evt.command || evt.control;
93 | if (!modifierPressed && evt.type == EventType.KeyDown && evt.character == ' ')
94 | {
95 | evt.Use();
96 | return false;
97 | }
98 | return evt.type == EventType.KeyDown
99 | && (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter)
100 | && !modifierPressed;
101 | }
102 |
103 | ///
104 | /// 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.
105 | /// Takes a dictionary of options and whether or not they should be enabled.
106 | ///
107 | private static void ShowMenu(Func menuName, Func menuEnabled, Func isSelected, Action callback, T[] items, Rect position)
108 | {
109 | var menu = new GenericMenu();
110 |
111 | for (var i = 0; i < items.Length; i++)
112 | {
113 | // Need to cache index so that it doesn't get passed through to the callback by reference.
114 | int index = i;
115 | var item = items[index];
116 |
117 | var content = new GUIContent(menuName(item));
118 |
119 | if (menuEnabled(item))
120 | {
121 | menu.AddItem(content, isSelected(item), () => callback(item));
122 | }
123 | else
124 | {
125 | menu.AddDisabledItem(content);
126 | }
127 | }
128 |
129 | menu.DropDown(position);
130 | }
131 |
132 | ///
133 | /// Tell Unity that a change has been made to a specified object and we have to save the scene.
134 | ///
135 | public static void MarkSceneDirty(GameObject gameObject)
136 | {
137 | // TODO: Undo.RecordObject also marks the scene dirty, so this will no longer be necessary once undo support is added.
138 | EditorSceneManager.MarkSceneDirty(gameObject.scene);
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/Editor/InspectorUtils.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d7b4b21f4dc5f004689c8d4d822d8a09
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/OneWayPropertyBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using UnityEditor;
4 | using UnityEditor.AnimatedValues;
5 | using UnityEngine;
6 | using UnityWeld.Binding;
7 | using UnityWeld.Binding.Internal;
8 |
9 | namespace UnityWeld_Editor
10 | {
11 | [CustomEditor(typeof(OneWayPropertyBinding))]
12 | class OneWayPropertyBindingEditor : BaseBindingEditor
13 | {
14 | private OneWayPropertyBinding targetScript;
15 |
16 | private AnimBool viewAdapterOptionsFade;
17 |
18 | // Whether each property in the target differs from the prefab it uses.
19 | private bool viewAdapterPrefabModified;
20 | private bool viewAdapterOptionsPrefabModified;
21 | private bool viewModelPropertyPrefabModified;
22 | private bool viewPropertyPrefabModified;
23 |
24 | protected override void OnEnabled()
25 | {
26 | // Initialise reference to target script
27 | targetScript = (OneWayPropertyBinding)target;
28 |
29 | viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
30 | viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
31 | }
32 |
33 | private void OnDisable()
34 | {
35 | viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
36 | }
37 |
38 | protected override void OnInspector()
39 | {
40 | UpdatePrefabModifiedProperties();
41 |
42 | var defaultLabelStyle = EditorStyles.label.fontStyle;
43 | EditorStyles.label.fontStyle = viewPropertyPrefabModified
44 | ? FontStyle.Bold
45 | : defaultLabelStyle;
46 |
47 | Type viewPropertyType;
48 | ShowViewPropertyMenu(
49 | new GUIContent("View property", "Property on the view to bind to"),
50 | PropertyFinder.GetBindableProperties(targetScript.gameObject),
51 | updatedValue => targetScript.ViewPropertyName = updatedValue,
52 | targetScript.ViewPropertyName,
53 | out viewPropertyType
54 | );
55 |
56 | // Don't let the user set anything else until they've chosen a view property.
57 | var guiPreviouslyEnabled = GUI.enabled;
58 | if (string.IsNullOrEmpty(targetScript.ViewPropertyName))
59 | {
60 | GUI.enabled = false;
61 | }
62 |
63 | var viewAdapterTypeNames = TypeResolver.GetAdapterIds(
64 | o => viewPropertyType == null || o.OutType == viewPropertyType);
65 |
66 | EditorStyles.label.fontStyle = viewAdapterPrefabModified
67 | ? FontStyle.Bold
68 | : defaultLabelStyle;
69 |
70 | ShowAdapterMenu(
71 | new GUIContent(
72 | "View adapter",
73 | "Adapter that converts values sent from the view-model to the view."
74 | ),
75 | viewAdapterTypeNames,
76 | targetScript.ViewAdapterId,
77 | newValue =>
78 | {
79 | // Get rid of old adapter options if we changed the type of the adapter.
80 | if (newValue != targetScript.ViewAdapterId)
81 | {
82 | Undo.RecordObject(targetScript, "Set view adapter options");
83 | targetScript.ViewAdapterOptions = null;
84 | }
85 |
86 | UpdateProperty(
87 | updatedValue => targetScript.ViewAdapterId = updatedValue,
88 | targetScript.ViewAdapterId,
89 | newValue,
90 | "Set view adapter"
91 | );
92 | }
93 | );
94 |
95 | Type adapterType;
96 | viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
97 | targetScript.ViewAdapterId,
98 | out adapterType
99 | );
100 |
101 | EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
102 | ? FontStyle.Bold
103 | : defaultLabelStyle;
104 |
105 | ShowAdapterOptionsMenu(
106 | "View adapter options",
107 | adapterType,
108 | options => targetScript.ViewAdapterOptions = options,
109 | targetScript.ViewAdapterOptions,
110 | viewAdapterOptionsFade.faded
111 | );
112 |
113 | EditorGUILayout.Space();
114 |
115 | EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
116 | ? FontStyle.Bold
117 | : defaultLabelStyle;
118 |
119 | var adaptedViewPropertyType = AdaptTypeBackward(
120 | viewPropertyType,
121 | targetScript.ViewAdapterId
122 | );
123 | ShowViewModelPropertyMenu(
124 | new GUIContent(
125 | "View-model property",
126 | "Property on the view-model to bind to."
127 | ),
128 | TypeResolver.FindBindableProperties(targetScript),
129 | updatedValue => targetScript.ViewModelPropertyName = updatedValue,
130 | targetScript.ViewModelPropertyName,
131 | property => property.PropertyType == adaptedViewPropertyType
132 | );
133 |
134 | GUI.enabled = guiPreviouslyEnabled;
135 |
136 | EditorStyles.label.fontStyle = defaultLabelStyle;
137 | }
138 |
139 | ///
140 | /// Check whether each of the properties on the object have been changed
141 | /// from the value in the prefab.
142 | ///
143 | private void UpdatePrefabModifiedProperties()
144 | {
145 | var property = serializedObject.GetIterator();
146 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
147 | // will iterate through the properties.
148 | property.Next(true);
149 | do
150 | {
151 | switch (property.name)
152 | {
153 | case "viewAdapterTypeName":
154 | viewAdapterPrefabModified = property.prefabOverride;
155 | break;
156 |
157 | case "viewAdapterOptions":
158 | viewAdapterOptionsPrefabModified = property.prefabOverride;
159 | break;
160 |
161 | case "viewModelPropertyName":
162 | viewModelPropertyPrefabModified = property.prefabOverride;
163 | break;
164 |
165 | case "viewPropertyName":
166 | viewPropertyPrefabModified = property.prefabOverride;
167 | break;
168 | }
169 | }
170 | while (property.Next(false));
171 | }
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/Editor/OneWayPropertyBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 362976f353b49a545856b39257e9eb02
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/SubViewModelBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEditor;
3 | using UnityWeld.Binding;
4 | using UnityWeld.Binding.Internal;
5 | using System.Linq;
6 | using System.Reflection;
7 |
8 | namespace UnityWeld_Editor
9 | {
10 | ///
11 | /// Inspector window for SubViewModelBinding
12 | ///
13 | [CustomEditor(typeof(SubViewModelBinding))]
14 | public class SubViewModelBindingEditor : BaseBindingEditor
15 | {
16 | private SubViewModelBinding targetScript;
17 |
18 | ///
19 | /// Whether or not the value on our target matches its prefab.
20 | ///
21 | private bool propertyPrefabModified;
22 |
23 | protected override void OnEnabled()
24 | {
25 | targetScript = (SubViewModelBinding)target;
26 | }
27 |
28 | protected override void OnInspector()
29 | {
30 | UpdatePrefabModifiedProperties();
31 |
32 | var bindableProperties = FindBindableProperties();
33 |
34 | EditorStyles.label.fontStyle = propertyPrefabModified
35 | ? FontStyle.Bold
36 | : DefaultFontStyle;
37 |
38 | ShowViewModelPropertyMenu(
39 | new GUIContent(
40 | "Sub view-model property",
41 | "The property on the top level view model containing the sub view-model"
42 | ),
43 | bindableProperties,
44 | updatedValue =>
45 | {
46 | targetScript.ViewModelPropertyName = updatedValue;
47 |
48 | targetScript.ViewModelTypeName = bindableProperties
49 | .Single(prop => prop.ToString() == updatedValue)
50 | .Member.PropertyType.ToString();
51 | },
52 | targetScript.ViewModelPropertyName,
53 | p => true
54 | );
55 | }
56 |
57 | private BindableMember[] FindBindableProperties()
58 | {
59 | return TypeResolver.FindBindableProperties(targetScript)
60 | .Where(prop => prop.Member.PropertyType.HasBindingAttribute()
61 | )
62 | .ToArray();
63 | }
64 |
65 | ///
66 | /// Check whether each of the properties on the object have been changed
67 | /// from the value in the prefab.
68 | ///
69 | private void UpdatePrefabModifiedProperties()
70 | {
71 | var property = serializedObject.GetIterator();
72 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
73 | // will iterate through the properties.
74 |
75 | propertyPrefabModified = false;
76 | property.Next(true);
77 | do
78 | {
79 | switch (property.name)
80 | {
81 | case "viewModelPropertyName":
82 | case "viewModelTypeName":
83 | propertyPrefabModified = property.prefabOverride
84 | || propertyPrefabModified;
85 | break;
86 | }
87 | }
88 | while (property.Next(false));
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/Editor/SubViewModelBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a40c8489f721eb24991cefbc29d4e6fa
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/TemplateBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using UnityEditor;
2 | using UnityEngine;
3 | using UnityWeld.Binding;
4 | using UnityWeld.Binding.Internal;
5 |
6 | namespace UnityWeld_Editor
7 | {
8 | [CustomEditor(typeof(TemplateBinding))]
9 | class TemplateBindingEditor : BaseBindingEditor
10 | {
11 | private TemplateBinding targetScript;
12 |
13 | private bool viewModelPrefabModified;
14 | private SerializedProperty _templatesProperty;
15 |
16 | protected override void OnEnabled()
17 | {
18 | targetScript = (TemplateBinding)target;
19 | _templatesProperty = serializedObject.FindProperty("_templates");
20 | }
21 |
22 | protected override void OnInspector()
23 | {
24 | UpdatePrefabModifiedProperties();
25 |
26 | EditorStyles.label.fontStyle = viewModelPrefabModified
27 | ? FontStyle.Bold
28 | : DefaultFontStyle;
29 |
30 | ShowViewModelPropertyMenu(
31 | new GUIContent(
32 | "Template property",
33 | "Property on the view model to use for selecting templates."
34 | ),
35 | TypeResolver.FindBindableProperties(targetScript),
36 | updatedValue => targetScript.ViewModelPropertyName = updatedValue,
37 | targetScript.ViewModelPropertyName,
38 | property => true
39 | );
40 |
41 | EditorGUILayout.PropertyField(_templatesProperty, true);
42 | }
43 |
44 | ///
45 | /// Check whether each of the properties on the object have been changed
46 | /// from the value in the prefab.
47 | ///
48 | private void UpdatePrefabModifiedProperties()
49 | {
50 | var property = serializedObject.GetIterator();
51 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
52 | // will iterate through the properties.
53 | property.Next(true);
54 | do
55 | {
56 | switch (property.name)
57 | {
58 | case "viewModelPropertyName":
59 | viewModelPrefabModified = property.prefabOverride;
60 | break;
61 | }
62 | }
63 | while (property.Next(false));
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Editor/TemplateBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b4ca5578372f3924fb0e72cdea97397d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/TemplateEditor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using UnityEditor;
4 | using UnityEngine;
5 | using UnityWeld.Binding;
6 | using UnityWeld.Binding.Internal;
7 |
8 | namespace UnityWeld_Editor
9 | {
10 | ///
11 | /// Editor for template bindings with a dropdown for selecting what view model
12 | /// to bind to.
13 | ///
14 | [CustomEditor(typeof(Template))]
15 | public class TemplateEditor : BaseBindingEditor
16 | {
17 | private Template targetScript;
18 |
19 | ///
20 | /// Whether the value on our target matches its prefab.
21 | ///
22 | private bool propertyPrefabModified;
23 |
24 | protected override void OnEnabled()
25 | {
26 | targetScript = (Template)target;
27 | }
28 |
29 | protected override void OnInspector()
30 | {
31 | UpdatePrefabModifiedProperties();
32 |
33 | var availableViewModels = TypeResolver.TypesWithBindingAttribute
34 | .Select(type => type.ToString())
35 | .OrderBy(name => name)
36 | .ToArray();
37 |
38 | var selectedIndex = Array.IndexOf(
39 | availableViewModels,
40 | targetScript.ViewModelTypeName
41 | );
42 |
43 | EditorStyles.label.fontStyle = propertyPrefabModified
44 | ? FontStyle.Bold
45 | : DefaultFontStyle;
46 |
47 | var newSelectedIndex = EditorGUILayout.Popup(
48 | new GUIContent(
49 | "Template view model",
50 | "Type of the view model that this template will be bound to when it is instantiated."
51 | ),
52 | selectedIndex,
53 | availableViewModels
54 | .Select(viewModel => new GUIContent(viewModel))
55 | .ToArray()
56 | );
57 |
58 | EditorStyles.label.fontStyle = DefaultFontStyle;
59 |
60 | UpdateProperty(newValue => targetScript.ViewModelTypeName = newValue,
61 | selectedIndex < 0
62 | ? string.Empty
63 | : availableViewModels[selectedIndex],
64 | newSelectedIndex < 0
65 | ? string.Empty
66 | : availableViewModels[newSelectedIndex],
67 | "Set bound view-model for template"
68 | );
69 | }
70 |
71 | ///
72 | /// Check whether each of the properties on the object have been changed from the value in the prefab.
73 | ///
74 | private void UpdatePrefabModifiedProperties()
75 | {
76 | var property = serializedObject.GetIterator();
77 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
78 | // will iterate through the properties.
79 | property.Next(true);
80 | do
81 | {
82 | if (property.name == "viewModelTypeName")
83 | {
84 | propertyPrefabModified = property.prefabOverride;
85 | }
86 | }
87 | while (property.Next(false));
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Editor/TemplateEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 41e9680918dbe374abf0efb0d8d9884d
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/ToggleActiveBindingEditor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEditor;
3 | using UnityEditor.AnimatedValues;
4 | using UnityEngine;
5 | using UnityWeld.Binding;
6 | using UnityWeld.Binding.Internal;
7 |
8 | namespace UnityWeld_Editor
9 | {
10 | [CustomEditor(typeof(ToggleActiveBinding))]
11 | public class ToggleActiveBindingEditor : BaseBindingEditor
12 | {
13 | private ToggleActiveBinding targetScript;
14 |
15 | private AnimBool viewAdapterOptionsFade;
16 |
17 | private bool viewAdapterPrefabModified;
18 | private bool viewAdapterOptionsPrefabModified;
19 | private bool viewModelPropertyPrefabModified;
20 |
21 | protected override void OnEnabled()
22 | {
23 | targetScript = (ToggleActiveBinding)target;
24 |
25 | viewAdapterOptionsFade = new AnimBool(ShouldShowAdapterOptions(targetScript.ViewAdapterId, out _));
26 | viewAdapterOptionsFade.valueChanged.AddListener(Repaint);
27 | }
28 |
29 | private void OnDisable()
30 | {
31 | viewAdapterOptionsFade.valueChanged.RemoveListener(Repaint);
32 | }
33 |
34 | protected override void OnInspector()
35 | {
36 | UpdatePrefabModifiedProperties();
37 |
38 | var viewPropertyType = typeof(bool);
39 |
40 | var viewAdapterTypeNames = TypeResolver.GetAdapterIds(o => o.OutType == viewPropertyType);
41 |
42 | EditorStyles.label.fontStyle = viewAdapterPrefabModified
43 | ? FontStyle.Bold
44 | : DefaultFontStyle;
45 |
46 | ShowAdapterMenu(
47 | new GUIContent(
48 | "View adapter",
49 | "Adapter that converts values sent from the view-model to the view."
50 | ),
51 | viewAdapterTypeNames,
52 | targetScript.ViewAdapterId,
53 | newValue =>
54 | {
55 | // Get rid of old adapter options if we changed the type of the adapter.
56 | if (newValue != targetScript.ViewAdapterId)
57 | {
58 | Undo.RecordObject(targetScript, "Set view adapter options");
59 | targetScript.ViewAdapterOptions = null;
60 | }
61 |
62 | UpdateProperty(
63 | updatedValue => targetScript.ViewAdapterId = updatedValue,
64 | targetScript.ViewAdapterId,
65 | newValue,
66 | "Set view adapter"
67 | );
68 | }
69 | );
70 |
71 | Type adapterType;
72 | viewAdapterOptionsFade.target = ShouldShowAdapterOptions(
73 | targetScript.ViewAdapterId,
74 | out adapterType
75 | );
76 |
77 | EditorStyles.label.fontStyle = viewAdapterOptionsPrefabModified
78 | ? FontStyle.Bold
79 | : DefaultFontStyle;
80 |
81 | ShowAdapterOptionsMenu(
82 | "View adapter options",
83 | adapterType,
84 | options => targetScript.ViewAdapterOptions = options,
85 | targetScript.ViewAdapterOptions,
86 | viewAdapterOptionsFade.faded
87 | );
88 |
89 | EditorGUILayout.Space();
90 |
91 | EditorStyles.label.fontStyle = viewModelPropertyPrefabModified
92 | ? FontStyle.Bold
93 | : DefaultFontStyle;
94 |
95 | var adaptedViewPropertyType = AdaptTypeBackward(
96 | viewPropertyType,
97 | targetScript.ViewAdapterId
98 | );
99 | ShowViewModelPropertyMenu(
100 | new GUIContent(
101 | "View-model property",
102 | "Property on the view-model to bind to."
103 | ),
104 | TypeResolver.FindBindableProperties(targetScript),
105 | updatedValue => targetScript.ViewModelPropertyName = updatedValue,
106 | targetScript.ViewModelPropertyName,
107 | property => property.PropertyType == adaptedViewPropertyType
108 | );
109 | }
110 |
111 | private void UpdatePrefabModifiedProperties()
112 | {
113 | var property = serializedObject.GetIterator();
114 | // Need to call Next(true) to get the first child. Once we have it, Next(false)
115 | // will iterate through the properties.
116 | property.Next(true);
117 | do
118 | {
119 | switch (property.name)
120 | {
121 | case "viewAdapterTypeName":
122 | viewAdapterPrefabModified = property.prefabOverride;
123 | break;
124 |
125 | case "viewAdapterOptions":
126 | viewAdapterOptionsPrefabModified = property.prefabOverride;
127 | break;
128 |
129 | case "viewModelPropertyName":
130 | viewModelPropertyPrefabModified = property.prefabOverride;
131 | break;
132 | }
133 | }
134 | while (property.Next(false));
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Editor/ToggleActiveBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e2ae3e18ed11fbd46af8d4ae70099c0e
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/TwoWayPropertyBindingEditor.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2c7ae872ed2100b46a600b9b54780bf0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Editor/UnityWeld.Editor.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "UnityWeld.Editor",
3 | "rootNamespace": "UnityWeld_Editor",
4 | "references": [
5 | "GUID:af0045edb0742c34c9f8aefb8323e52c"
6 | ],
7 | "includePlatforms": [
8 | "Editor"
9 | ],
10 | "excludePlatforms": [],
11 | "allowUnsafeCode": false,
12 | "overrideReferences": false,
13 | "precompiledReferences": [],
14 | "autoReferenced": true,
15 | "defineConstraints": [],
16 | "versionDefines": [],
17 | "noEngineReferences": false
18 | }
--------------------------------------------------------------------------------
/Editor/UnityWeld.Editor.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c29ab574f158e5945b4ec0492d3c47e7
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Real Serious Games
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 950ecacf92f7f7743b5e5d373d964b7f
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unity-Weld
2 | *[MVVM-style](https://msdn.microsoft.com/en-us/library/hh848246.aspx) data-binding system for Unity.*
3 |
4 | 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.
5 |
6 | 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).
7 |
8 | 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).
9 |
10 | ## Installation
11 |
12 | To install Unity-Weld in a new or existing Unity project:
13 | Option 1: use package.json to locally install as UPM package using UPM package mananger
14 | Option 2: generate upm package using npm tool (npm pack), upload to your feed and add your feed to Unity Package Manager
15 |
16 | Alternatively, just copy the `Editor` to `Scripts/UnityWeld/Editor` and `Runtime` to `Scripts/UnityWeld/Runtime` into your `Assets` directory in your Unity project.
17 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 43a15502f0f199743b3e22834c7408ee
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Runtime.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7ece62dfb5dd94349a31b908b4e1355d
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/AOTOptimisationHelper.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.EventSystems;
3 | using UnityWeld.Binding.Internal;
4 | // ReSharper disable UnusedMember.Global
5 | // ReSharper disable UnusedMember.Local
6 | // ReSharper disable UnusedVariable
7 |
8 | #pragma warning disable 219 // Disable warning that variable is never used
9 |
10 | namespace UnityWeld
11 | {
12 | ///
13 | /// In order for certain generic types to not be optimised-out by IL2CPP for
14 | /// platforms like Xbox One, iPhone and WebGL, we need to reference them at
15 | /// least once in code instead of just calling them via reflection.
16 | ///
17 | /// See this page for more details:
18 | /// https://docs.unity3d.com/Manual/TroubleShootingIPhone.html
19 | /// In the section "The game crashes with the error message “ExecutionEngineException:
20 | /// Attempting to JIT compile method ‘SometType`1<SomeValueType>:.ctor ()’ while
21 | /// running with –aot-only.”"
22 | ///
23 | internal class AOTOptimisationHelper
24 | {
25 | // Even though this method is never called, the fact that it exists will
26 | // ensure the compiler includes the types referenced in it so that we can
27 | // later refer to those via reflection.
28 | private void EnsureGenericTypes()
29 | {
30 | // Used by InputField
31 | var strEventBinder = new UnityEventBinder(null, null);
32 |
33 | // Used by Slider and Scrollbar
34 | var floatEventBinder = new UnityEventBinder(null, null);
35 |
36 | // Used by Toggle
37 | var boolEventBinder = new UnityEventBinder(null, null);
38 |
39 | // Used by Dropdown
40 | var intEventBinder = new UnityEventBinder(null, null);
41 |
42 | // Used by ScrollRect
43 | var vector2EventBinder = new UnityEventBinder(null, null);
44 |
45 | // Used by ColorTween
46 | var colorEventBinder = new UnityEventBinder(null, null);
47 |
48 | // Used by EventTrigger
49 | var baseEventDataEventBinder = new UnityEventBinder(null, null);
50 | }
51 | }
52 | }
53 |
54 | #pragma warning restore 219
--------------------------------------------------------------------------------
/Runtime/AOTOptimisationHelper.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9b067127a7e01f0408d7cb11797df5c0
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Binding.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1f7dff4136c1c10459fc630a239805ee
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Runtime/Binding/AbstractMemberBinding.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using UnityEngine;
3 | using UnityWeld.Binding.Exceptions;
4 | using UnityWeld.Binding.Internal;
5 |
6 | namespace UnityWeld.Binding
7 | {
8 | ///
9 | /// Base class for binders to Unity MonoBehaviours.
10 | ///
11 | [HelpURL("https://github.com/Real-Serious-Games/Unity-Weld")]
12 | public abstract class AbstractMemberBinding : MonoBehaviour, IMemberBinding
13 | {
14 | private bool _isInitCalled;
15 |
16 | [SerializeField, Header("Automatically bind once on \"OnEnable()\"")]
17 | private bool _isAutoConnection;
18 |
19 |
20 | ///
21 | /// Initialise this binding. Used when we first start the scene.
22 | /// Detaches any attached view models, finds available view models afresh and then connects the binding.
23 | ///
24 | public virtual void Init()
25 | {
26 | if(_isAutoConnection && !gameObject.activeInHierarchy)
27 | {
28 | return; //wait for enabling
29 | }
30 |
31 | if (_isInitCalled)
32 | {
33 | return; //avoid double connect
34 | }
35 |
36 | _isInitCalled = true;
37 |
38 | Disconnect();
39 | Connect();
40 | }
41 |
42 | ///
43 | /// Scan up the hierarchy and find a view model that corresponds to the specified name.
44 | ///
45 | private object FindViewModel(string viewModelName)
46 | {
47 | var trans = transform;
48 | while(trans != null)
49 | {
50 | using(var cache = trans.gameObject.GetComponentsWithCache(false))
51 | {
52 | var monoBehaviourViewModel = cache.Components
53 | .FirstOrDefault(component => component.GetType().ToString() == viewModelName);
54 | if(monoBehaviourViewModel != null)
55 | {
56 | return monoBehaviourViewModel;
57 | }
58 |
59 | var providedViewModel = cache.Components
60 | .Select(component => component.GetViewModelData())
61 | .Where(component => component != null)
62 | .FirstOrDefault(viewModelData => viewModelData.TypeName == viewModelName);
63 |
64 | if(providedViewModel != null)
65 | {
66 | return providedViewModel.Model;
67 | }
68 | }
69 |
70 | trans = trans.parent;
71 | }
72 |
73 | throw new ViewModelNotFoundException(
74 | $"Tried to get view model {viewModelName} but it could not be found on " +
75 | $"object {gameObject.name}. Check that a ViewModelBinding for that view model exists further up in " +
76 | "the scene hierarchy. "
77 | );
78 | }
79 |
80 | ///
81 | /// Make a property end point for a property on the view model.
82 | ///
83 | protected PropertyEndPoint MakeViewModelEndPoint(string viewModelPropertyName, string adapterId,
84 | AdapterOptions adapterOptions)
85 | {
86 | string propertyName;
87 | object viewModel;
88 | ParseViewModelEndPointReference(viewModelPropertyName, out propertyName, out viewModel);
89 |
90 | var adapter = TypeResolver.GetAdapter(adapterId);
91 | return new PropertyEndPoint(viewModel, propertyName, adapter, adapterOptions, "view-model", this);
92 | }
93 |
94 | ///
95 | /// Parse an end-point reference including a type name and member name separated by a period.
96 | ///
97 | protected static void ParseEndPointReference(string endPointReference, out string memberName,
98 | out string typeName)
99 | {
100 | var lastPeriodIndex = endPointReference.LastIndexOf('.');
101 | if(lastPeriodIndex == -1)
102 | {
103 | throw new InvalidEndPointException(
104 | "No period was found, expected end-point reference in the following format: .. " +
105 | "Provided end-point reference: " + endPointReference
106 | );
107 | }
108 |
109 | typeName = endPointReference.Substring(0, lastPeriodIndex);
110 | memberName = endPointReference.Substring(lastPeriodIndex + 1);
111 | //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
112 | //So to be safe, we remove all namespaces from any component that starts with UnityEngine
113 | if(typeName.StartsWith("UnityEngine."))
114 | {
115 | typeName = typeName.Substring(typeName.LastIndexOf('.') + 1);
116 | }
117 |
118 | if(typeName.Length == 0 || memberName.Length == 0)
119 | {
120 | throw new InvalidEndPointException(
121 | "Bad format for end-point reference, expected the following format: .. " +
122 | "Provided end-point reference: " + endPointReference
123 | );
124 | }
125 | }
126 |
127 | ///
128 | /// Parse an end-point reference and search up the hierarchy for the named view-model.
129 | ///
130 | protected void ParseViewModelEndPointReference(string endPointReference, out string memberName,
131 | out object viewModel)
132 | {
133 | string viewModelName;
134 | ParseEndPointReference(endPointReference, out memberName, out viewModelName);
135 |
136 | viewModel = FindViewModel(viewModelName);
137 | if(viewModel == null)
138 | {
139 | throw new ViewModelNotFoundException("Failed to find view-model in hierarchy: " + viewModelName);
140 | }
141 | }
142 |
143 | ///
144 | /// Parse an end-point reference and get the component for the view.
145 | ///
146 | protected void ParseViewEndPointReference(string endPointReference, out string memberName, out Component view)
147 | {
148 | string boundComponentType;
149 | ParseEndPointReference(endPointReference, out memberName, out boundComponentType);
150 |
151 | view = GetComponent(boundComponentType);
152 | if(view == null)
153 | {
154 | throw new ComponentNotFoundException("Failed to find component on current game object: " +
155 | boundComponentType);
156 | }
157 | }
158 |
159 | ///
160 | /// Connect to all the attached view models
161 | ///
162 | public abstract void Connect();
163 |
164 | ///
165 | /// Disconnect from all attached view models.
166 | ///
167 | public abstract void Disconnect();
168 |
169 | ///
170 | /// Standard MonoBehaviour awake message, do not call this explicitly.
171 | /// Initialises the binding.
172 | ///
173 | protected void OnEnable()
174 | {
175 | if (!_isAutoConnection || _isInitCalled)
176 | {
177 | return;
178 | }
179 |
180 | Init();
181 | }
182 |
183 | ///
184 | /// Clean up when the game object is destroyed.
185 | ///
186 | public virtual void OnDestroy()
187 | {
188 | Disconnect();
189 | }
190 |
191 | public void ResetBinding()
192 | {
193 | _isInitCalled = false;
194 | Disconnect();
195 | }
196 | }
197 | }
--------------------------------------------------------------------------------
/Runtime/Binding/AbstractMemberBinding.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: cbecddc181c8f044dadab0f55a663934
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Runtime/Binding/AbstractTemplateSelector.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using UnityEngine;
5 | using UnityEngine.Assertions;
6 | using UnityWeld.Binding.Exceptions;
7 | using UnityWeld.Binding.Internal;
8 |
9 | namespace UnityWeld.Binding
10 | {
11 | public abstract class AbstractTemplateSelector : AbstractMemberBinding
12 | {
13 | [Header("Set templates for collection")]
14 | [SerializeField] private Template[] _templates;
15 | [SerializeField] private string viewModelPropertyName = string.Empty;
16 |
17 | private IDictionary _availableTemplates;
18 |
19 | ///
20 | /// All the child objects that have been created, indexed by the view they are connected to.
21 | ///
22 | private readonly IDictionary