@@ -36,6 +36,7 @@ namespace PlayEveryWare.EpicOnlineServices.Editor.Utility
3636 using System . Globalization ;
3737 using System . Linq ;
3838 using System . Reflection ;
39+ using System . Text ;
3940 using UnityEditor ;
4041 using UnityEditorInternal ;
4142 using UnityEngine ;
@@ -245,7 +246,7 @@ public static void AssigningEnumField<T>(string label, ref T value, float labelW
245246 . OrderBy ( group => group . Key ) ;
246247 }
247248
248- private static IOrderedEnumerable < IGrouping < int , ( MemberInfo MemberInfo , ConfigFieldAttribute FieldDetails ) > > GetMembersByGroup < T > ( )
249+ private static IOrderedEnumerable < IGrouping < int , ( MemberInfo MemberInfo , ConfigFieldAttribute FieldDetails , IEnumerable < FieldValidatorAttribute > FieldValidators ) > > GetMembersByGroup < T > ( )
249250 {
250251 var fields = typeof ( T ) . GetFields ( BindingFlags . Public | BindingFlags . Instance ) ;
251252 var properties = typeof ( T ) . GetProperties ( BindingFlags . Public | BindingFlags . Instance ) ;
@@ -254,7 +255,8 @@ public static void AssigningEnumField<T>(string label, ref T value, float labelW
254255
255256 return members
256257 . Where ( member => member . GetCustomAttribute < ConfigFieldAttribute > ( ) != null )
257- . Select ( member => ( MemberInfo : member , FieldDetails : member . GetCustomAttribute < ConfigFieldAttribute > ( ) ) )
258+ . Select ( member => ( MemberInfo : member , FieldDetails : member . GetCustomAttribute < ConfigFieldAttribute > ( ) ,
259+ FieldValidators : member . GetCustomAttributes < FieldValidatorAttribute > ( ) ) )
258260 . GroupBy ( r => r . FieldDetails . Group )
259261 . OrderBy ( group => group . Key ) ;
260262 }
@@ -271,7 +273,6 @@ public static void AssigningEnumField<T>(string label, ref T value, float labelW
271273 { typeof ( float ) , ( attr , val , width ) => RenderInput ( attr , ( float ) val , width ) } ,
272274 { typeof ( double ) , ( attr , val , width ) => RenderInput ( attr , ( double ) val , width ) } ,
273275 { typeof ( bool ) , ( attr , val , width ) => RenderInput ( attr , ( bool ) val , width ) } ,
274- { typeof ( Version ) , ( attr , val , width ) => RenderInput ( attr , ( Version ) val , width ) } ,
275276 { typeof ( Guid ) , ( attr , val , width ) => RenderInput ( attr , ( Guid ) val , width ) } ,
276277 { typeof ( List < string > ) , ( attr , val , width ) => RenderInput ( attr , ( List < string > ) val , width ) } ,
277278#if ! EOS_DISABLE
@@ -500,6 +501,9 @@ public static void RenderInputs<T>(ref T value)
500501 {
501502 continue ; // Skip if MemberInfo is neither FieldInfo nor PropertyInfo
502503 }
504+
505+ // Assign the validators
506+ member . FieldDetails . Validators = member . FieldValidators ;
503507
504508 // Use the handler from the dictionary
505509 if ( FieldHandlers . TryGetValue ( member . FieldDetails . FieldType , out var handler ) )
@@ -1126,7 +1130,7 @@ public static WrappedInitializeThreadAffinity RenderInput(ConfigFieldAttribute a
11261130#endif
11271131 private static Guid RenderInput ( ConfigFieldAttribute configFieldDetails , Guid value , float labelWidth )
11281132 {
1129- return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value ,
1133+ return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , configFieldDetails . Validators ,
11301134 GuidField ) ;
11311135 }
11321136
@@ -1212,22 +1216,13 @@ public static Deployment RenderInput(ConfigFieldAttribute configFieldAttribute,
12121216 public static TEnum RenderEnumInput < TEnum > ( ConfigFieldAttribute configFieldAttribute , TEnum value , float labelWidth ) where TEnum : Enum
12131217 {
12141218 return InputRendererWrapper ( configFieldAttribute . Label , configFieldAttribute . ToolTip , labelWidth , value ,
1215- EnumFlagsField , configFieldAttribute . HelpURL ) ;
1219+ configFieldAttribute . Validators , EnumFlagsField , configFieldAttribute . HelpURL ) ;
12161220 }
12171221
12181222 private static TEnum EnumFlagsField < TEnum > ( GUIContent label , TEnum value , params GUILayoutOption [ ] options ) where TEnum : Enum
12191223 {
12201224 return ( TEnum ) EditorGUILayout . EnumFlagsField ( label , value , options ) ;
12211225 }
1222- private static Version RenderInput ( ConfigFieldAttribute configFieldAttribute , Version value , float labelWidth )
1223- {
1224- return RenderInput ( value , configFieldAttribute . Label , configFieldAttribute . ToolTip , labelWidth ) ;
1225- }
1226-
1227- public static Version RenderInput ( Version value , string label , string tooltip , float labelWidth )
1228- {
1229- return InputRendererWrapper ( label , tooltip , labelWidth , value , VersionField ) ;
1230- }
12311226
12321227 public static ProductionEnvironments RenderInput ( ConfigFieldAttribute configFieldAttribute ,
12331228 ProductionEnvironments value , float labelWidth )
@@ -1306,7 +1301,7 @@ public static string RenderInput(DirectoryPathFieldAttribute configFieldAttribut
13061301 {
13071302 EditorGUILayout . BeginHorizontal ( ) ;
13081303
1309- string filePath = InputRendererWrapper ( configFieldAttributeDetails . Label , value , labelWidth , tooltip , EditorGUILayout . TextField , configFieldAttributeDetails . HelpURL ) ;
1304+ string filePath = InputRendererWrapper ( configFieldAttributeDetails . Label , value , labelWidth , tooltip , configFieldAttributeDetails . Validators , EditorGUILayout . TextField , configFieldAttributeDetails . HelpURL ) ;
13101305
13111306 if ( GUILayout . Button ( "Select" , GUILayout . MaxWidth ( MAXIMUM_BUTTON_WIDTH ) ) )
13121307 {
@@ -1327,7 +1322,7 @@ public static string RenderInput(FilePathFieldAttribute configFieldAttributeDeta
13271322 {
13281323 EditorGUILayout . BeginHorizontal ( ) ;
13291324
1330- string filePath = InputRendererWrapper ( configFieldAttributeDetails . Label , value , labelWidth , tooltip , EditorGUILayout . TextField , configFieldAttributeDetails . HelpURL ) ;
1325+ string filePath = InputRendererWrapper ( configFieldAttributeDetails . Label , value , labelWidth , tooltip , configFieldAttributeDetails . Validators , EditorGUILayout . TextField , configFieldAttributeDetails . HelpURL ) ;
13311326
13321327 if ( GUILayout . Button ( "Select" , GUILayout . MaxWidth ( MAXIMUM_BUTTON_WIDTH ) ) )
13331328 {
@@ -1347,35 +1342,25 @@ public static string RenderInput(FilePathFieldAttribute configFieldAttributeDeta
13471342
13481343 public static double RenderInput ( ConfigFieldAttribute configFieldDetails , double value , float labelWidth )
13491344 {
1350- return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , EditorGUILayout . DoubleField , configFieldDetails . HelpURL ) ;
1345+ return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , configFieldDetails . Validators , EditorGUILayout . DoubleField , configFieldDetails . HelpURL ) ;
13511346 }
13521347
13531348 public static float RenderInput ( ConfigFieldAttribute configFieldDetails , float value , float labelWidth )
13541349 {
1355- return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , EditorGUILayout . FloatField , configFieldDetails . HelpURL ) ;
1350+ return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , configFieldDetails . Validators , EditorGUILayout . FloatField , configFieldDetails . HelpURL ) ;
13561351 }
13571352
13581353 public static string RenderInput ( ConfigFieldAttribute configFieldDetails , string value , float labelWidth )
13591354 {
1360- return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , EditorGUILayout . TextField , configFieldDetails . HelpURL ) ;
1355+ return InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , value , configFieldDetails . Validators , EditorGUILayout . TextField , configFieldDetails . HelpURL ) ;
13611356 }
13621357
13631358 public static ulong RenderInput ( ConfigFieldAttribute configFieldDetails , ulong value , float labelWidth )
13641359 {
13651360 _ = SafeTranslatorUtility . TryConvert ( value , out long temp ) ;
13661361
13671362 long longValue = InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth ,
1368- temp , EditorGUILayout . LongField ) ;
1369-
1370- return SafeTranslatorUtility . TryConvert ( longValue , out ulong newValue ) ? newValue : value ;
1371- }
1372-
1373- private static ulong RenderInput ( string label , string tooltip , ulong value , float labelWidth )
1374- {
1375- _ = SafeTranslatorUtility . TryConvert ( value , out long temp ) ;
1376-
1377- long longValue = InputRendererWrapper ( label , tooltip , labelWidth ,
1378- temp , EditorGUILayout . LongField ) ;
1363+ temp , configFieldDetails . Validators , EditorGUILayout . LongField ) ;
13791364
13801365 return SafeTranslatorUtility . TryConvert ( longValue , out ulong newValue ) ? newValue : value ;
13811366 }
@@ -1384,7 +1369,7 @@ public static uint RenderInput(ConfigFieldAttribute configFieldDetails, uint val
13841369 {
13851370 _ = SafeTranslatorUtility . TryConvert ( value , out int temp ) ;
13861371
1387- int intValue = InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , temp ,
1372+ int intValue = InputRendererWrapper ( configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth , temp , configFieldDetails . Validators ,
13881373 EditorGUILayout . IntField ) ;
13891374
13901375 return SafeTranslatorUtility . TryConvert ( intValue , out uint newValue ) ? newValue : value ;
@@ -1394,10 +1379,10 @@ public static bool RenderInput(ConfigFieldAttribute configFieldDetails, bool val
13941379 {
13951380 return InputRendererWrapper (
13961381 configFieldDetails . Label , configFieldDetails . ToolTip , labelWidth ,
1397- value , EditorGUILayout . Toggle ) ;
1382+ value , configFieldDetails . Validators , EditorGUILayout . Toggle ) ;
13981383 }
13991384
1400- public delegate T TestDelegate < T > ( GUIContent label , T value , params GUILayoutOption [ ] options ) ;
1385+ public delegate T InputRendererDelegate < T > ( GUIContent label , T value , params GUILayoutOption [ ] options ) ;
14011386
14021387 private static T InputRendererWithAlignedLabel < T > ( float labelWidth , Func < T > renderFn )
14031388 {
@@ -1412,10 +1397,50 @@ private static T InputRendererWithAlignedLabel<T>(float labelWidth, Func<T> rend
14121397 return newValue ;
14131398 }
14141399
1415- private static T InputRendererWrapper < T > ( string label , string toolTip , float labelWidth , T value , TestDelegate < T > renderFn , string helpURL = null )
1400+ private static void RunValidators ( IEnumerable < FieldValidatorAttribute > validators , object value , out bool isValid )
1401+ {
1402+ isValid = true ;
1403+ StringBuilder errorMessageBuilder = new ( ) ;
1404+ foreach ( var validator in validators )
1405+ {
1406+ // If field is valid then go to the next validator.
1407+ if ( validator . FieldValueIsValid ( value , out string errorMessage ) )
1408+ {
1409+ continue ;
1410+ }
1411+
1412+ // Otherwise append message from the validator.
1413+ errorMessageBuilder . AppendLine ( errorMessage ) ;
1414+ }
1415+
1416+ // If there are no error messages, then stop here
1417+ if ( errorMessageBuilder . Length == 0 )
1418+ {
1419+ return ;
1420+ }
1421+
1422+ isValid = false ;
1423+ EditorGUILayout . HelpBox ( errorMessageBuilder . ToString ( ) , MessageType . Warning ) ;
1424+ }
1425+
1426+ private static T InputRendererWrapper < T > ( string label , string toolTip , float labelWidth , T value , IEnumerable < FieldValidatorAttribute > validators , InputRendererDelegate < T > renderFn , string helpURL = null )
14161427 {
14171428 return InputRendererWithAlignedLabel ( labelWidth , ( ) =>
14181429 {
1430+ // Run validators for the config field.
1431+ RunValidators ( validators , value , out bool isCurrentValueValid ) ;
1432+
1433+ // Store the previous background color so that it can be
1434+ // restored if need be.
1435+ Color previousBackgroundColor = GUI . backgroundColor ;
1436+ if ( ! isCurrentValueValid )
1437+ {
1438+ // This sets the background color for the input field that
1439+ // is about to be rendered to red - further highlighting the
1440+ // field that has invalid values.
1441+ GUI . backgroundColor = Color . red ;
1442+ }
1443+
14191444 if ( ! string . IsNullOrEmpty ( helpURL ) )
14201445 {
14211446 EditorGUILayout . BeginHorizontal ( ) ;
@@ -1429,6 +1454,10 @@ private static T InputRendererWrapper<T>(string label, string toolTip, float lab
14291454 EditorGUILayout . EndHorizontal ( ) ;
14301455 }
14311456
1457+ // Restore the background color that was set before the field
1458+ // was rendered.
1459+ GUI . backgroundColor = previousBackgroundColor ;
1460+
14321461 return newValue ;
14331462 } ) ;
14341463 }
0 commit comments