Skip to content

Commit

Permalink
Merge pull request #110 from FishingCactus/feature/select_implementat…
Browse files Browse the repository at this point in the history
…ion_utils

Feature/select implementation utils
  • Loading branch information
lucas-baran authored May 3, 2024
2 parents 2ff7315 + da74f5c commit 2533bb1
Show file tree
Hide file tree
Showing 38 changed files with 1,353 additions and 9 deletions.
37 changes: 32 additions & 5 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_style_prefer_conditional_expression_over_return = true
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
Expand All @@ -70,7 +72,7 @@ dotnet_remove_unnecessary_suppression_exclusions = none

# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion

#### C# Coding Conventions ####

Expand All @@ -92,6 +94,7 @@ csharp_style_expression_bodied_properties = true
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
csharp_style_prefer_switch_expression = true
Expand All @@ -101,20 +104,29 @@ csharp_style_conditional_delegate_call = true

# Modifier preferences
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_readonly_struct_member = true

# Code-block preferences
csharp_prefer_braces = true
csharp_prefer_braces = true:suggestion
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_primary_constructors = true
csharp_style_prefer_top_level_statements = true

# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true
csharp_style_inlined_variable_declaration = true
csharp_style_pattern_local_over_anonymous_function = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
Expand All @@ -124,6 +136,8 @@ csharp_using_directive_placement = outside_namespace

# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true

Expand All @@ -142,7 +156,7 @@ csharp_new_line_between_query_expression_clauses = true
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true

Expand Down Expand Up @@ -190,6 +204,10 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.local_should_be_snake_case.severity = suggestion
dotnet_naming_rule.local_should_be_snake_case.symbols = local
dotnet_naming_rule.local_should_be_snake_case.style = snake_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
Expand All @@ -204,6 +222,10 @@ dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, meth
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =

dotnet_naming_symbols.local.applicable_kinds = parameter, local
dotnet_naming_symbols.local.applicable_accessibilities = local
dotnet_naming_symbols.local.required_modifiers = abstract, async, readonly, static

# Naming styles

dotnet_naming_style.pascal_case.required_prefix =
Expand All @@ -215,3 +237,8 @@ dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case

dotnet_naming_style.snake_case.required_prefix =
dotnet_naming_style.snake_case.required_suffix =
dotnet_naming_style.snake_case.word_separator = _
dotnet_naming_style.snake_case.capitalization = all_lower
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Changelog

## [1.2.0] - 2024-04-24
### Added
- Add `ReflectionUtilities` static class
- Get implementation types from base type
- Create instances of implementation types from base type
- Add `SelectImplementationDropdown` to select the implementation of a base type in the inspector
- Add property context menu utilities scripts:
- Add `IPropertyContextMenuCallback` and `PropertyContextMenuCallbackFetcher` to make it easier to add custom property context menus
- Add `CreateAssetContextMenu` to quickly create scriptable objects by selecting an implementation
- Add `VectorContextMenu` to quickly set values of vectors
- Add `HierarchyScriptDropHandler` which allows people to drag and drop scripts inside the scene hierarchy to directly create an empty game object with the script attached
- Add `ReorderableSubAssetList` which allows to create sub assets in a list by selecting the implementation
- Add `HierarchyUtilities` with utility functions to rename and create game objects in the scene hierarchy
- Add utility functions in `InspectorUtilities` to get the type of a serialized property that works for lists and arrays
### Updated
- Update .editorconfig
- correct indentation for brackets inside switch-cases
- suggest empty line after code blocks
- suggest brackets for code blocks statements
- suggest snake_case for local variables and parameters

## [1.1.2] - 2024-04-11
### Added
- Add publish config in package.json
Expand Down
8 changes: 8 additions & 0 deletions Editor/ContextMenus.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Editor/ContextMenus/PropertyContextMenus.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

182 changes: 182 additions & 0 deletions Editor/ContextMenus/PropertyContextMenus/CreateAssetContextMenu.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace FishingCactus
{
public sealed class CreateAssetContextMenu : IPropertyContextMenuCallback
{
private readonly static float MIN_DROPDOWN_WIDTH = 300f;

private SerializedObject _targetObject = null;
private SerializedProperty _targetProperty = null;
private SelectImplementationDropdown _dropdown = null;

public void OnPropertyContextMenu(
Rect context_menu_rect,
GenericMenu menu,
SerializedProperty property
)
{
if( property.propertyType != SerializedPropertyType.ObjectReference )
{
return;
}

Type property_type = InspectorUtilities.GetPropertyType( property );
Type scriptable_object_type = typeof( ScriptableObject );

if( !scriptable_object_type.IsAssignableFrom( property_type ) )
{
return;
}

context_menu_rect.size = new Vector2( Mathf.Max( context_menu_rect.size.x, MIN_DROPDOWN_WIDTH ), context_menu_rect.size.y );
_targetObject = property.serializedObject;
_targetProperty = property;

menu.AddItem( new GUIContent( "Create asset" ), on: false, () =>
{
if( _dropdown != null )
{
_dropdown.OnImplementationSelected -= Dropdown_OnImplementationSelected;
}
var implementations = ReflectionUtilities.GetImplementations( property_type, IsValidImplementation );
_dropdown = new SelectImplementationDropdown( new AdvancedDropdownState(), property_type, IsValidImplementation );
_dropdown.Show( context_menu_rect );
_dropdown.OnImplementationSelected += Dropdown_OnImplementationSelected;
} );
}

private void Dropdown_OnImplementationSelected(
Type type
)
{
CreateAssetOfType( _targetObject, _targetProperty, type );
_dropdown.OnImplementationSelected -= Dropdown_OnImplementationSelected;
}

private static bool IsValidImplementation(
Type type
)
{
return !type.IsInterface
&& !type.IsAbstract
&& !type.ContainsGenericParameters;
}

private static void GetCreateAssetMenuAttributeInfo(
Type asset_type,
out string file_name
)
{
file_name = string.Empty;

foreach( CustomAttributeData attribute in asset_type.CustomAttributes )
{
if( attribute.AttributeType != typeof( CreateAssetMenuAttribute ) )
{
continue;
}

for( int argument_index = 0; argument_index < attribute.NamedArguments.Count; argument_index++ )
{
var argument_info = attribute.NamedArguments[ argument_index ];

if( argument_info.MemberName == "fileName" )
{
file_name = ( string )argument_info.TypedValue.Value;
}
}

if( !string.IsNullOrWhiteSpace( file_name ) )
{
break;
}
}
}

private static void CreateAssetOfType(
SerializedObject serialized_object,
SerializedProperty property,
Type asset_type
)
{
Type project_window_util_type = typeof( ProjectWindowUtil );
MethodInfo get_active_folder_path = project_window_util_type.GetMethod( "TryGetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic );
object[] parameters = new object[ 1 ];
bool has_project_window = ( bool )get_active_folder_path.Invoke( obj: null, parameters );

if( !has_project_window )
{
Debug.LogError( "No project window is currently opened" );

return;
}

ScriptableObject asset = ScriptableObject.CreateInstance( asset_type );
string asset_path = ( string )parameters[ 0 ];
// TODO: use custom project settings for asset name convention (eg "SO_{name}")
string asset_name = $"SO_{asset_type.Name}";

GetCreateAssetMenuAttributeInfo( asset_type, out string file_name );

if( !string.IsNullOrWhiteSpace( file_name ) )
{
asset_name = file_name;
}

asset_path += $"/{asset_name}";
AddIndexInName( ref asset_path );
asset_path += ".asset";
AssetDatabase.CreateAsset( asset, asset_path );

serialized_object.Update();
property.objectReferenceValue = asset;
serialized_object.ApplyModifiedProperties();

if( serialized_object.targetObject is Component target_component )
{
Scene scene = target_component.gameObject.scene;
EditorSceneManager.MarkSceneDirty( scene );
EditorSceneManager.SaveScene( scene );
}

AssetDatabase.SaveAssets();
}

private static void AddIndexInName(
ref string asset_path
)
{
string new_asset_path = asset_path;
int index = 0;

while( !string.IsNullOrEmpty( AssetDatabase.AssetPathToGUID( $"{new_asset_path}.asset", AssetPathToGUIDOptions.OnlyExistingAssets ) ) )
{
new_asset_path = GetFormatedAssetName( asset_path, index );
index++;
}

asset_path = new_asset_path;
}

private static string GetFormatedAssetName(
string asset_path,
int index
)
{
return EditorSettings.gameObjectNamingScheme switch
{
EditorSettings.NamingScheme.SpaceParenthesis => $"{asset_path} ({index})",
EditorSettings.NamingScheme.Dot => $"{asset_path}.{index}",
_ => $"{asset_path}_{index}",
};
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using UnityEditor;
using UnityEngine;

namespace FishingCactus
{
public interface IPropertyContextMenuCallback
{
public void OnPropertyContextMenu( Rect context_menu_rect, GenericMenu menu, SerializedProperty property );
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2533bb1

Please sign in to comment.