added localization packages, generated meta, adresables

This commit is contained in:
2025-02-06 08:03:53 +01:00
parent 089626ad94
commit 543aaf71a6
171 changed files with 16888 additions and 832 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 175cafb710462ef489cbf56cb46c97d5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3459a30abd1a4e979488e906febc27e7
timeCreated: 1707714315

View File

@@ -0,0 +1,29 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fc91909e6444cb29aacb664f2098c7e, type: 3}
m_Name: OdinLocalizationConfig
m_EditorClassIdentifier:
recordUndosForAssetTableEntries: 1
assetRowHeight: 128
useSyntaxHighlighter: 1
placeholderColor:
lightColor: {r: 0.743147, g: 0.9433962, b: 0.9242815, a: 1}
darkColor: {r: 0, g: 0.5882353, b: 0.5333334, a: 1}
selectorColor:
lightColor: {r: 1, g: 0.7727525, b: 0.3632075, a: 1}
darkColor: {r: 1, g: 0.6470588, b: 0, a: 1}
formatterColor:
lightColor: {r: 0.9921569, g: 0.9855571, b: 0.8823529, a: 1}
darkColor: {r: 0.9607843, g: 0.9607843, b: 0.8627451, a: 1}
scrollSpeed: 24
invertMouseDragNavigation: 1
mouseDragSpeed: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c09625914af7d2d4e92c3653d5f15bd7
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,173 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationConfig.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
using Sirenix.Reflection.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Configs
{
[GlobalConfig("Plugins/Sirenix/Odin Inspector/Modules/Unity.Localization/Editor/Configs")]
public class OdinLocalizationConfig : GlobalConfig<OdinLocalizationConfig>
{
public class ThemeColorDrawer : OdinValueDrawer<ThemeColor>
{
protected override void Initialize()
{
base.Initialize();
this.Property.State.Expanded = false;
}
protected override void DrawPropertyLayout(GUIContent label)
{
SirenixEditorGUI.BeginBox(string.Empty);
{
SirenixEditorGUI.BeginBoxHeader();
{
GUILayout_Internal.BeginRow();
{
GUILayout_Internal.BeginColumn(LayoutSize.Pixels(EditorGUIUtility.labelWidth + 6.0f));
{
this.Property.State.Expanded = EditorGUILayout.Foldout(this.Property.State.Expanded,
$"{label.text} ({(EditorGUIUtility.isProSkin ? "Dark" : "Light")})",
true);
}
GUILayout_Internal.EndColumn();
GUILayout_Internal.BeginColumn(LayoutSize.Auto);
{
this.Property.Children[nameof(ThemeColor.Color)].Draw(null);
}
GUILayout_Internal.EndColumn();
}
GUILayout_Internal.EndRow();
}
SirenixEditorGUI.EndBoxHeader();
bool toggle = this.ValueEntry.ValueState != PropertyValueState.NullReference && this.Property.State.Expanded;
if (SirenixEditorGUI.BeginFadeGroup(this, toggle))
{
GUILayout.BeginHorizontal();
this.Property.Children[nameof(ThemeColor.lightColor)].Draw();
GUILayout.Space(3.5f);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
this.Property.Children[nameof(ThemeColor.darkColor)].Draw();
GUILayout.Space(3.5f);
GUILayout.EndHorizontal();
}
SirenixEditorGUI.EndFadeGroup();
}
SirenixEditorGUI.EndBox();
if (this.Property.State.Expanded)
{
GUILayout.Space(4.0f);
}
}
}
[Serializable]
public class ThemeColor
{
[ShowInInspector]
public Color Color
{
get => EditorGUIUtility.isProSkin ? this.darkColor : this.lightColor;
set
{
if (EditorGUIUtility.isProSkin)
{
this.darkColor = value;
}
else
{
this.lightColor = value;
}
}
}
public Color lightColor;
public Color darkColor;
public ThemeColor(Color lightColor, Color darkColor)
{
this.lightColor = lightColor;
this.darkColor = darkColor;
}
public static implicit operator Color(ThemeColor color) => color.Color;
}
[ShowInInspector]
[BoxGroup("User Interface")]
[Range(96, 1024)]
public int assetRowHeight = 128;
[BoxGroup("Syntax Highlighting")]
public bool useSyntaxHighlighter = true;
[EnableIf(nameof(useSyntaxHighlighter))]
[BoxGroup("Syntax Highlighting")]
public ThemeColor placeholderColor = new ThemeColor(new Color(0.743147f, 0.9433962f, 0.9242815f), new Color(0, 0.5882353f, 0.5333334f));
[EnableIf(nameof(useSyntaxHighlighter))]
[BoxGroup("Syntax Highlighting")]
public ThemeColor selectorColor = new ThemeColor(new Color(1.0f, 0.7727525f, 0.3632075f), new Color(1, 0.6470588f, 0));
[EnableIf(nameof(useSyntaxHighlighter))]
[BoxGroup("Syntax Highlighting")]
public ThemeColor formatterColor = new ThemeColor(new Color(0.9921569f, 0.9855571f, 0.8823529f), new Color(0.9607843f, 0.9607843f, 0.8627451f));
[BoxGroup("Navigation")]
[Range(1, 1000.0f)]
public float scrollSpeed = 24.0f;
[BoxGroup("Navigation")]
public bool invertMouseDragNavigation = true;
[BoxGroup("Navigation")]
[Range(0.5f, 5.0f)]
public float mouseDragSpeed = 1.0f;
[InfoBox("We couldn't find the necessary methods/classes to perform custom undo operations, therefore this option has been disabled and will be considered false even if true.",
VisibleIf = "@!OdinLocalizationReflectionValues.HasAPIForCustomUndo")]
[EnableIf("@OdinLocalizationReflectionValues.HasAPIForCustomUndo")]
[BoxGroup("Undo")]
public bool useCustomUndoHandlingForAssetCollections = true;
[Button(ButtonSizes.Large)]
public void Reset()
{
if (!EditorUtility.DisplayDialog("Odin Localization Config", "Are you certain you want to reset your Localization config?", "Yes", "No"))
{
return;
}
this.useCustomUndoHandlingForAssetCollections = OdinLocalizationReflectionValues.HasAPIForCustomUndo;
this.assetRowHeight = 128;
this.useSyntaxHighlighter = true;
this.placeholderColor = new ThemeColor(new Color(0.743147f, 0.9433962f, 0.9242815f), new Color(0, 0.5882353f, 0.5333334f));
this.selectorColor = new ThemeColor(new Color(1.0f, 0.7727525f, 0.3632075f), new Color(1, 0.6470588f, 0));
this.formatterColor = new ThemeColor(new Color(0.9921569f, 0.9855571f, 0.8823529f), new Color(0.9607843f, 0.9607843f, 0.8627451f));
this.scrollSpeed = 24.0f;
this.invertMouseDragNavigation = true;
this.mouseDragSpeed = 1.0f;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2fc91909e6444cb29aacb664f2098c7e
timeCreated: 1707714331

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bf930ee5d27d4f90ab84f23d337eaaca
timeCreated: 1704811167

View File

@@ -0,0 +1,993 @@
//-----------------------------------------------------------------------
// <copyright file="LocalizationMetadata.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEditor.Localization;
using UnityEngine;
using UnityEngine.Localization.Metadata;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class LocalizationMetadata
{
private object lastTarget;
public object Target;
private LocalizationTableCollection localizationTableCollection;
private SearchField searchField = new SearchField();
private string searchTerm;
private Vector2 scrollPosition;
private IMetadata toBeAdded;
private IMetadata toBeRemoved;
private Type lastMetadataTypeUnderCursor;
private StringBuilder stringBuilder = new StringBuilder();
private OdinLocalizationEditorWindow.WindowState windowState;
private List<Type> tags = new List<Type>
{
typeof(ExcludeEntryFromExport)
};
private GUIStyle _contentPadding;
private GUIStyle ContentPadding =>
this._contentPadding = this._contentPadding ?? new GUIStyle
{
padding = new RectOffset(10, 10, 10, 10)
};
private GUIStyle _platformOverridePadding;
private GUIStyle PlatformOverridePadding =>
this._platformOverridePadding = this._platformOverridePadding ?? new GUIStyle
{
padding = new RectOffset(10, 20, 10, 10)
};
public LocalizationMetadata(LocalizationTableCollection localizationTableCollection, OdinLocalizationEditorWindow.WindowState windowState)
{
this.localizationTableCollection = localizationTableCollection;
this.windowState = windowState;
}
/// <summary>
/// Adds the given metadata to the shared data of the <see cref="LocalizationTableCollection"/>.
/// </summary>
public void Add(IMetadata metadata)
{
this.localizationTableCollection.SharedData.Metadata.AddMetadata(metadata);
}
/// <summary>
/// Adds the given metadata to the given <see cref="LocalizationTable"/>.
/// </summary>
public void Add(LocalizationTable localizationTable, IMetadata metadata)
{
localizationTable.AddMetadata(metadata);
}
/// <summary>
/// Adds the given metadata to the given <see cref="SharedTableData"/>.
/// </summary>
public void Add(SharedTableData.SharedTableEntry sharedTableEntry, IMetadata metadata)
{
sharedTableEntry.Metadata.AddMetadata(metadata);
}
/// <summary>
/// Adds the given metadata to the given <see cref="TableEntry"/>.
/// </summary>
public void Add(TableEntry tableEntry, IMetadata metadata)
{
tableEntry.AddMetadata(metadata);
}
/// <summary>
/// Removes the given metadata from the shared data of the <see cref="LocalizationTableCollection"/>.
/// </summary>
public void Remove(IMetadata metadata)
{
this.localizationTableCollection.SharedData.Metadata.RemoveMetadata(metadata);
}
/// <summary>
/// Removes the given metadata from the given <see cref="LocalizationTable"/>.
/// </summary>
public void Remove(LocalizationTable localizationTable, IMetadata metadata)
{
localizationTable.RemoveMetadata(metadata);
}
/// <summary>
/// Removes the given metadata from the given <see cref="SharedTableData"/>.
/// </summary>
public void Remove(SharedTableData.SharedTableEntry sharedTableEntry, IMetadata metadata)
{
sharedTableEntry.Metadata.RemoveMetadata(metadata);
}
/// <summary>
/// Removes the given metadata from the given <see cref="TableEntry"/>.
/// </summary>
public void Remove(TableEntry tableEntry, IMetadata metadata)
{
tableEntry.RemoveMetadata(metadata);
}
/// <summary>
/// Draws the metadata for the given <see cref="LocalizationTableCollection"/>
/// </summary>
public float Draw(Rect rect)
{
var minWidth = 0f;
if (this.Target == null) return minWidth;
if (Event.current.type != EventType.Repaint)
{
if (this.toBeAdded != null)
{
switch (this.Target)
{
case LocalizationTable localizationTable:
Undo.RecordObject(this.localizationTableCollection, "Localization Tables Metadata Added");
this.Add(localizationTable, this.toBeAdded);
break;
case SharedTableData.SharedTableEntry sharedTableEntry:
Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Added");
this.Add(sharedTableEntry, this.toBeAdded);
break;
case TableEntry tableEntry:
Undo.RecordObject(tableEntry.Table, "Localization Tables Metadata Added");
this.Add(tableEntry, this.toBeAdded);
break;
default:
Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Added");
this.Add(this.toBeAdded);
break;
}
this.toBeAdded = null;
this.windowState.Dispose();
}
if (this.toBeRemoved != null)
{
switch (this.Target)
{
case LocalizationTable localizationTable:
Undo.RecordObject(this.localizationTableCollection, "Localization Tables Metadata Removed");
this.Remove(localizationTable, this.toBeRemoved);
break;
case SharedTableData.SharedTableEntry sharedTableEntry:
Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Removed");
this.Remove(sharedTableEntry, this.toBeRemoved);
break;
case TableEntry tableEntry:
Undo.RecordObject(tableEntry.Table, "Localization Tables Metadata Removed");
this.Remove(tableEntry, this.toBeRemoved);
break;
default:
Undo.RecordObject(this.localizationTableCollection.SharedData, "Localization Tables Metadata Removed");
this.Remove(this.toBeRemoved);
break;
}
this.toBeRemoved = null;
this.windowState.Dispose();
}
}
if (this.windowState.MetadataTree == null || this.lastTarget != this.Target)
{
this.windowState.MetadataTree?.Dispose();
switch (this.Target)
{
case LocalizationTable localizationTable:
this.windowState.MetadataTree = PropertyTree.Create(localizationTable);
break;
case SharedTableData.SharedTableEntry sharedTableEntry:
this.windowState.MetadataTree = PropertyTree.Create(sharedTableEntry);
break;
case TableEntry tableEntry:
{
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
var entryData = typeof(TableEntry).GetProperty("Data", bindingFlags).GetValue(tableEntry);
this.windowState.MetadataTree = PropertyTree.Create(entryData);
break;
}
case LocalizationTableCollection localizationTableCollection:
this.windowState.MetadataTree = PropertyTree.Create(localizationTableCollection.SharedData);
break;
}
this.lastTarget = this.Target;
}
var metadataCollection = this.windowState.MetadataTree.RootProperty.Children["m_Metadata"];
var metadataEntries = metadataCollection.Children["m_Items"].Children.ToList();
metadataEntries = metadataEntries
.Where(entry => !entry.Attributes.HasAttribute<HideInInspector>())
.OrderBy(entry => !this.tags.Contains(entry.ValueEntry.TypeOfValue))
.ThenBy(entry => entry.Name)
.ToList();
this.windowState.MetadataTree.BeginDraw(false);
switch (this.Target)
{
case LocalizationTable localizationTable:
minWidth = this.Draw(rect, localizationTable, "Table", metadataEntries, m => this.Add(localizationTable, m), m => this.Remove(localizationTable, m), () => this.OpenMetadataSelector(localizationTable));
break;
case SharedTableData.SharedTableEntry sharedTableEntry:
minWidth = this.Draw(rect, this.localizationTableCollection.SharedData, "Shared Entry", metadataEntries, m => this.Add(sharedTableEntry, m), m => this.Remove(sharedTableEntry, m), () => this.OpenMetadataSelector(sharedTableEntry));
break;
case TableEntry tableEntry:
minWidth = this.Draw(rect, tableEntry.Table, "Entry", metadataEntries, m => this.Add(tableEntry, m), m => this.Remove(tableEntry, m), () => this.OpenMetadataSelector(tableEntry));
break;
default:
minWidth = this.Draw(rect, this.localizationTableCollection.SharedData, "Shared Table", metadataEntries, m => this.Add(m), m => this.Remove(m), () => this.OpenMetadataSelector(this.localizationTableCollection));
break;
}
this.windowState.MetadataTree.EndDraw();
return minWidth;
}
private float Draw(Rect rect, UnityEngine.Object target, string title, List<InspectorProperty> metadataEntries, Action<IMetadata> add, Action<IMetadata> remove, Action openMetadataSelector)
{
const int headerHeight = 22;
const int addButtonAreaHeight = 40;
EditorGUIUtility.labelWidth = 0f;
// We only want to process events if the user's mouse is currently
// in the metadata panel so we grab some information for later.
var metadataPanelHasFocus = Event.current.IsMouseOver(rect);
var holdingControl = Event.current.control && metadataPanelHasFocus;
var holdingAlt = Event.current.alt && metadataPanelHasFocus;
if (Event.current.OnKeyUp(KeyCode.LeftAlt))
{
this.lastMetadataTypeUnderCursor = null;
}
var searchAreaRect = rect.TakeFromTop(32);
EditorGUI.DrawRect(searchAreaRect.AlignBottom(1), SirenixGUIStyles.BorderColor);
// Draw search field.
if (searchAreaRect.width > 125)
{
var titleWidth = SirenixGUIStyles.LabelCentered.CalcWidth(title) + 13;
var titleRect = searchAreaRect.AlignRight(titleWidth);
EditorGUI.DrawRect(titleRect.TakeFromLeft(1), SirenixGUIStyles.BorderColor);
this.searchTerm = this.searchField.Draw(searchAreaRect.SubXMax(titleWidth).Padding(6), this.searchTerm,
"Search for metadata...");
GUI.Label(titleRect, title, SirenixGUIStyles.LabelCentered);
}
else
{
GUI.Label(searchAreaRect, title, SirenixGUIStyles.LabelCentered);
}
var filteredMetadataEntries = string.IsNullOrWhiteSpace(this.searchTerm)
? metadataEntries.ToList()
: metadataEntries.Where(metadataEntry =>
{
if (FuzzySearch.Contains(this.searchTerm, metadataEntry.Info.TypeOfOwner.Name))
{
return true;
}
if (FuzzySearch.Contains(this.searchTerm, metadataEntry.ValueEntry?.WeakSmartValue?.ToString()))
{
return true;
}
foreach (var child in metadataEntry.Children.Recurse())
{
if (FuzzySearch.Contains(this.searchTerm, child.Name))
{
return true;
}
if (FuzzySearch.Contains(this.searchTerm, child.ValueEntry?.WeakSmartValue?.ToString()))
{
return true;
}
}
return false;
}).ToList();
var metadataViewHeight = rect.height - addButtonAreaHeight;
GUILayout.BeginArea(rect.AlignTop(metadataViewHeight));
this.scrollPosition = EditorGUILayout.BeginScrollView(this.scrollPosition, GUIStyle.none, GUI.skin.verticalScrollbar);
GUIHelper.BeginLayoutMeasuring();
{
for (var i = 0; i < filteredMetadataEntries.Count; i++)
{
var metadataEntry = filteredMetadataEntries[i];
var metadataHeaderRect = GUILayoutUtility.GetRect(0, headerHeight, GUILayoutOptions.ExpandWidth().ExpandHeight(false));
var unchangedMetadataHeaderRect = metadataHeaderRect;
var genericMenu = new GenericMenu();
genericMenu.AddItem(new GUIContent("Delete"), false, () =>
{
var metadata = (IMetadata)metadataEntry.ValueEntry.WeakSmartValue;
this.toBeRemoved = metadata;
});
var isTag = this.tags.Contains(metadataEntry.ValueEntry.TypeOfValue);
if (!isTag)
{
genericMenu.AddItem(new GUIContent("Copy"), false, () =>
{
var metadata = (IMetadata)metadataEntry.ValueEntry.WeakSmartValue;
Clipboard.Copy(metadata);
});
if (Clipboard.CanPaste<IMetadata>())
genericMenu.AddItem(new GUIContent("Paste"), false, () =>
{
var metadata = (IMetadata)Clipboard.Paste();
this.toBeAdded = metadata;
});
else
genericMenu.AddDisabledItem(new GUIContent("Paste"));
genericMenu.AddSeparator("");
genericMenu.AddItem(new GUIContent($"{(metadataEntry.State.Expanded ? "Collapse" : "Expand")} All (Ctrl-Click)"), false, () =>
{
var newState = !metadataEntry.State.Expanded;
foreach (var entry in filteredMetadataEntries) entry.State.Expanded = newState;
});
genericMenu.AddItem(new GUIContent($"{(metadataEntry.State.Expanded ? "Collapse" : "Expand")} All Of Same Type (Alt-Click)"), false, () =>
{
var newState = !metadataEntry.State.Expanded;
foreach (var entry in filteredMetadataEntries.Where(m => m.Info.TypeOfOwner == metadataEntry.Info.TypeOfOwner)) entry.State.Expanded = newState;
});
}
Color metadataHeaderBackground = new Color(0.243f, 0.243f, 0.243f);
if (!isTag && holdingControl)
{
metadataHeaderBackground = new Color(0.278f, 0.278f, 0.278f);
GUIHelper.RequestRepaint();
}
else if (!isTag && holdingAlt)
{
if (Event.current.IsMouseOver(unchangedMetadataHeaderRect))
{
this.lastMetadataTypeUnderCursor = metadataEntry.ValueEntry.TypeOfValue;
GUIHelper.RequestRepaint();
}
metadataHeaderBackground = this.lastMetadataTypeUnderCursor == metadataEntry.ValueEntry.TypeOfValue
? new Color(0.278f, 0.278f, 0.278f)
: new Color(0.243f, 0.243f, 0.243f);
}
if (Event.current.IsMouseOver(unchangedMetadataHeaderRect))
{
metadataHeaderBackground = new Color(0.278f, 0.278f, 0.278f);
}
EditorGUI.DrawRect(unchangedMetadataHeaderRect, metadataHeaderBackground);
if (i != 0)
{
EditorGUI.DrawRect(unchangedMetadataHeaderRect.AlignTop(1), new Color(0.102f, 0.102f, 0.102f));
}
var foldoutTriangleRect = metadataHeaderRect.TakeFromLeft(headerHeight);
if (!isTag)
SdfIcons.DrawIcon(
foldoutTriangleRect.Padding(6),
metadataEntry.State.Expanded ? SdfIconType.CaretDownFill : SdfIconType.CaretRightFill,
new Color(0.443f, 0.443f, 0.443f));
var iconRect = metadataHeaderRect.TakeFromLeft(headerHeight);
SdfIcons.DrawIcon(
iconRect.Padding(0, 4, 4, 4),
isTag ? SdfIconType.TagFill : SdfIconType.GeoAltFill,
new Color(0.933f, 0.933f, 0.933f));
var contextMenuIconRect = metadataHeaderRect.TakeFromRight(headerHeight);
SdfIcons.DrawIcon(contextMenuIconRect.Padding(7), SdfIconType.ThreeDotsVertical);
var niceName = ObjectNames.NicifyVariableName(metadataEntry.ValueEntry.TypeOfValue.GetNiceName());
if (metadataEntry.Info.TypeOfOwner == typeof(Comment))
{
var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1;
var comment = ((string)metadataEntry.ValueEntry.WeakSmartValue).Split('\n')[0];
var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10);
if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(comment) > hintRect.width)
{
if (hintRect.width < 30)
{
comment = "";
}
else
{
GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
}
GUI.Label(hintRect, comment, SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
else if (metadataEntry.Info.TypeOfOwner == typeof(PreloadAssetTableMetadata))
{
var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1;
var preloadAssetLabel = ObjectNames.NicifyVariableName(metadataEntry.ValueEntry.WeakSmartValue.ToString());
var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10);
if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(preloadAssetLabel) > hintRect.width)
{
if (hintRect.width < 30)
{
preloadAssetLabel = "";
}
else
{
GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
}
GUI.Label(hintRect, preloadAssetLabel, SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
else if (metadataEntry.Info.TypeOfOwner == typeof(PlatformOverride))
{
var platformOverrides = metadataEntry.Children;
var headerLabelWidth = EditorStyles.label.CalcWidth(niceName) + 1;
var hintRect = metadataHeaderRect.AlignRight(metadataHeaderRect.width - headerLabelWidth - 10);
this.stringBuilder.Clear();
for (var j = 0; j < platformOverrides.Count; j++)
{
var platformOverrideInspectorProperty = platformOverrides[j];
var platform = (RuntimePlatform)platformOverrideInspectorProperty.Children[0].ValueEntry.WeakSmartValue;
this.stringBuilder.Append(platform);
if (j != platformOverrides.Count - 1)
{
this.stringBuilder.Append(", ");
}
}
var platforms = this.stringBuilder.ToString();
if (SirenixGUIStyles.RightAlignedGreyMiniLabel.CalcWidth(platforms) > hintRect.width)
{
if (hintRect.width < 30)
{
GUI.Label(hintRect, $"[{platformOverrides.Count}]", SirenixGUIStyles.RightAlignedGreyMiniLabel);
platforms = "";
}
else if (platformOverrides.Count > 1)
{
GUI.Label(hintRect.TakeFromLeft(11), "...", SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
}
GUI.Label(hintRect, platforms, SirenixGUIStyles.RightAlignedGreyMiniLabel);
}
GUI.Label(metadataHeaderRect, niceName);
var evt = Event.current;
if (evt.OnMouseDown(contextMenuIconRect, 0, false))
genericMenu.ShowAsContext();
else if (evt.OnMouseDown(unchangedMetadataHeaderRect, 1, false)) genericMenu.ShowAsContext();
if (metadataEntry.ValueEntry.TypeOfValue == typeof(ExcludeEntryFromExport)) continue;
if (evt.OnMouseDown(unchangedMetadataHeaderRect, 0, false))
{
if (holdingControl)
{
var newState = !metadataEntry.State.Expanded;
foreach (var entry in filteredMetadataEntries)
{
entry.State.Expanded = newState;
}
}
else if (holdingAlt)
{
var newState = !metadataEntry.State.Expanded;
foreach (var entry in filteredMetadataEntries.Where(m => m.ValueEntry.TypeOfValue == this.lastMetadataTypeUnderCursor))
{
entry.State.Expanded = newState;
}
}
else
{
metadataEntry.State.Expanded = !metadataEntry.State.Expanded;
}
GUIHelper.RemoveFocusControl();
}
if (SirenixEditorGUI.BeginFadeGroup(metadataEntry, metadataEntry.State.Expanded))
{
EditorGUI.DrawRect(unchangedMetadataHeaderRect.AlignBottom(1),
new Color(0.188f, 0.188f, 0.188f));
var contentRect = EditorGUILayout.BeginVertical();
if (metadataEntry.ValueEntry.TypeOfValue == typeof(Comment))
{
EditorGUILayout.BeginVertical(this.ContentPadding);
foreach (var child in metadataEntry.Children)
{
child.Draw(null);
}
EditorGUILayout.EndVertical();
}
else if (metadataEntry.ValueEntry.TypeOfValue == typeof(PreloadAssetTableMetadata))
{
EditorGUILayout.BeginVertical(this.ContentPadding);
var preloadAssetTableMetadata = (PreloadAssetTableMetadata)metadataEntry.ValueEntry.WeakSmartValue;
var btnRect = EditorGUILayout.GetControlRect(false);
var leftRect = btnRect.Split(0, 2);
var mouseOverLeft = Event.current.IsMouseOver(leftRect);
var leftActive = mouseOverLeft && Event.current.type == EventType.MouseDown && Event.current.button == 0;
var leftOn = preloadAssetTableMetadata.Behaviour == PreloadAssetTableMetadata.PreloadBehaviour.NoPreload;
var rightRect = btnRect.Split(1, 2);
var mouseOverRight = Event.current.IsMouseOver(rightRect);
var rightActive = mouseOverRight && Event.current.type == EventType.MouseDown && Event.current.button == 0;
var rightOn = preloadAssetTableMetadata.Behaviour == PreloadAssetTableMetadata.PreloadBehaviour.PreloadAll;
if (Event.current.type == EventType.Repaint)
{
SirenixGUIStyles.ButtonLeft.Draw(leftRect, "No Preload", mouseOverLeft, leftActive, leftOn, false);
SirenixGUIStyles.ButtonRight.Draw(rightRect, "Preload All", mouseOverRight, rightActive, rightOn, false);
}
if (Event.current.OnMouseDown(leftRect, 0, false))
{
preloadAssetTableMetadata.Behaviour = PreloadAssetTableMetadata.PreloadBehaviour.NoPreload;
}
else if (Event.current.OnMouseDown(rightRect, 0, false))
{
preloadAssetTableMetadata.Behaviour = PreloadAssetTableMetadata.PreloadBehaviour.PreloadAll;
}
EditorGUILayout.EndVertical();
}
else if (metadataEntry.ValueEntry.TypeOfValue == typeof(PlatformOverride))
{
this.DrawPlatformOverride(metadataEntry);
}
else
{
EditorGUILayout.BeginVertical(this.ContentPadding);
foreach (var child in metadataEntry.Children)
{
child.Draw();
}
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndVertical();
}
SirenixEditorGUI.EndFadeGroup();
}
}
var measureRect = GUIHelper.EndLayoutMeasuring();
EditorGUILayout.EndScrollView();
GUILayout.EndArea();
var pinAddMetadataButton = measureRect.height > metadataViewHeight;
rect.TakeFromTop(pinAddMetadataButton ? metadataViewHeight : measureRect.height);
if (metadataEntries.Count > 1)
{
EditorGUI.DrawRect(rect.TakeFromTop(1), SirenixGUIStyles.BorderColor);
}
var addMetadataRect = rect.AlignTop(addButtonAreaHeight).AlignCenter(Mathf.Min(200, rect.width - 16)).VerticalPadding(8);
if (GUI.Button(addMetadataRect, "Add Metadata"))
{
openMetadataSelector();
}
// Draw the shadow that appears under the search bar while scrolling through the metadata.
GUI.DrawTexture(
searchAreaRect.AlignBottom(10).AddY(10),
OdinLocalizationGUITextures.TopToBottomFade,
ScaleMode.StretchToFill,
true,
1.0f,
new Color(0, 0, 0, Mathf.InverseLerp(0f, 30f, this.scrollPosition.y) * 0.6f),
0,
0);
if (measureRect.height > metadataViewHeight)
{
var pos = this.scrollPosition.y + metadataViewHeight;
// Draw the shadow that appears at the bottom of the metadata while scrolling through the metadata
GUI.DrawTexture(
rect.AlignTop(10).SubY(10),
OdinLocalizationGUITextures.BottomToTopFade,
ScaleMode.StretchToFill,
true,
1.0f,
new Color(0, 0, 0, Mathf.InverseLerp(measureRect.height, measureRect.height - 30, pos) * 0.6f),
0,
0);
}
return measureRect.width;
}
private void DrawPlatformOverride(InspectorProperty metadataEntry)
{
var platformOverride = (PlatformOverride)metadataEntry.ValueEntry.WeakSmartValue;
var platformOverrideDatas = (IList)metadataEntry.Children["m_PlatformOverrides"]?.ValueEntry.WeakSmartValue;
var platformOverrideDataInspectorProperties = metadataEntry.Children["m_PlatformOverrides"]?.Children;
if (platformOverrideDatas == null)
{
return;
}
if (platformOverrideDatas.Count == 0)
{
platformOverride.AddPlatformOverride(RuntimePlatform.WindowsPlayer, null, null, EntryOverrideType.None);
}
for (var i = 0; i < platformOverrideDataInspectorProperties.Count; i++)
{
var platformOverrideDataInspectorProperty = platformOverrideDataInspectorProperties[i];
var backgroundRect = EditorGUILayout.BeginVertical(this.PlatformOverridePadding);
var backgroundColor = i % 2 == 0 ? SirenixGUIStyles.ListItemColorEven : SirenixGUIStyles.ListItemColorOdd;
EditorGUI.DrawRect(backgroundRect, backgroundColor);
platformOverrideDataInspectorProperty.Children["platform"].Draw();
platformOverrideDataInspectorProperty.Children["entryOverrideType"].Draw();
var entryOverrideType = (EntryOverrideType)platformOverrideDataInspectorProperty.Children
.FirstOrDefault(c => c.Info.TypeOfValue == typeof(EntryOverrideType))?.ValueEntry.WeakSmartValue;
var entryOverrideRect = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight, GUILayoutOptions.ExpandWidth(true).ExpandHeight(false));
switch (entryOverrideType)
{
case EntryOverrideType.Table:
{
var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference))?.ValueEntry.WeakSmartValue;
this.DoTableGUI(entryOverrideRect, tableReference, platformOverrideDataInspectorProperty);
break;
}
case EntryOverrideType.Entry:
{
var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference))
?.ValueEntry.WeakSmartValue;
var tableEntryReference = (TableEntryReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableEntryReference))?.ValueEntry.WeakSmartValue;
this.DoTableAndEntryGUI(entryOverrideRect, tableReference, tableEntryReference, platformOverrideDataInspectorProperty, false);
break;
}
case EntryOverrideType.TableAndEntry:
{
var tableReference = (TableReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableReference))?.ValueEntry.WeakSmartValue;
var tableEntryReference = (TableEntryReference)platformOverrideDataInspectorProperty.Children.FirstOrDefault(c => c.Info.TypeOfValue == typeof(TableEntryReference))?.ValueEntry.WeakSmartValue;
this.DoTableAndEntryGUI(entryOverrideRect, tableReference, tableEntryReference, platformOverrideDataInspectorProperty, true);
break;
}
}
EditorGUILayout.EndVertical();
var xIconRect = backgroundRect.AlignRight(10).SubX(4).AlignCenterY(10);
var mouseOverX = Event.current.IsMouseOver(xIconRect);
SdfIcons.DrawIcon(xIconRect, SdfIconType.X, mouseOverX ? Color.white : EditorStyles.label.normal.textColor);
if (Event.current.OnMouseDown(xIconRect, 0, false))
{
var platform = (RuntimePlatform)platformOverrideDataInspectorProperty.Children[0].ValueEntry.WeakSmartValue;
platformOverride.RemovePlatformOverride(platform);
}
}
var addButtonRect = GUILayoutUtility.GetRect(0, 40, GUILayoutOptions.ExpandWidth().ExpandHeight(false));
EditorGUI.DrawRect(addButtonRect.AlignTop(1), new Color(0.188f, 0.188f, 0.188f));
if (GUI.Button(addButtonRect.Padding(8), "Add Platform Override"))
{
var selector = new EnumSelector<RuntimePlatform>();
selector.SelectionConfirmed += platforms =>
{
var platform = platforms.FirstOrDefault();
platformOverride.AddPlatformOverride(platform, null, null, EntryOverrideType.None);
};
selector.ShowInPopup();
}
}
private void OpenMetadataSelector(LocalizationTableCollection localizationTableCollection)
{
this.OpenMetadataSelector(localizationTableCollection.SharedData.Metadata.MetadataEntries, MetadataType.SharedTableData);
}
private void OpenMetadataSelector(LocalizationTable localizationTable)
{
switch (localizationTable)
{
case StringTable _:
this.OpenMetadataSelector(localizationTable.MetadataEntries, MetadataType.StringTable);
break;
case AssetTable _:
this.OpenMetadataSelector(localizationTable.MetadataEntries, MetadataType.AssetTable);
break;
}
}
private void OpenMetadataSelector(SharedTableData.SharedTableEntry sharedTableEntry)
{
switch (this.localizationTableCollection)
{
case StringTableCollection _:
this.OpenMetadataSelector(sharedTableEntry.Metadata.MetadataEntries, MetadataType.StringTableEntry, MetadataType.SharedStringTableEntry);
break;
case AssetTableCollection _:
this.OpenMetadataSelector(sharedTableEntry.Metadata.MetadataEntries, MetadataType.AssetTableEntry, MetadataType.SharedAssetTableEntry);
break;
}
}
private void OpenMetadataSelector(TableEntry tableEntry)
{
switch (tableEntry)
{
case StringTableEntry _:
this.OpenMetadataSelector(tableEntry.MetadataEntries, MetadataType.StringTableEntry);
break;
case AssetTableEntry _:
this.OpenMetadataSelector(tableEntry.MetadataEntries, MetadataType.AssetTableEntry);
break;
}
}
private void OpenMetadataSelector(IList<IMetadata> existingMetadata, params MetadataType[] allowedTypes)
{
var existingMetadataTypes = existingMetadata.Select(m => m?.GetType()).ToList();
var metadataTypes = TypeCache.GetTypesDerivedFrom<IMetadata>();
var validMetadataTypes = new List<Type>();
foreach (var metadataType in metadataTypes)
{
var metadataAttribute = metadataType.GetCustomAttribute<MetadataAttribute>();
if (metadataAttribute != null && allowedTypes.Any(allowedType => metadataAttribute.AllowedTypes.HasFlag(allowedType)))
{
if (existingMetadataTypes.Contains(metadataType) && !OdinLocalizationMetadataRegistry.MetadataAllowsMultiple.ContainsKey(metadataType)) continue;
validMetadataTypes.Add(metadataType);
}
}
var selector = new GenericSelector<Type>("", validMetadataTypes, false, type => ObjectNames.NicifyVariableName(type.Name));
foreach (var item in selector.SelectionTree.MenuItems)
{
item.SdfIcon = SdfIconType.Braces;
}
selector.SelectionConfirmed += types =>
{
var selectedType = types.FirstOrDefault();
if (selectedType == null)
{
return;
}
var metadata = (IMetadata)Activator.CreateInstance(selectedType);
this.toBeAdded = metadata;
};
selector.EnableSingleClickToSelect();
selector.ShowInPopup();
}
public class TableReferenceSelector : OdinSelector<LocalizationTableCollection>
{
private Type tableType;
public TableReferenceSelector(Type tableType)
{
this.tableType = tableType;
}
protected override void BuildSelectionTree(OdinMenuTree tree)
{
tree.Config.SelectMenuItemsOnMouseDown = true;
tree.Selection.SupportsMultiSelect = false;
var collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}");
foreach (var guid in collectionGUIDs)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var collection = AssetDatabase.LoadAssetAtPath<LocalizationTableCollection>(path);
if (collection is null) continue;
if (collection.Tables.Count < 1) continue;
if (collection.Tables[0].asset.GetType() != this.tableType) continue;
var collectionItem = new OdinMenuItem(tree, collection.name, collection)
{
SdfIcon = SdfIconType.Table
};
tree.MenuItems.Add(collectionItem);
}
}
}
public class TableEntryReferenceSelector : OdinSelector<TableEntryReferenceSelector.TableEntry>
{
private Type tableType;
public struct TableEntry
{
public LocalizationTableCollection Collection;
public SharedTableData.SharedTableEntry SharedEntry;
}
public TableEntryReferenceSelector(Type tableType)
{
this.tableType = tableType;
}
protected override void BuildSelectionTree(OdinMenuTree tree)
{
tree.Config.SelectMenuItemsOnMouseDown = true;
tree.Selection.SupportsMultiSelect = false;
var collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}");
foreach (var guid in collectionGUIDs)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var collection = AssetDatabase.LoadAssetAtPath<LocalizationTableCollection>(path);
if (collection is null) continue;
if (collection.Tables.Count < 1) continue;
if (collection.Tables[0].asset.GetType() != this.tableType) continue;
var collectionItem = new OdinMenuItem(tree, collection.name, collection)
{
SdfIcon = SdfIconType.Table
};
List<SharedTableData.SharedTableEntry> entries = collection.SharedData.Entries;
for (var j = 0; j < entries.Count; j++)
{
SharedTableData.SharedTableEntry entry = entries[j];
var tableEntry = new TableEntry {Collection = collection, SharedEntry = entry};
var entryItem = new OdinMenuItem(tree, entry.Key, tableEntry)
{
SdfIcon = SdfIconType.KeyFill
};
collectionItem.ChildMenuItems.Add(entryItem);
}
collectionItem.IsSelectable = false;
tree.MenuItems.Add(collectionItem);
}
}
}
private void DoTableGUI(Rect rect, TableReference tableReference, InspectorProperty platformOverrideData)
{
var dropDownPosition = EditorGUI.PrefixLabel(rect, new GUIContent("Table Collection"));
var label = tableReference.TableCollectionName;
if (EditorGUI.DropdownButton(dropDownPosition, new GUIContent(label), FocusType.Passive))
{
TableReferenceSelector tableReferenceSelector;
tableReferenceSelector = this.localizationTableCollection.GetType() == typeof(AssetTableCollection)
? new TableReferenceSelector(typeof(AssetTable))
: new TableReferenceSelector(typeof(StringTable));
tableReferenceSelector.GetCurrentSelection();
tableReferenceSelector.SelectionConfirmed += selections =>
{
var selection = selections.FirstOrDefault();
var platformOverrideType = typeof(PlatformOverride);
var platformOverrideDataType = platformOverrideType.GetNestedType("PlatformOverrideData", BindingFlags.NonPublic);
platformOverrideDataType
.GetField("tableReference")
.SetValue(platformOverrideData.ValueEntry.WeakSmartValue, selection.TableCollectionNameReference);
platformOverrideDataType
.GetField("tableEntryReference")
.SetValue(platformOverrideData.ValueEntry.WeakSmartValue, (TableEntryReference) string.Empty);
};
tableReferenceSelector.ShowInPopup();
}
}
private void DoTableAndEntryGUI(Rect rect,
TableReference tableReference,
TableEntryReference tableEntryReference,
InspectorProperty platformOverrideData,
bool displayTableName)
{
var dropDownPosition = EditorGUI.PrefixLabel(rect, new GUIContent("Reference"));
var entryLabel = tableEntryReference.ReferenceType != TableEntryReference.Type.Empty
? tableEntryReference.ResolveKeyName(this.localizationTableCollection.SharedData)
: null;
string label;
if (displayTableName)
{
var tableLabel = tableReference.TableCollectionName ?? "";
label = string.IsNullOrEmpty(tableLabel) || string.IsNullOrEmpty(entryLabel) ? "None" : $"{tableLabel}/{entryLabel}";
}
else
{
label = string.IsNullOrWhiteSpace(entryLabel) ? "None" : $"{entryLabel}";
}
if (EditorGUI.DropdownButton(dropDownPosition, new GUIContent(label), FocusType.Passive))
{
Type tableType = this.localizationTableCollection.GetType() == typeof(AssetTableCollection)
? typeof(AssetTable)
: typeof(StringTable);
TableEntryReferenceSelector tableEntryReferenceSelector = new TableEntryReferenceSelector(tableType);
tableEntryReferenceSelector.SelectionConfirmed += selections =>
{
var selection = selections.FirstOrDefault();
var platformOverrideType = typeof(PlatformOverride);
var platformOverrideDataType = platformOverrideType.GetNestedType("PlatformOverrideData", BindingFlags.NonPublic);
platformOverrideDataType
.GetField("tableReference")
.SetValue(platformOverrideData.ValueEntry.WeakSmartValue, selection.Collection.TableCollectionNameReference);
platformOverrideDataType
.GetField("tableEntryReference")
.SetValue(platformOverrideData.ValueEntry.WeakSmartValue, (TableEntryReference) selection.SharedEntry.Key);
};
tableEntryReferenceSelector.ShowInPopup();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1490cea564e237341b75e0cdba1f374a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc0d9dcd4d4f4e3094085c752e185ef8
timeCreated: 1704811181

View File

@@ -0,0 +1,196 @@
//-----------------------------------------------------------------------
// <copyright file="OdinGUITable.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#define USING_WIDTH_NON_PERCENT
//#undef USING_WIDTH_NON_PERCENT
using System;
using Sirenix.OdinInspector.Editor.Internal;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Tables;
using UnityEngine.Serialization;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class OdinGUITable<TTable> : IComparable<OdinGUITable<TTable>> where TTable : LocalizationTable
{
public const string KEY_DISPLAY_NAME = "Key";
public enum GUITableType
{
Default,
Key
}
public float Width
{
get => this._width;
set
{
if (value < OdinLocalizationConstants.MIN_COLUMN_WIDTH)
{
this._width = OdinLocalizationConstants.MIN_COLUMN_WIDTH;
return;
}
this._width = Mathf.Round(value);
}
}
public bool IsDraggingSlider = false;
public bool IsVisible;
public string DisplayName;
public GUITableType Type;
[FormerlySerializedAs("Table")]
public TTable Asset;
public bool IsPinned;
private float _width;
#if USING_WIDTH_NON_PERCENT
//public float VisualWidth;
#else
internal float WidthPercentage;
#endif
#if USING_WIDTH_NON_PERCENT
public static OdinGUITable<TTable> CreateKeyTable()
#else
public static OdinGUITable<TTable> CreateKeyTable(float widthPercent)
#endif
{
return new OdinGUITable<TTable>
{
IsVisible = true,
DisplayName = KEY_DISPLAY_NAME,
Type = GUITableType.Key,
Asset = null,
IsPinned = false,
Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH,
#if USING_WIDTH_NON_PERCENT
// VisualWidth = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH
#else
WidthPercentage = widthPercent
#endif
};
}
#if USING_WIDTH_NON_PERCENT
public static OdinGUITable<TTable> CreateTable(TTable table, Locale locale)
#else
public static OdinGUITable<TTable> CreateTable(TTable table, float widthPercent)
#endif
{
return new OdinGUITable<TTable>
{
IsVisible = true,
DisplayName = locale.LocaleName,
Type = GUITableType.Default,
Asset = table,
IsPinned = false,
Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH,
#if USING_WIDTH_NON_PERCENT
// VisualWidth = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH
#else
WidthPercentage = widthPercent
#endif
};
}
public int CompareTo(OdinGUITable<TTable> other)
{
if (other == null)
{
return -1;
}
if (this.IsVisible && !other.IsVisible)
{
return -1;
}
if (!this.IsVisible && other.IsVisible)
{
return 1;
}
if (this.IsPinned && !other.IsPinned)
{
return -1;
}
if (!this.IsPinned && other.IsPinned)
{
return 1;
}
if (this.DisplayName == KEY_DISPLAY_NAME && other.DisplayName == KEY_DISPLAY_NAME)
{
return 0;
}
if (other.DisplayName == KEY_DISPLAY_NAME)
{
return 1;
}
if (this.DisplayName == KEY_DISPLAY_NAME)
{
return -1;
}
return this.Asset.LocaleIdentifier.CompareTo(other.Asset.LocaleIdentifier);
}
public Vector2 HandleSlider(Rect position)
{
if (GUIUtility.hotControl == 0)
{
this.IsDraggingSlider = false;
}
if (!GUI.enabled)
{
return Vector2.zero;
}
EditorGUIUtility.AddCursorRect(position, MouseCursor.ResizeHorizontal);
if (GUI.enabled && Event.current.OnMouseDown(position, 0))
{
this.IsDraggingSlider = true;
SharedUniqueControlId.SetActive();
EditorGUIUtility.SetWantsMouseJumping(1);
Event.current.Use();
}
else if (SharedUniqueControlId.IsActive && this.IsDraggingSlider)
{
if (Event.current.type == EventType.MouseDrag)
{
Event.current.Use();
GUI.changed = true;
return Event.current.delta;
}
if (Event.current.type != EventType.MouseUp)
{
return Vector2.zero;
}
this.IsDraggingSlider = false;
SharedUniqueControlId.SetInactive();
EditorGUIUtility.SetWantsMouseJumping(0);
Event.current.Use();
}
return Vector2.zero;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c6172b1a79444464bfac84d14344febf
timeCreated: 1706411334

View File

@@ -0,0 +1,483 @@
//-----------------------------------------------------------------------
// <copyright file="OdinGUITableCollection.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#define USING_WIDTH_NON_PERCENT
//#undef USING_WIDTH_NON_PERCENT
using System;
using System.Collections.Generic;
using Sirenix.OdinInspector.Editor.Internal;
using UnityEditor;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class OdinGUITableCollection<TTable> : List<OdinGUITable<TTable>> where TTable : LocalizationTable
{
public readonly struct VisibleTables
{
public readonly int Offset;
public readonly int Length;
public readonly int PinnedLength;
public readonly float StartOffset;
private readonly OdinGUITableCollection<TTable> collection;
public VisibleTables(OdinGUITableCollection<TTable> collection, int offset, int length, int pinnedLength, float startOffset)
{
this.collection = collection;
this.Offset = offset;
this.Length = length + pinnedLength;
this.PinnedLength = pinnedLength;
this.StartOffset = startOffset;
}
public OdinGUITable<TTable> this[int index] =>
index >= this.PinnedLength ? this.collection[this.Offset + index - this.PinnedLength] : this.collection[index];
}
//public VisibleTables CurrentVisibleTables;
public HashSet<OdinGUITable<TTable>> TablesWithinVisibleBounds = new HashSet<OdinGUITable<TTable>>();
public OdinGUITableCollection(int capacity) : base(capacity) { }
#if USING_WIDTH_NON_PERCENT
public void AddKeyTable()
{
for (var i = 0; i < this.Count; i++)
{
if (this[i].Type == OdinGUITable<TTable>.GUITableType.Key)
{
return;
}
}
this.Add(OdinGUITable<TTable>.CreateKeyTable());
}
#else
public void AddKeyTable(float widthPercent)
{
for (var i = 0; i < this.Count; i++)
{
if (this[i].Type == OdinGUITable<TTable>.GUITableType.Key)
{
return;
}
}
this.Add(OdinGUITable<TTable>.CreateKeyTable(widthPercent));
}
#endif
#if USING_WIDTH_NON_PERCENT
public void UpdateVisibleTables(OdinGUIScrollView view, float pinnedWidth)
{
#if true
this.TablesWithinVisibleBounds.Clear();
var offset = 0;
var currentVisibleWidth = 0.0f;
var xMin = 0.0f;
var xMax = 0.0f;
for (var i = 0; i < this.Count; i++)
{
OdinGUITable<TTable> table = this[i];
if (!table.IsVisible)
{
continue;
}
if (table.IsPinned)
{
this.TablesWithinVisibleBounds.Add(table);
continue;
}
xMax += table.Width;
bool isVisible = view.Position.x >= xMin && view.Position.x <= xMax;
if (!isVisible)
{
xMin = xMax;
continue;
}
offset = i;
currentVisibleWidth = xMax - view.Position.x;
this.TablesWithinVisibleBounds.Add(table);
break;
}
float width = view.Bounds.width - pinnedWidth;
for (int i = offset + 1; i < this.Count; i++)
{
OdinGUITable<TTable> table = this[i];
if (!table.IsVisible)
{
break;
}
if (currentVisibleWidth >= width)
{
this.TablesWithinVisibleBounds.Add(table);
break;
}
this.TablesWithinVisibleBounds.Add(table);
currentVisibleWidth += this[i].Width;
}
#else
if (this.GetVisibleCount() < 1)
{
this.CurrentVisibleTables = new VisibleTables(this, 0, 0, 0, 0.0f);
return;
}
const int LENGTH_NOT_SET = -1;
var offset = 0;
int length = LENGTH_NOT_SET;
var currentVisibleWidth = 0.0f;
float xMin = 0.0f, xMax = 0.0f;
float positionX = view.Position.x;
float width = view.Bounds.width - pinnedWidth;
for (var i = 0; i < this.Count; i++)
{
OdinGUITable<TTable> table = this[i];
if (table.IsPinned)
{
continue;
}
if (!table.IsVisible)
{
break;
}
xMax += table.VisualWidth;
bool isVisible = positionX >= xMin && positionX <= xMax;
if (!isVisible)
{
xMin = xMax;
continue;
}
offset = i;
currentVisibleWidth = xMax - positionX;
break;
}
float startOffset = xMin;
for (int i = offset + 1; i < this.Count; i++)
{
if (!this[i].IsVisible)
{
length = i - offset;
break;
}
if (currentVisibleWidth >= width)
{
length = i - offset + 1;
break;
}
currentVisibleWidth += this[i].VisualWidth;
}
int pinnedCount = this.GetPinnedCount();
if (length == LENGTH_NOT_SET)
{
length = this.Count - offset;
}
this.CurrentVisibleTables = new VisibleTables(this, offset, length, pinnedCount, startOffset);
#endif
}
public float GetVisibleWidth()
{
var result = 0.0f;
for (var i = 0; i < this.Count; i++)
{
if (this[i].IsVisible)
{
result += this[i].Width;
}
}
return result;
}
public void ResizeBy(float factor)
{
for (var i = 0; i < this.Count; i++)
{
if (this[i].IsVisible)
{
this[i].Width *= factor;
}
}
}
#else
/// <summary> Calculates the <see cref="GUITable.Width"/> field on <see cref="GUITable"/> beforehand. </summary>
/// <param name="scrollView">The <see cref="OdinGUIScrollView"/> to perform calculations on.</param>
public void CalcWidths(OdinGUIScrollView scrollView)
{
float viewWidth = scrollView.ViewRect.width;
float visibleFactor = this.GetVisibleFactor();
for (var i = 0; i < this.Count; i++)
{
this[i].Width = viewWidth * (this[i].WidthPercentage * visibleFactor);
}
}
#endif
public int GetLastVisibleIndex()
{
for (int i = this.Count - 1; i >= 0; i--)
{
if (this[i].IsVisible)
{
return i;
}
}
return 0;
}
public int GetLastVisiblePinnedIndex()
{
for (int i = this.Count - 1; i >= 0; i--)
{
if (this[i].IsVisible && this[i].IsPinned)
{
return i;
}
}
return 0;
}
public int GetVisibleCount()
{
var result = 0;
for (var i = 0; i < this.Count; i++)
{
if (this[i].IsVisible)
{
result++;
}
}
return result;
}
public int GetHiddenCount()
{
var result = 0;
for (var i = 0; i < this.Count; i++)
{
if (!this[i].IsVisible)
{
result++;
}
}
return result;
}
public int GetPinnedCount()
{
var result = 0;
for (var i = 0; i < this.Count; i++)
{
if (this[i].IsVisible && this[i].IsPinned)
{
result++;
}
}
return result;
}
public OdinGUITable<TTable> GetNextVisible(int index)
{
for (int i = index + 1; i < this.Count; i++)
{
if (this[i].IsVisible)
{
return this[i];
}
}
return null;
}
public void UndoRecordCollection(SharedTableData sharedTableData, string name)
{
for (var i = 0; i < this.Count; i++)
{
switch (this[i].Type)
{
case OdinGUITable<TTable>.GUITableType.Default:
Undo.RecordObject(this[i].Asset, name);
break;
case OdinGUITable<TTable>.GUITableType.Key:
if (sharedTableData != null)
{
Undo.RecordObject(sharedTableData, name);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
public void SetDirty(SharedTableData sharedTableData)
{
for (var i = 0; i < this.Count; i++)
{
switch (this[i].Type)
{
case OdinGUITable<TTable>.GUITableType.Default:
EditorUtility.SetDirty(this[i].Asset);
break;
case OdinGUITable<TTable>.GUITableType.Key:
if (sharedTableData != null)
{
EditorUtility.SetDirty(sharedTableData);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
#if !USING_WIDTH_NON_PERCENT
private float GetVisibleFactor()
{
var visibleCount = 0;
var sum = 0.0f;
for (var i = 0; i < this.Count; i++)
{
if (!this[i].IsVisible)
{
continue;
}
sum += this[i].WidthPercentage;
visibleCount++;
}
float result = (float) this.Count / visibleCount;
sum *= result;
result *= 1.0f / sum;
return result;
}
#endif
public void ResizeToFit(float targetWidth)
{
var count = 0;
for (var i = 0; i < this.Count; i++)
{
if (this[i].IsPinned || !this[i].IsVisible)
{
continue;
}
count++;
}
if (count < 1)
{
return;
}
int averageSize = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH * count;
float scaleFactor = targetWidth / averageSize;
for (var i = 0; i < this.Count; i++)
{
if (!this[i].IsVisible || this[i].IsPinned)
{
continue;
}
this[i].Width = OdinLocalizationConstants.DEFAULT_COLUMN_WIDTH * scaleFactor;
}
}
public void ResizePinnedToFit(float targetWidth)
{
var currentTotalWidth = 0.0f;
for (var i = 0; i < this.Count; i++)
{
if (!this[i].IsPinned || !this[i].IsVisible)
{
continue;
}
currentTotalWidth += this[i].Width;
}
if (currentTotalWidth < 1.0f)
{
return;
}
float scaleFactor = targetWidth / currentTotalWidth;
for (var i = 0; i < this.Count; i++)
{
if (!this[i].IsVisible || !this[i].IsPinned)
{
continue;
}
this[i].Width *= scaleFactor;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a739bb40d31496a9591094d81fd2d19
timeCreated: 1706411492

View File

@@ -0,0 +1,441 @@
//-----------------------------------------------------------------------
// <copyright file="OdinSharedEntryCollection.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEditor.Localization;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class OdinSharedEntryCollection
{
private class StringComparer : IComparer<string>
{
public bool IsAscending = true;
public int Compare(string self, string other)
{
if (string.IsNullOrEmpty(self) && string.IsNullOrEmpty(other))
{
return 0;
}
if (string.IsNullOrEmpty(self))
{
return this.IsAscending ? 1 : -1;
}
if (string.IsNullOrEmpty(other))
{
return this.IsAscending ? -1 : 1;
}
return string.Compare(self, other, StringComparison.InvariantCulture);
}
}
public enum SortOrderState
{
Unsorted,
Ascending,
Descending
}
public bool IsSearching;
public bool IsSorted => this.CurrentSortOrderState != SortOrderState.Unsorted;
public int Length => this.Entries.Count;
private string _searchTerm = string.Empty;
private StringComparer stringComparer = new StringComparer();
public string SearchTerm
{
get => this._searchTerm;
private set
{
this._searchTerm = value;
this.IsSearching = !string.IsNullOrEmpty(value);
}
}
public List<SharedTableData.SharedTableEntry> Entries => this.IsSorted ? this.SortedEntries : this.Collection.SharedData.Entries;
public List<SharedTableData.SharedTableEntry> SortedEntries;
public readonly HashSet<SharedTableData.SharedTableEntry> FilteredEntries;
public readonly LocalizationTableCollection Collection;
public SortOrderState CurrentSortOrderState = SortOrderState.Unsorted;
public OdinSharedEntryCollection(LocalizationTableCollection collection)
{
this.Collection = collection;
this.FilteredEntries = new HashSet<SharedTableData.SharedTableEntry>();
}
public SharedTableData.SharedTableEntry this[int index] => this.Entries[index];
public bool IsVisible(SharedTableData.SharedTableEntry sharedEntry)
{
return !this.IsSearching || (this.IsSearching && this.FilteredEntries.Contains(sharedEntry));
}
public bool UpdateSearchTerm<TTable>(string value,
OdinGUITableCollection<TTable> tables,
LocalizationTableCollection collection,
bool forceUpdate = false) where TTable : LocalizationTable
{
if (this.SearchTerm == value && !forceUpdate)
{
return false;
}
this.SearchTerm = value;
if (string.IsNullOrEmpty(this.SearchTerm))
{
return true;
}
this.FilteredEntries.Clear();
for (var i = 0; i < tables.Count; i++)
{
OdinGUITable<TTable> table = tables[i];
switch (table.Type)
{
case OdinGUITable<TTable>.GUITableType.Default:
switch (table.Asset)
{
case AssetTable assetTable:
var assetCollection = collection as AssetTableCollection;
for (var j = 0; j < this.Length; j++)
{
SharedTableData.SharedTableEntry sharedEntry = this[j];
Type assetType = assetCollection.GetEntryAssetType(sharedEntry.Id);
UnityEngine.Object asset = OdinLocalizationAssetCache.Get(sharedEntry, assetTable, assetType);
if (asset == null)
{
continue;
}
if (FuzzySearch.Contains(this.SearchTerm, asset.name))
{
this.FilteredEntries.Add(sharedEntry);
}
}
break;
case StringTable stringTable:
for (var j = 0; j < this.Length; j++)
{
SharedTableData.SharedTableEntry sharedEntry = this[j];
StringTableEntry entry = stringTable.GetEntry(sharedEntry.Id);
if (entry is null || string.IsNullOrEmpty(entry.Value))
{
continue;
}
if (FuzzySearch.Contains(this.SearchTerm, entry.Value))
{
this.FilteredEntries.Add(sharedEntry);
}
}
break;
}
break;
case OdinGUITable<TTable>.GUITableType.Key:
for (var j = 0; j < this.Entries.Count; j++)
{
if (FuzzySearch.Contains(this.SearchTerm, this.Entries[j].Key))
{
this.FilteredEntries.Add(this.Entries[j]);
}
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}
return true;
}
public void SortByKeys(bool preserveCurrentOrder)
{
switch (this.CurrentSortOrderState)
{
case SortOrderState.Ascending:
this.stringComparer.IsAscending = true;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries.OrderBy(entry => entry.Key, this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => entry.Key, this.stringComparer).ToList();
}
return;
case SortOrderState.Descending:
this.stringComparer.IsAscending = false;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries.OrderByDescending(entry => entry.Key, this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Collection.SharedData.Entries.OrderByDescending(entry => entry.Key, this.stringComparer).ToList();
}
return;
}
}
public void SortByAssetTable(AssetTableCollection collection, AssetTable table, bool preserveCurrentOrder)
{
switch (this.CurrentSortOrderState)
{
case SortOrderState.Ascending:
this.stringComparer.IsAscending = true;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries
.OrderBy(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer)
.ToList();
}
return;
case SortOrderState.Descending:
this.stringComparer.IsAscending = false;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries
.OrderByDescending(entry => GetAssetNameFromEntry(entry, table, collection),
this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Entries.OrderByDescending(entry => GetAssetNameFromEntry(entry, table, collection), this.stringComparer).ToList();
}
return;
}
}
public void SortByStringTable(StringTable table, bool preserveCurrentOrder)
{
switch (this.CurrentSortOrderState)
{
case SortOrderState.Ascending:
this.stringComparer.IsAscending = true;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries
.OrderBy(entry => GetStringFromEntry(entry, table), this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Collection.SharedData.Entries.OrderBy(entry => GetStringFromEntry(entry, table), this.stringComparer).ToList();
}
return;
case SortOrderState.Descending:
this.stringComparer.IsAscending = false;
if (preserveCurrentOrder)
{
List<SharedTableData.SharedTableEntry> result = this.Collection.SharedData.Entries
.OrderByDescending(entry => GetStringFromEntry(entry, table), this.stringComparer)
.ThenBy(this.GetOrderIndex)
.ToList();
this.SortedEntries = result;
}
else
{
this.SortedEntries = this.Entries.OrderByDescending(entry => GetStringFromEntry(entry, table), this.stringComparer).ToList();
}
return;
}
}
private static string GetStringFromEntry(SharedTableData.SharedTableEntry sharedEntry, StringTable table)
{
StringTableEntry entry = table.GetEntry(sharedEntry.Id);
return entry?.Value;
}
private static string GetAssetNameFromEntry(SharedTableData.SharedTableEntry sharedEntry, AssetTable table, AssetTableCollection collection)
{
AssetTableEntry entry = table.GetEntry(sharedEntry.Id);
if (entry == null || entry.IsEmpty)
{
return null;
}
Type type = collection.GetEntryAssetType(sharedEntry.Id);
UnityEngine.Object asset = OdinLocalizationAssetCache.Get(entry.Guid, type);
return asset == null ? null : asset.name;
}
public void MoveEntry(int from, int to)
{
if (from < 0 || from >= this.Entries.Count)
{
return;
}
if (to < 0 || to > this.Entries.Count)
{
return;
}
if (from == to)
{
return;
}
SharedTableData.SharedTableEntry fromEntry = this.Collection.SharedData.Entries[from];
if (to > from)
{
to -= 1;
}
//to = afterTo ? to + 1 : to;
this.Collection.SharedData.Entries.RemoveAt(from);
this.Collection.SharedData.Entries.Insert(to, fromEntry);
OdinLocalizationEvents.RaiseTableEntryModified(this.Collection.SharedData.Entries[from]);
OdinLocalizationEvents.RaiseTableEntryModified(this.Collection.SharedData.Entries[to]);
EditorUtility.SetDirty(this.Collection.SharedData);
}
public void GotoNextSortOrderState()
{
switch (this.CurrentSortOrderState)
{
case SortOrderState.Unsorted:
this.CurrentSortOrderState = SortOrderState.Ascending;
break;
case SortOrderState.Ascending:
this.CurrentSortOrderState = SortOrderState.Descending;
break;
case SortOrderState.Descending:
this.CurrentSortOrderState = SortOrderState.Unsorted;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public int GetIndex(SharedTableData.SharedTableEntry sharedEntry)
{
for (var i = 0; i < this.Length; i++)
{
if (this[i].Id == sharedEntry.Id)
{
return i;
}
}
return -1;
}
public int GetOrderIndex(SharedTableData.SharedTableEntry sharedEntry)
{
if (this.IsSorted && this.SortedEntries.Count == this.Length)
{
for (var i = 0; i < this.SortedEntries.Count; i++)
{
if (this.SortedEntries[i].Id == sharedEntry.Id)
{
return i;
}
}
return -1;
}
for (var i = 0; i < this.Length; i++)
{
if (this[i].Id == sharedEntry.Id)
{
return i;
}
}
return -1;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a543e4ee8d6d4b629befbccf5aa7b5e6
timeCreated: 1706407967

View File

@@ -0,0 +1,449 @@
//-----------------------------------------------------------------------
// <copyright file="OdinStringTableCollectionEditor.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#define USING_WIDTH_NON_PERCENT
using System;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Internal;
using Sirenix.OdinInspector.Modules.Localization.Editor.Configs;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEditor.Localization;
using UnityEngine;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class OdinStringTableCollectionEditor : OdinTableCollectionEditor<StringTableCollection, StringTable, StringTableEntry>
{
private string currentSyntaxSource;
private string currentSyntaxHighlightedText;
private string currentSyntaxErrorMessage;
private Exception currentSyntaxException;
private bool currentSyntaxHasErrors;
public OdinStringTableCollectionEditor(StringTableCollection collection, OdinMenuEditorWindow relatedWindow,
OdinLocalizationEditorWindow.WindowState windowState) :
base(collection, relatedWindow, windowState) { }
protected override void OnInitialize()
{
for (var i = 0; i < this.SharedEntries.Length; i++)
{
SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i];
this.MeasureEntry(sharedEntry);
}
//this.SharedEntries.OnSharedEntryAdded += (i, sharedEntry) => { this.MeasureEntry(sharedEntry); };
//this.SharedEntries.OnSharedEntryRemoved += (i, sharedEntry) => { this.SharedEntryHeights.Remove(sharedEntry.Id); };
this.OnTableEntryModified = sharedEntry =>
{
if (!this.Collection.SharedData.Contains(sharedEntry.Id))
{
return;
}
int index = this.SharedEntries.GetIndex(sharedEntry);
this.MeasureEntry(sharedEntry);
this.EntryScrollView.ReallocateRect(index, this.SharedEntryHeights[sharedEntry.Id], sharedEntry);
};
}
protected override void AllocateItems()
{
for (var i = 0; i < this.SharedEntries.Length; i++)
{
SharedTableData.SharedTableEntry sharedEntry = this.SharedEntries[i];
if (!this.SharedEntries.IsVisible(sharedEntry))
{
continue;
}
if (!this.SharedEntryHeights.ContainsKey(sharedEntry.Id))
{
this.MeasureEntry(sharedEntry);
}
this.EntryScrollView.AllocateRect(this.SharedEntryHeights[sharedEntry.Id], sharedEntry);
#if false
this.ControlIds[sharedEntry] = GUIUtility.GetControlID(FocusType.Keyboard);
for (var j = 0; j < this.GUITables.Count; j++)
{
OdinGUITable<StringTable> table = this.GUITables[j];
if (table.Type == OdinGUITable<StringTable>.GUITableType.Key)
{
continue;
}
StringTableEntry entry = table.Table.GetEntry(sharedEntry.Id);
if (entry is null)
{
table.Table.AddEntry(sharedEntry.Id, string.Empty);
entry = table.Table.GetEntry(sharedEntry.Id);
}
this.ControlIds[entry] = GUIUtility.GetControlID(FocusType.Keyboard);
}
#endif
}
}
protected override void DrawItems(ref OdinGUIScrollView.VisibleItems visibleItems)
{
this.MeasureVisibleEntries(ref visibleItems);
float scrollSpeed = OdinLocalizationConfig.Instance.scrollSpeed;
this.EntryScrollView.BeginScrollView(offset: new Vector2(this.PinnedWidth, OdinLocalizationConstants.COLUMN_HEIGHT),
addViewSize: new Vector2(-this.PinnedWidth, 0),
scrollSpeed: scrollSpeed);
{
this.DrawEntries(ref visibleItems, false);
}
this.EntryScrollView.EndScrollView();
this.EntryScrollView.BeginClip(offset: new Vector2(0.0f, OdinLocalizationConstants.COLUMN_HEIGHT), ignoreScrollX: true);
{
this.DrawEntries(ref visibleItems, true);
}
this.EntryScrollView.EndClip();
}
private void DrawEntries(ref OdinGUIScrollView.VisibleItems visibleItems, bool pinned)
{
for (var i = 0; i < visibleItems.Length; i++)
{
if (!visibleItems.HasAssociatedData(i))
{
continue;
}
int hint = visibleItems.Offset + i + this.ControlIdHint;
Rect position = visibleItems.GetRect(i);
var sharedEntry = visibleItems.GetAssociatedData<SharedTableData.SharedTableEntry>(i);
bool isEven = (visibleItems.Offset + i) % 2 == 0;
for (var j = 0; j < this.GUITables.Count; j++)
{
OdinGUITable<StringTable> table = this.GUITables[j];
if (!table.IsVisible)
{
continue;
}
if (table.IsPinned != pinned)
{
continue;
}
if (!this.GUITables.TablesWithinVisibleBounds.Contains(table))
{
GUIUtility.GetControlID(hint, FocusType.Keyboard);
position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING);
continue;
}
#if USING_WIDTH_NON_PERCENT
Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING);
#else
Rect entryRect = position.TakeFromLeft(table.Width).Padding(OdinLocalizationConstants.ENTRY_PADDING);
#endif
bool isCellPressed, isSelected;
switch (table.Type)
{
case OdinGUITable<StringTable>.GUITableType.Key:
isSelected = this.IsSharedEntrySelected(sharedEntry);
if (isSelected)
{
this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine);
FancyColor start = FancyColor.Gray;
var end = OdinLocalizationGUI.Selected;
FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay);
}
isCellPressed = DrawCell(entryRect, isEven);
this.DrawKey(entryRect, sharedEntry, GUIUtility.GetControlID(hint, FocusType.Keyboard));
if (isSelected)
{
FancyColor.PopBlend();
}
if (isCellPressed)
{
this.SelectSharedEntry(sharedEntry);
}
break;
case OdinGUITable<StringTable>.GUITableType.Default:
StringTableEntry entry = table.Asset.GetEntry(sharedEntry.Id);
isSelected = this.IsEntrySelected(entry);
if (isSelected)
{
this.SelectionAnimFloat.Move(1 / 0.18f, Easing.InSine);
FancyColor start = FancyColor.Gray;
FancyColor end = OdinLocalizationGUI.Selected;
if (entry.IsSmart && OdinLocalizationConfig.Instance.useSyntaxHighlighter)
{
if (this.currentSyntaxSource != entry.Value)
{
this.currentSyntaxHighlightedText = OdinLocalizationSyntaxHighlighter.HighlightAsRichText(entry.Value);
this.currentSyntaxErrorMessage = OdinLocalizationSyntaxHighlighter.GetErrorMessage(entry.Value, out bool foundError, out Exception exception);
this.currentSyntaxHasErrors = foundError;
this.currentSyntaxException = exception;
this.currentSyntaxSource = entry.Value;
}
if (this.currentSyntaxHasErrors)
{
FancyColor.PushBlend(start.Lerp(new FancyColor(0.68f, 0.2f, 0.2f), this.SelectionAnimFloat), FancyColor.BlendMode.Overlay);
}
else
{
FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay);
}
}
else
{
FancyColor.PushBlend(start.Lerp(end, this.SelectionAnimFloat), FancyColor.BlendMode.Overlay);
}
}
isCellPressed = DrawCell(entryRect, isEven);
this.DrawEntry(entryRect, entry, GUIUtility.GetControlID(hint, FocusType.Keyboard), table, sharedEntry);
if (isSelected)
{
if (OdinLocalizationConfig.Instance.useSyntaxHighlighter && entry.IsSmart && this.currentSyntaxHasErrors)
{
Rect errorRect = entryRect.AlignLeft(OdinLocalizationConstants.ROW_MENU_WIDTH).AlignMiddle(16);
SdfIcons.DrawIcon(errorRect, SdfIconType.ExclamationOctagonFill,
Event.current.IsMouseOver(errorRect) ? new Color(1, 1, 1, 1f) : new Color(1, 1, 1, 0.6f));
if (Event.current.OnMouseDown(errorRect, 0))
{
this.RelatedWindow.ShowToast(ToastPosition.BottomLeft,
SdfIconType.ExclamationOctagonFill,
this.currentSyntaxErrorMessage,
new Color(0.68f, 0.2f, 0.2f),
20.0f);
if (this.currentSyntaxException != null)
{
Debug.LogException(this.currentSyntaxException);
}
}
}
FancyColor.PopBlend();
}
if (isCellPressed)
{
if (entry is null)
{
entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty);
}
this.SelectEntry(entry);
}
break;
}
}
}
}
private void DrawEntry(Rect position, StringTableEntry entry, int id, OdinGUITable<StringTable> table, SharedTableData.SharedTableEntry sharedEntry)
{
bool changed;
string value;
Rect smartToggleRect = position.TakeFromRight(OdinLocalizationConstants.ROW_MENU_WIDTH);
position.TakeFromLeft(OdinLocalizationConstants.ROW_MENU_WIDTH);
if (entry?.Value is null)
{
value = OdinLocalizationGUI.TextField(position, string.Empty, out changed, id);
}
else if (OdinLocalizationConfig.Instance.useSyntaxHighlighter && entry.IsSmart && entry == this.CurrentSelectedEntry)
{
value = OdinLocalizationGUI.TextFieldSyntaxHighlighted(position, entry.Value, this.currentSyntaxHighlightedText, out changed, id);
if (changed)
{
this.currentSyntaxHighlightedText = OdinLocalizationSyntaxHighlighter.HighlightAsRichText(value);
this.currentSyntaxErrorMessage = OdinLocalizationSyntaxHighlighter.GetErrorMessage(value, out bool foundError, out Exception exception);
this.currentSyntaxHasErrors = foundError;
this.currentSyntaxException = exception;
this.currentSyntaxSource = value;
}
}
else
{
value = OdinLocalizationGUI.TextField(position, entry.Value, out changed, id);
}
if (changed)
{
if (entry == null)
{
entry = table.Asset.AddEntry(sharedEntry.Id, value);
}
Undo.RecordObject(entry.Table, "Modified String Table Entry Text");
entry.Value = value;
OdinLocalizationEvents.RaiseTableEntryModified(entry.SharedEntry);
EditorUtility.SetDirty(entry.Table);
}
smartToggleRect = smartToggleRect.AlignMiddle(16);
if (entry == null)
{
SdfIcons.DrawIcon(smartToggleRect, SdfIconType.Lightbulb, new Color(1, 1, 1, Event.current.IsMouseOver(smartToggleRect) ? 0.8f : 0.3f));
if (Event.current.OnMouseDown(smartToggleRect, 0))
{
Undo.RecordObject(table.Asset, "Added String Table Entry By Smart Toggle");
entry = table.Asset.AddEntry(sharedEntry.Id, string.Empty);
entry.IsSmart = !entry.IsSmart;
EditorUtility.SetDirty(table.Asset);
}
}
else
{
SdfIcons.DrawIcon(smartToggleRect,
entry.IsSmart ? SdfIconType.LightbulbFill : SdfIconType.Lightbulb,
new Color(1, 1, 1, Event.current.IsMouseOver(smartToggleRect) ? 0.8f : 0.3f));
if (Event.current.OnMouseDown(smartToggleRect, 0))
{
Undo.RecordObject(entry.Table, "Toggled Smart Flag On String Entry");
entry.IsSmart = !entry.IsSmart;
EditorUtility.SetDirty(entry.Table);
}
}
GUI.Label(smartToggleRect, GUIHelper.TempContent(string.Empty, "Toggle Smart String"));
}
protected override void MeasureAllEntries()
{
for (var i = 0; i < this.SharedEntries.Length; i++)
{
this.MeasureEntry(this.SharedEntries[i]);
}
this.HasGUIChanged = true;
}
protected override void MeasureVisibleEntries(ref OdinGUIScrollView.VisibleItems visibleItems)
{
int dataOffset = visibleItems.Offset;
for (var i = 0; i < visibleItems.Length; i++)
{
if (!visibleItems.HasAssociatedData(i))
{
continue;
}
var sharedEntry = visibleItems.GetAssociatedData<SharedTableData.SharedTableEntry>(i);
this.MeasureEntry(sharedEntry);
this.EntryScrollView.ReallocateRect(dataOffset + i, this.SharedEntryHeights[sharedEntry.Id], sharedEntry);
}
}
private void MeasureEntry(SharedTableData.SharedTableEntry sharedEntry)
{
float height = OdinLocalizationConstants.ROW_HEIGHT;
for (var i = 0; i < this.GUITables.Count; i++)
{
OdinGUITable<StringTable> currentTable = this.GUITables[i];
switch (currentTable.Type)
{
case OdinGUITable<StringTable>.GUITableType.Default:
StringTableEntry strEntry = currentTable.Asset.GetEntry(sharedEntry.Id);
if (strEntry is null)
{
continue;
}
#if USING_WIDTH_NON_PERCENT
float strEntryHeight = MeasureText(strEntry.Value, currentTable.Width);
#else
float strEntryHeight = MeasureText(strEntry.Value, currentTable.Width);
#endif
if (strEntryHeight > height)
{
height = strEntryHeight;
}
break;
case OdinGUITable<StringTable>.GUITableType.Key:
#if USING_WIDTH_NON_PERCENT
float keyHeight = MeasureText(sharedEntry.Key, currentTable.Width);
#else
float keyHeight = MeasureText(sharedEntry.Key, currentTable.Width);
#endif
if (keyHeight > height)
{
height = keyHeight;
}
break;
}
}
this.SharedEntryHeights[sharedEntry.Id] = height;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b8803d1557464e9a9d34713c9c9755af
timeCreated: 1704811189

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b8f34a530ab34e11b148c6c45d14fd12
timeCreated: 1704797131

View File

@@ -0,0 +1,23 @@
//-----------------------------------------------------------------------
// <copyright file="OdinTemplateMetadata.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Localization.Metadata;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
#if false
[HideInInspector] // ReadOnly
[Metadata(AllowedTypes = MetadataType.SharedTableData, AllowMultiple = false)]
public class OdinTemplateMetadata : IMetadata
{
public Type TableAssetType;
public List<Type> MetadataExpected = new List<Type>();
}
#endif
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 50bcb1c3eb1c42b3b6a84c57b1c240dd
timeCreated: 1707729097

View File

@@ -0,0 +1,38 @@
//-----------------------------------------------------------------------
// <copyright file="Toggle.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using Sirenix.OdinInspector.Editor.Internal;
using UnityEngine;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class Toggle
{
public string Label;
public float StartXMin;
public float StartXMax;
public Color StartColor;
public Rect CurrentThumbRect;
public Color CurrentColor;
public bool Enabled;
public SirenixAnimationUtility.InterpolatedFloat T1 = new SirenixAnimationUtility.InterpolatedFloat {Destination = 1f};
public SirenixAnimationUtility.InterpolatedFloat T2 = new SirenixAnimationUtility.InterpolatedFloat {Destination = 1f};
private bool _toggled;
public bool Toggled
{
get => this._toggled;
set
{
this._toggled = value;
this.StartXMin = this.CurrentThumbRect.xMin;
this.StartXMax = this.CurrentThumbRect.xMax;
this.StartColor = this.CurrentColor;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4e8615bf94265a4a96bf4c23380de7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7cf3a6e0f9c8495d843c313b4437232b
timeCreated: 1704797609

View File

@@ -0,0 +1,68 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationAssetCache.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using Sirenix.Config;
using UnityEditor;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationAssetCache
{
private readonly struct AssetIdentifier
{
public readonly Type AssetType;
public readonly string Guid;
public AssetIdentifier(Type assetType, string guid)
{
this.AssetType = assetType;
this.Guid = guid;
}
}
private static readonly Dictionary<AssetIdentifier, UnityEngine.Object> Assets = new Dictionary<AssetIdentifier, UnityEngine.Object>();
public static UnityEngine.Object Get(string guid, Type assetType)
{
if (string.IsNullOrEmpty(guid))
{
return null;
}
var identifier = new AssetIdentifier(assetType, guid);
if (Assets.TryGetValue(identifier, out UnityEngine.Object result))
{
return result;
}
string path = AssetDatabase.GUIDToAssetPath(guid);
result = AssetDatabase.LoadAssetAtPath(path, assetType);
Assets.Add(identifier, result);
return result;
}
public static UnityEngine.Object Get(SharedTableData.SharedTableEntry sharedEntry, AssetTable assetTable, Type assetType)
{
AssetTableEntry entry = assetTable.GetEntry(sharedEntry.Id);
if (entry == null || entry.IsEmpty)
{
return null;
}
return Get(entry.Guid, assetType);
}
public static void Clear() => Assets.Clear();
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f5b97d1f7b884ac99cc8ebcdeb3363a7
timeCreated: 1704809501

View File

@@ -0,0 +1,68 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationEditorSettings.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using UnityEditor;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.Localization;
using UnityEngine;
using UnityEngine.Localization.Settings;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationEditorSettings
{
public static LocalizationEditorSettings Instance =>
(LocalizationEditorSettings) OdinLocalizationReflectionValues.LocalizationEditorSettings_Instance.GetValue(null);
public static AddressableAssetSettings GetAddressableAssetSettings(bool create)
{
return OdinLocalizationReflectionValues.LocalizationEditorSettings_GetAddressableAssetSettingsFunc(create);
}
public static bool IsTableNameValid(Type collectionType, string name, out string errorMessage)
{
errorMessage = (string) OdinLocalizationReflectionValues.LocalizationEditorSettings_IsTableNameValid.Invoke(Instance,
new object[] {collectionType, name});
return string.IsNullOrEmpty(errorMessage);
}
public static bool IsTableNameValid(Type collectionType, string name)
{
return IsTableNameValid(collectionType, name, out string _);
}
public static bool CreateDefaultLocalizationSettingsAsset()
{
if (LocalizationEditorSettings.ActiveLocalizationSettings != null)
{
return false;
}
string localizationSettingsPath = EditorUtility.SaveFilePanelInProject("Create Localization Settings",
"Localization Settings",
"asset",
"Create the Localization Settings asset");
if (string.IsNullOrEmpty(localizationSettingsPath))
{
return false;
}
var settings = ScriptableObject.CreateInstance<LocalizationSettings>();
settings.name = "Default Localization Settings";
AssetDatabase.CreateAsset(settings, localizationSettingsPath);
AssetDatabase.SaveAssets();
LocalizationEditorSettings.ActiveLocalizationSettings = settings;
return true;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 645f3592e85448d7a963218911547f3c
timeCreated: 1706732179

View File

@@ -0,0 +1,50 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationEvents.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using UnityEditor.Localization;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationEvents
{
private static readonly Action<SharedTableData.SharedTableEntry> RaiseTableEntryModifiedAction;
private static readonly Action<LocalizationTableCollection, SharedTableData.SharedTableEntry> RaiseTableEntryAddedAction;
private static readonly Action<LocalizationTableCollection, SharedTableData.SharedTableEntry> RaiseTableEntryRemovedAction;
private static readonly Action<AssetTableCollection, AssetTable, AssetTableEntry> RaiseAssetTableEntryAddedAction;
private static readonly Action<AssetTableCollection, AssetTable, AssetTableEntry, string> RaiseAssetTableEntryRemovedAction;
static OdinLocalizationEvents()
{
RaiseTableEntryModifiedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryModified_Method_Delegate(LocalizationEditorSettings.EditorEvents);
RaiseTableEntryAddedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryAdded_Method_Delegate(LocalizationEditorSettings.EditorEvents);
RaiseTableEntryRemovedAction = OdinLocalizationReflectionValues.Create_RaiseTableEntryRemoved_Method_Delegate(LocalizationEditorSettings.EditorEvents);
RaiseAssetTableEntryAddedAction =
OdinLocalizationReflectionValues.Create_AssetTableEntryAdded_Method_Delegate(LocalizationEditorSettings.EditorEvents);
RaiseAssetTableEntryRemovedAction =
OdinLocalizationReflectionValues.Create_AssetTableEntryRemoved_Method_Delegate(LocalizationEditorSettings.EditorEvents);
}
public static void RaiseTableEntryModified(SharedTableData.SharedTableEntry sharedEntry) => RaiseTableEntryModifiedAction(sharedEntry);
public static void RaiseTableEntryAdded(LocalizationTableCollection collection, SharedTableData.SharedTableEntry sharedEntry) =>
RaiseTableEntryAddedAction(collection, sharedEntry);
public static void RaiseTableEntryRemoved(LocalizationTableCollection collection, SharedTableData.SharedTableEntry sharedEntry) =>
RaiseTableEntryRemovedAction(collection, sharedEntry);
public static void RaiseAssetTableEntryAdded(AssetTableCollection assetCollection, AssetTable table, AssetTableEntry entry) =>
RaiseAssetTableEntryAddedAction(assetCollection, table, entry);
public static void RaiseAssetTableEntryRemoved(AssetTableCollection assetCollection, AssetTable table, AssetTableEntry entry, string guid) =>
RaiseAssetTableEntryRemovedAction(assetCollection, table, entry, guid);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a7cf7883289c4de29b6e2ae8a072c3bd
timeCreated: 1705050052

View File

@@ -0,0 +1,438 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationGUI.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Internal;
using Sirenix.Reflection.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
// TODO: update name
public static class OdinLocalizationGUI
{
public static GUIStyle RichLabelTextCenteredMultiLine
{
get
{
if (_richLabelTextCenteredMultiLine == null)
{
_richLabelTextCenteredMultiLine = new GUIStyle(SirenixGUIStyles.MultiLineCenteredLabel)
{
richText = true
};
}
return _richLabelTextCenteredMultiLine;
}
}
public static GUIStyle CardTitleWhite
{
get
{
if (_cardTitleWhite == null)
{
_cardTitleWhite = new GUIStyle(SirenixGUIStyles.MultiLineWhiteLabel)
{
fontSize = 12,
fontStyle = FontStyle.Bold
};
}
return _cardTitleWhite;
}
}
public static FancyColor Background => EditorGUIUtility.isProSkin ? Color.black : Color.white;
// TODO: LIGHT MODE
public static FancyColor MenuBackground => EditorGUIUtility.isProSkin ? DarkMenuBackground : LightMenuBackground;
public static FancyColor WindowBackground => EditorGUIUtility.isProSkin ? DarkWindowBackground : LightWindowBackground;
public static FancyColor Panel => EditorGUIUtility.isProSkin ? DarkPanel : LightPanel;
public static FancyColor PanelGap => EditorGUIUtility.isProSkin ? DarkPanelGap : LightPanelGap;
public static FancyColor Selected => EditorGUIUtility.isProSkin ? DarkSelected : LightSelected;
public static FancyColor TabsBackground => EditorGUIUtility.isProSkin ? DarkTabsBackground : LightTabsBackground;
public static FancyColor ColumnBackground => EditorGUIUtility.isProSkin ? DarkColumnBackground : LightColumnBackground;
public static FancyColor RowEvenBackground => EditorGUIUtility.isProSkin ? DarkRowEvenBackground : LightRowEvenBackground;
public static FancyColor RowEvenBackground2 => EditorGUIUtility.isProSkin ? DarkRowEvenBackground2 : LightRowEvenBackground2;
public static FancyColor RowOddBackground => EditorGUIUtility.isProSkin ? DarkRowOddBackground : LightRowOddBackground;
public static FancyColor RowOddBackground2 => EditorGUIUtility.isProSkin ? DarkRowOddBackground2 : LightRowOddBackground2;
public static FancyColor RowBorder => EditorGUIUtility.isProSkin ? DarkRowBorder : LightRowBorder;
public static FancyColor RowBorderHover => EditorGUIUtility.isProSkin ? DarkRowBorderHover : LightRowBorderHover;
public static FancyColor Tab => EditorGUIUtility.isProSkin ? DarkTab : LightTab;
public static FancyColor TabHover => EditorGUIUtility.isProSkin ? DarkTabHover : LightTabHover;
public static FancyColor TabSelected => EditorGUIUtility.isProSkin ? DarkTabSelected : LightTabSelected;
public static FancyColor Checkerboard => DarkCheckerboard;
public static FancyColor CheckerboardBorder => DarkCheckerboardBorder;
public static FancyColor DarkMenuBackground;
public static FancyColor DarkWindowBackground;
public static FancyColor DarkPanel;
public static FancyColor DarkPanelGap;
public static FancyColor DarkSelected;
public static FancyColor DarkTabsBackground;
public static FancyColor DarkColumnBackground;
public static FancyColor DarkRowEvenBackground;
public static FancyColor DarkRowEvenBackground2;
public static FancyColor DarkRowOddBackground;
public static FancyColor DarkRowOddBackground2;
public static FancyColor DarkRowBorder;
public static FancyColor DarkRowBorderHover;
public static FancyColor DarkTab;
public static FancyColor DarkTabHover;
public static FancyColor DarkTabSelected;
public static FancyColor LightMenuBackground;
public static FancyColor LightWindowBackground;
public static FancyColor LightPanel;
public static FancyColor LightPanelGap;
public static FancyColor LightSelected;
public static FancyColor LightTabsBackground;
public static FancyColor LightColumnBackground;
public static FancyColor LightRowEvenBackground;
public static FancyColor LightRowEvenBackground2;
public static FancyColor LightRowOddBackground;
public static FancyColor LightRowOddBackground2;
public static FancyColor LightRowBorder;
public static FancyColor LightRowBorderHover;
public static FancyColor LightTab;
public static FancyColor LightTabHover;
public static FancyColor LightTabSelected;
public static FancyColor DarkCheckerboard;
public static FancyColor DarkCheckerboardBorder;
private static GUIStyle DefaultTextStyle;
private static GUIStyle _richLabelTextCenteredMultiLine;
private static GUIStyle _cardTitleWhite;
static OdinLocalizationGUI()
{
DarkWindowBackground = new Color(0.15f, 0.15f, 0.15f, 1.0f);
LightWindowBackground = new Color(0.358f, 0.358f, 0.358f, 1.0f);
DarkPanel = new Color(0.2156863f, 0.2156863f, 0.2156863f, 1.0f);
DarkPanelGap = new Color(0.0856863f, 0.0856863f, 0.0856863f, 1.0f);
LightPanel = new Color(0.761f, 0.761f, 0.761f, 1.0f);
LightPanelGap = new Color(0.532f, 0.532f, 0.532f, 1.0f);
DarkSelected = new Color(0.7076807f, 0.8213042f, 0.8773585f, 1.0f);
LightSelected = new Color(0.358f, 0.446f, 0.509f, 1.0f);
DarkTabsBackground = new Color(0.1886792f, 0.1886792f, 0.1886792f, 1.0f);
LightTabsBackground = new Color(0.538f, 0.538f, 0.538f, 1.0f);
DarkColumnBackground = new Color(0.099f, 0.099f, 0.099f, 1.0f);
LightColumnBackground = new FancyColor(0.84f);
var rowBaseBlend = new FancyColor(0.42f);
#region DARK_ROW
var darkRowEvenBackground = new FancyColor(0.2f);
var darkRowOddBackground = new FancyColor(0.16f);
DarkRowEvenBackground = darkRowEvenBackground;
DarkRowOddBackground = darkRowOddBackground;
DarkRowEvenBackground2 = darkRowEvenBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay);
DarkRowOddBackground2 = darkRowOddBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay);
DarkRowBorder = new Color(0, 0, 0, 0.3607843f);
DarkRowBorderHover = new Color(1, 1, 1, 0.2470588f);
#endregion
#region LIGHT_ROW
var lightRowEvenBackground = new FancyColor(0.711f);
var lightRowOddBackground = new FancyColor(0.611f);
LightRowEvenBackground = lightRowEvenBackground;
LightRowOddBackground = lightRowOddBackground;
LightRowEvenBackground2 = lightRowEvenBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay);
LightRowOddBackground2 = lightRowOddBackground.Blend(rowBaseBlend, FancyColor.BlendMode.Overlay);
LightRowBorder = new Color(0, 0, 0, 0.2f);
LightRowBorderHover = new Color(1, 1, 1, 0.4f);
#endregion
DarkTab = new Color(0.254717f, 0.254717f, 0.254717f, 1.0f);
DarkTabHover = new Color(0.277f, 0.277f, 0.277f, 1.0f);
DarkTabSelected = new Color(0.312f, 0.312f, 0.312f, 1.0f);
float scalar = 0.925f;
LightTab = new FancyColor(0.85f * scalar);
LightTabHover = new FancyColor(0.915f * scalar);
LightTabSelected = new FancyColor(0.95f * scalar);
DarkCheckerboard = new Color(0.298f, 0.298f, 0.298f, 1.0f);
DarkCheckerboardBorder = new Color(0, 0, 0, 0.5529412f);
DefaultTextStyle = new GUIStyle(SirenixGUIStyles.MultiLineCenteredLabel);
DefaultTextStyle.focused.textColor = DefaultTextStyle.normal.textColor;
DarkMenuBackground = DarkPanel;
LightMenuBackground = new FancyColor(0.84f);
}
public static string TextField(Rect position, string text, out bool changed, int id)
{
if (id == 0)
{
GUI.Label(position, text, SirenixGUIStyles.MultiLineCenteredLabel);
changed = false;
return text;
}
Color lastCursorColor = GUI.skin.settings.cursorColor;
bool isMouseDown = Event.current.type == EventType.MouseDown;
if (isMouseDown)
{
GUI.skin.settings.cursorColor = Color.clear;
}
text = EditorGUI_Internals.DoTextField(id, position, text, DefaultTextStyle, null, out changed, false, true, false);
if (isMouseDown)
{
GUI.skin.settings.cursorColor = lastCursorColor;
}
return text;
}
public static string TextFieldSyntaxHighlighted(Rect position, string text, string syntaxRichText, out bool changed, int id)
{
if (string.IsNullOrEmpty(syntaxRichText))
{
return TextField(position, text, out changed, id);
}
GUIHelper.PushContentColor(Color.clear);
text = EditorGUI_Internals.DoTextField(id, position, text, SirenixGUIStyles.MultiLineCenteredLabel, null, out changed, false, true, false);
GUIHelper.PopContentColor();
GUI.Label(position, syntaxRichText, RichLabelTextCenteredMultiLine);
return text;
}
public static bool ObjectPickerButton(Rect position)
{
bool isMouseOver = Event.current.IsMouseOver(position);
var c1 = EditorGUIUtility.isProSkin
? new Color(1, 1, 1, isMouseOver ? 0.8f : 0.3f)
: new Color(0, 0, 0, isMouseOver ? 0.8f : 0.3f);
SdfIcons.DrawIcon(position, SdfIconType.StopCircle, c1);
return Event.current.OnMouseDown(position, 0);
}
public static TEnum Tabs<TEnum>(Rect rect, TEnum value, float width) where TEnum : Enum
{
EnumTypeUtilities<TEnum>.EnumMember[] infos = EnumTypeUtilities<TEnum>.VisibleEnumMemberInfos;
for (var i = 0; i < infos.Length; i++)
{
Rect itemRect = rect.TakeFromLeft(width);
bool isMouseOver = Event.current.IsMouseOver(itemRect);
bool isSelected = value.Equals(infos[i].Value);
bool isPressed = Event.current.OnMouseDown(itemRect, 0);
Color tabColor;
if (isSelected)
{
tabColor = TabSelected;
}
else if (isMouseOver)
{
tabColor = TabHover;
}
else
{
tabColor = Tab;
}
SirenixEditorGUI.DrawSolidRect(itemRect, tabColor);
Vector2 textSize = EditorStyles.label.CalcSize(GUIHelper.TempContent(infos[i].NiceName));
float iconSize = textSize.y;
float size = iconSize + 2.0f + textSize.x + 1.0f;
if (size > itemRect.width)
{
if (iconSize > itemRect.width)
{
continue;
}
SdfIcons.DrawIcon(itemRect.AlignCenter(iconSize), infos[i].Icon);
}
else
{
Rect contentRect = itemRect.AlignCenter(size);
Rect iconRect = contentRect.TakeFromLeft(iconSize).AlignMiddle(iconSize);
SdfIcons.DrawIcon(iconRect, infos[i].Icon);
contentRect.TakeFromLeft(2);
GUI.Label(contentRect, infos[i].NiceName);
}
if (isPressed)
{
value = infos[i].Value;
}
}
return value;
}
public static bool OverlaidButton(Rect position, string text, SdfIconType icon = SdfIconType.None, GUIStyle labelStyle = null, bool invert = false)
{
const float ICON_SIZE = 18;
const float SPACE = 4;
bool isMouseOver = Event.current.IsMouseOver(position);
ref SirenixAnimationUtility.InterpolatedFloat t = ref SirenixAnimationUtility.GetTemporaryFloat(position, isMouseOver ? 1.0f : 0.0f);
t.ChangeDestination(isMouseOver ? 1.0f : 0.0f);
t.Move(1.0f / 0.15f, Easing.OutQuad);
float value = t.GetValue();
float inverseValue = 1.0f - value;
if (invert)
{
SirenixEditorGUI.DrawRoundRect(position, new Color(0, 0, 0, 0.2f * inverseValue), 5.0f, new Color(0, 0, 0, 0.05f * inverseValue), 1);
SirenixEditorGUI.DrawRoundRect(position, new Color(0, 0, 0, 0.4f * value), 5.0f, new Color(1.0f, 1.0f, 1.0f, 0.05f * value), 1);
}
else
{
SirenixEditorGUI.DrawRoundRect(position, new Color(1, 1, 1, 0.2f * inverseValue), 5.0f, new Color(0, 0, 0, 0.05f * inverseValue), 1);
SirenixEditorGUI.DrawRoundRect(position, new Color(1, 1, 1, 0.4f * value), 5.0f, new Color(1.0f, 1.0f, 1.0f, 0.05f * value), 1);
}
labelStyle = labelStyle ?? SirenixGUIStyles.WhiteLabelCentered;
if (icon == SdfIconType.None)
{
GUI.Label(position, text, labelStyle);
}
else
{
float textWidth = SirenixGUIStyles.WhiteLabelCentered.CalcWidth(text);
Rect contentPosition = position.AlignCenter(ICON_SIZE + SPACE + textWidth);
SdfIcons.DrawIcon(contentPosition.AlignLeft(ICON_SIZE), icon, Color.white);
GUI.Label(contentPosition.AlignRight(textWidth), text, labelStyle);
}
return Event.current.OnMouseDown(position, 0);
}
public static bool Metadata(Type metadataType, bool isFirst)
{
var isRemoved = false;
if (isFirst)
{
EditorGUI.DrawRect(GUILayoutUtility.GetRect(0, 1), new FancyColor(0.06603771f));
}
Vector2 padding = new Vector2(4, 4);
Rect rect = GUILayoutUtility.GetRect(0, 24);
if (Event.current.IsMouseOver(rect))
{
EditorGUI.DrawRect(rect, new FancyColor(0.2784314f));
}
else
{
EditorGUI.DrawRect(rect, new FancyColor(0.2431373f));
}
EditorGUI.DrawRect(GUILayoutUtility.GetRect(0, 1), new FancyColor(0.06603771f));
var iconWidth = 16.0f;
var iconPadding = new Vector2(2, 2);
SdfIcons.DrawIcon(rect.TakeFromLeft(iconWidth).Padding(iconPadding.x, iconPadding.y), SdfIconType.GripVertical);
rect = rect.Padding(padding.x, 0);
SdfIcons.DrawIcon(rect.TakeFromLeft(iconWidth).Padding(iconPadding.x, iconPadding.y), SdfIconType.Braces);
rect.TakeFromLeft(5);
GUI.Label(rect, metadataType.GetNiceName(), SirenixGUIStyles.BoldLabel);
var removeRect = rect.TakeFromRight(iconWidth).Padding(iconPadding.x, iconPadding.y);
SdfIcons.DrawIcon(removeRect, SdfIconType.X);
if (GUI.Button(removeRect, GUIContent.none, GUIStyle.none))
{
isRemoved = true;
}
return isRemoved;
}
public static void DrawRoundBlur20(Rect position, Color color)
{
position = position.Expand(63);
SirenixEditorGUI.DrawTextureSliced(position, OdinLocalizationGUITextures.RoundBlur20, color, 63);
}
public static void DrawRoundBlur6(Rect position, Color color)
{
position = position.Expand(10);
SirenixEditorGUI.DrawTextureSliced(position, OdinLocalizationGUITextures.RoundBlur6, color, 10);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12ea4fb9c8db4471801a31e2e0b353de
timeCreated: 1704803170

View File

@@ -0,0 +1,310 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationGUITextures.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using Sirenix.Utilities.Editor;
using UnityEngine;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationGUITextures
{
private const float FADE_STRENGTH = 0.4f;
public static Texture2D LeftToRightFade
{
get
{
if (leftToRightFade != null)
{
return leftToRightFade;
}
leftToRightFade = new Texture2D(32, 32)
{
hideFlags = HideFlags.HideAndDontSave
};
var pixels = new Color[32 * 32];
var index = 0;
for (var x = 0; x < 32; x++)
{
for (var y = 0; y < 32; y++)
{
pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(y / 31.0f, FADE_STRENGTH));
}
}
leftToRightFade.SetPixels(pixels);
leftToRightFade.Apply();
CleanupUtility.DestroyObjectOnAssemblyReload(leftToRightFade);
return leftToRightFade;
}
}
public static Texture2D TopToBottomFade
{
get
{
if (topToBottomFade != null)
{
return topToBottomFade;
}
topToBottomFade = new Texture2D(32, 32)
{
hideFlags = HideFlags.HideAndDontSave
};
var pixels = new Color[32 * 32];
var index = 0;
for (var x = 0; x < 32; x++)
{
for (var y = 0; y < 32; y++)
{
pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(1.0f - x / 31.0f, FADE_STRENGTH));
}
}
topToBottomFade.SetPixels(pixels);
topToBottomFade.Apply();
CleanupUtility.DestroyObjectOnAssemblyReload(topToBottomFade);
return topToBottomFade;
}
}
public static Texture2D BottomToTopFade
{
get
{
if (bottomToTopFade != null)
{
return bottomToTopFade;
}
bottomToTopFade = new Texture2D(32, 32)
{
hideFlags = HideFlags.HideAndDontSave
};
var pixels = new Color[32 * 32];
var index = 0;
for (var x = 0; x < 32; x++)
{
for (var y = 0; y < 32; y++)
{
pixels[index++] = Color.Lerp(Color.white, Color.clear, Mathf.Pow(x / 31.0f, FADE_STRENGTH));
}
}
bottomToTopFade.SetPixels(pixels);
bottomToTopFade.Apply();
CleanupUtility.DestroyObjectOnAssemblyReload(bottomToTopFade);
return bottomToTopFade;
}
}
public static Texture2D RoundBlur6
{
get
{
if (roundBlur6 == null)
{
const string BASE64 =
"iVBORw0KGgoAAAANSUhEUgAAAGYAAABmCAYAAAA53+RiAAAFDElEQVR4Ae2dzZIURRSFaQFBDGBBsHGB76Ju0BfxkXwRZaXvIgs3BhEiIT/DYHu+pG+R3XN6mDa6uZuTEacr61ZWVcb3kVXFaq5dSwuBELg6gVUN" +
"Xa/X1a1tHWO7G8bU8RqfrSdQYNnuhjPq+Dh7tXqP9cbY2/4p4J+pTK5PqRrbGqdu2iUEAP/vlHfqV6rO6VuCdsUAm5SQm+p/vgl9wjklSN3RIqlIvN/OkAv+uQ693eRMW8I+khhDW87bFcNBoFO/pdxW7ihfbML+" +
"LCdCBOSSVqulpLzW2FebvNSW/TcKxxG0tFnMvFKQgpC7yn3l3qZPreTkcSYYH2klhpWBBGS8UJ4r/AOHIY1xNZbtWBlsqzGQE4CPlAfKQ+Vb5XvlkcKjLe1wAjy6nio/K78pcKbxGKvHWT3SPrzA9VWGFKCzKlgl" +
"SPlK+VF5rKQdj8ATXeon5Q/lmcIKYjWd6atsyKmlpNqQxBcYcninIOcbJVIE4cgNpjyFYAxrmMN+eWc7MSwxBvMo+0FJOw0BXg0whjXMLxWDqHrH8Eh7pKSdhgBsYWw/pnZXDPt8qZUclljaaQjAtqTAHPb2Ucbt" +
"OTjLoZZ2OgIsgJIC96XNO9iq/8uUoGVgOichUJzZFv9xo1nMfOetQfOB9I9KYC/nfWKOevdc7HACTgwW0z4tgQvMnZhPO6XczRKIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR0+/AziBiLJb+YsT0O7AziBiL" +
"pb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMIsZi6S9GTL8DO4OIsVj6ixHT78DOIGIslv5ixPQ7sDOIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR0+/AziBiLJb+" +
"YsT0O7AziBiLpb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMIsZi6S9GTL8DO4OIsVj6ixHT78DOIGIslv5ixPQ7sDOIGIulvxgx/Q7sDCLGYukvRky/AzuDiLFY+osR" +
"0+/AziBiLJb+YsT0O7AziBiLpb8YMf0O7AwixmLpL0ZMvwM7g4ixWPqLEdPvwM4gYiyW/mLE9DuwM4gYi6W/GDH9DuwMnJjxZ8vt6BRPReACcyfmVDfPdQ8gsE8MBi9YPOC6GXo1Ans5z2JqEH9PvnK1y2fU/yVQ" +
"nNkW/3GtWQyFGniu/tsxIj+nJABjWBf35V6zGIwxoKS8Vv9sGZnOsQnAFsazHByM5sQwkBNeKk/HqPycggBsYVxy6nE27rUr5p2qiHmlvFB+UdJOQwC2MIY1zGG/d8VwkCXG4L+VX5UnStpxCcAUtjCGNcy3xCx/" +
"fHm9XtO/odxSvlTuKw+Uh8p3ymPla+WmknY4AVbF7worBSl/Ks+U58o/yhvlfLVajVWzK4ZHG3JuK3eUuwqC7m361DiGHMYu56ufdpEAkHl3zO9tHl+sFITQr/fM+DorMUioVhdhSWGPVhfF6F9KSeG8iBGEj7Ri" +
"On/p8ugiJQTWMN96+c9idGw0BnAhLsoJ9THAKiElZf5wyMoRmKnBrho8iyksCe8UQr+kqPuhLUD1jqlq1QBPrk+pGtsap27aJQRq1ZQgRFSqxulDgB5l41IL3EnMOKCfOsZ2N4yp4/TT9hOof/Fsd8NZdXxcocSM" +
"nfyEQAhckcB/Z2rwteNGzXsAAAAASUVORK5CYII=";
byte[] bytes = Convert.FromBase64String(BASE64);
roundBlur6 = TextureUtilities.LoadImage(102, 102, bytes);
roundBlur6.hideFlags = HideFlags.HideAndDontSave;
CleanupUtility.DestroyObjectOnAssemblyReload(roundBlur6);
}
return roundBlur6;
}
}
public static Texture2D RoundBlur20
{
get
{
if (roundBlur20 == null)
{
const string BASE64 =
"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";
byte[] bytes = Convert.FromBase64String(BASE64);
roundBlur20 = TextureUtilities.LoadImage(193, 193, bytes);
roundBlur20.hideFlags = HideFlags.HideAndDontSave;
CleanupUtility.DestroyObjectOnAssemblyReload(roundBlur20);
}
return roundBlur20;
}
}
private static Texture2D leftToRightFade;
private static Texture2D topToBottomFade;
private static Texture2D bottomToTopFade;
private static Texture2D roundBlur6;
private static Texture2D roundBlur20;
public static void Clear()
{
if (leftToRightFade != null)
{
UnityEngine.Object.Destroy(leftToRightFade);
}
if (topToBottomFade != null)
{
UnityEngine.Object.Destroy(topToBottomFade);
}
if (bottomToTopFade != null)
{
UnityEngine.Object.Destroy(bottomToTopFade);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 78842ff15d254d9b8994383880b759f1
timeCreated: 1704903394

View File

@@ -0,0 +1,55 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationMetadataRegistry.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine.Localization.Metadata;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public class OdinLocalizationMetadataRegistry
{
public static readonly List<Type> AssetEntryMetadataTypes = new List<Type>();
public static readonly List<Type> StringEntryMetadataTypes = new List<Type>();
public static readonly Dictionary<Type, bool> MetadataAllowsMultiple = new Dictionary<Type, bool>();
static OdinLocalizationMetadataRegistry()
{
TypeCache.TypeCollection metadataTypes = TypeCache.GetTypesDerivedFrom(typeof(IMetadata));
for (var i = 0; i < metadataTypes.Count; i++)
{
Type currentType = metadataTypes[i];
var attr = currentType.GetCustomAttribute<MetadataAttribute>();
if (attr is null)
{
MetadataAllowsMultiple[currentType] = true;
continue;
}
MetadataAllowsMultiple[currentType] = attr.AllowMultiple;
MetadataType currentAllowedTypes = attr.AllowedTypes;
if (currentAllowedTypes.HasFlag(MetadataType.StringTableEntry))
{
StringEntryMetadataTypes.Add(currentType);
}
if (currentAllowedTypes.HasFlag(MetadataType.AssetTableEntry))
{
AssetEntryMetadataTypes.Add(currentType);
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7deb6e0b5f6c4579ba2ead2af5620d3e
timeCreated: 1706509329

View File

@@ -0,0 +1,43 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationReflectionValidator.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if SIRENIX_INTERNAL
using System.Collections;
using System.Reflection;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
[assembly: RegisterValidator(typeof(OdinLocalizationReflectionValidator))]
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public class OdinLocalizationReflectionValidator : GlobalValidator
{
public override IEnumerable RunValidation(ValidationResult result)
{
OdinLocalizationReflectionValues.EnsureInit();
FieldInfo[] fields = typeof(OdinLocalizationReflectionValues).GetFields(BindingFlags.Static | BindingFlags.Public);
for (var i = 0; i < fields.Length; i++)
{
if (fields[i].IsLiteral)
{
continue;
}
if (fields[i].GetValue(null) != null)
{
continue;
}
result.AddError($"[Odin Localization Module]: {fields[i].Name} was not found.");
}
return null;
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c12d37dc91fe4285bd661d45717bf34a
timeCreated: 1704797628

View File

@@ -0,0 +1,174 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationReflectionValues.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Reflection;
using Sirenix.Serialization;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.Localization;
using UnityEngine.Localization;
using UnityEngine.Localization.Metadata;
using UnityEngine.Localization.Tables;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationReflectionValues
{
public const string TABLE_ENTRY_DATA__METADATA__PATH = "m_Metadata";
public const string METADATA_COLLECTION__ITEMS__PATH = "m_Items";
public const string TABLE_ENTRY__DATA__PATH = "Data";
public static readonly bool HasAPIForCustomUndo = false;
public static readonly PropertyInfo TableEntry_Data_Property;
public static readonly PropertyInfo AssetTableEntry_Data_Property;
public static readonly PropertyInfo StringTableEntry_Data_Property;
public static readonly FieldInfo TableEntryData_m_Metadata_Field;
public static readonly FieldInfo MetadataCollection_m_Items_Field;
public static readonly PropertyInfo LocalizationEditorSettings_Instance;
public static readonly MethodInfo LocalizationEditorSettings_GetAddressableAssetSettings;
public static readonly MethodInfo LocalizationEditorSettings_IsTableNameValid;
public static readonly MethodInfo RaiseTableEntryModified_Method;
public static readonly MethodInfo RaiseTableEntryAdded_Method;
public static readonly MethodInfo RaiseTableEntryRemoved_Method;
public static readonly MethodInfo RaiseAssetTableEntryAdded_Method;
public static readonly MethodInfo RaiseAssetTableEntryRemoved_Method;
public static readonly MethodInfo AssetTableCollection_SetEntryAssetType_PrivateMethod;
public static readonly MethodInfo AssetTableCollection_RemoveEntryAssetType_PrivateMethod;
public static readonly Type AddressHelper;
public static readonly MethodInfo FormatAssetLabelMethod;
public static readonly MethodInfo UpdateAssetGroupMethod;
private const BindingFlags INSTANCE_ALL = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags INSTANCE_NON_PUBLIC = BindingFlags.Instance | BindingFlags.NonPublic;
private const BindingFlags STATIC_NON_PUBLIC = BindingFlags.Static | BindingFlags.NonPublic;
internal static Func<bool, AddressableAssetSettings> LocalizationEditorSettings_GetAddressableAssetSettingsFunc;
static OdinLocalizationReflectionValues()
{
TableEntry_Data_Property = typeof(TableEntry).GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL);
AssetTableEntry_Data_Property = typeof(AssetTableEntry).BaseType.GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL);
StringTableEntry_Data_Property = typeof(StringTableEntry).BaseType.GetProperty(TABLE_ENTRY__DATA__PATH, INSTANCE_ALL);
Type tableEntryDataType = TwoWaySerializationBinder.Default.BindToType("UnityEngine.Localization.Tables.TableEntryData, Unity.Localization");
TableEntryData_m_Metadata_Field = tableEntryDataType?.GetField(TABLE_ENTRY_DATA__METADATA__PATH, INSTANCE_ALL);
MetadataCollection_m_Items_Field = typeof(MetadataCollection).GetField(METADATA_COLLECTION__ITEMS__PATH, INSTANCE_ALL);
LocalizationEditorSettings_Instance = typeof(LocalizationEditorSettings).GetProperty("Instance", STATIC_NON_PUBLIC);
LocalizationEditorSettings_GetAddressableAssetSettings = typeof(LocalizationEditorSettings).GetMethod("GetAddressableAssetSettings",
INSTANCE_NON_PUBLIC);
LocalizationEditorSettings_IsTableNameValid = typeof(LocalizationEditorSettings).GetMethod("IsTableNameValid", INSTANCE_NON_PUBLIC);
RaiseTableEntryModified_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryModified", INSTANCE_ALL);
RaiseTableEntryAdded_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryAdded", INSTANCE_ALL);
RaiseTableEntryRemoved_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseTableEntryRemoved", INSTANCE_ALL);
RaiseAssetTableEntryAdded_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseAssetTableEntryAdded", INSTANCE_ALL);
RaiseAssetTableEntryRemoved_Method = LocalizationEditorSettings.EditorEvents.GetType().GetMethod("RaiseAssetTableEntryRemoved", INSTANCE_ALL);
AssetTableCollection_SetEntryAssetType_PrivateMethod = typeof(AssetTableCollection).GetMethod("SetEntryAssetType", INSTANCE_NON_PUBLIC);
AssetTableCollection_RemoveEntryAssetType_PrivateMethod = typeof(AssetTableCollection).GetMethod("RemoveEntryAssetType", INSTANCE_NON_PUBLIC);
if (LocalizationEditorSettings_Instance != null && LocalizationEditorSettings_GetAddressableAssetSettings != null)
{
LocalizationEditorSettings_GetAddressableAssetSettingsFunc =
(Func<bool, AddressableAssetSettings>) Delegate.CreateDelegate(typeof(Func<bool, AddressableAssetSettings>),
LocalizationEditorSettings_Instance.GetValue(null),
LocalizationEditorSettings_GetAddressableAssetSettings);
}
AddressHelper = TwoWaySerializationBinder.Default.BindToType("UnityEngine.Localization.AddressHelper");
FormatAssetLabelMethod = AddressHelper?.GetMethod("FormatAssetLabel",
BindingFlags.Static | BindingFlags.Public,
null,
new[] {typeof(LocaleIdentifier)},
null);
UpdateAssetGroupMethod = typeof(AssetTableCollection).GetMethod("UpdateAssetGroup",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[]
{
typeof(AddressableAssetSettings),
typeof(AddressableAssetEntry),
typeof(bool)
},
null);
HasAPIForCustomUndo = AddressHelper != null &&
FormatAssetLabelMethod != null &&
UpdateAssetGroupMethod != null &&
LocalizationEditorSettings_GetAddressableAssetSettingsFunc != null;
}
public static Action<SharedTableData.SharedTableEntry> Create_RaiseTableEntryModified_Method_Delegate(LocalizationEditorEvents events)
{
return (Action<SharedTableData.SharedTableEntry>) Delegate.CreateDelegate(typeof(Action<SharedTableData.SharedTableEntry>),
events,
RaiseTableEntryModified_Method);
}
public static Action<LocalizationTableCollection, SharedTableData.SharedTableEntry> Create_RaiseTableEntryAdded_Method_Delegate(
LocalizationEditorEvents events)
{
return (Action<LocalizationTableCollection, SharedTableData.SharedTableEntry>)
Delegate.CreateDelegate(typeof(Action<LocalizationTableCollection, SharedTableData.SharedTableEntry>),
events,
RaiseTableEntryAdded_Method);
}
public static Action<LocalizationTableCollection, SharedTableData.SharedTableEntry> Create_RaiseTableEntryRemoved_Method_Delegate(
LocalizationEditorEvents events)
{
return (Action<LocalizationTableCollection, SharedTableData.SharedTableEntry>)
Delegate.CreateDelegate(typeof(Action<LocalizationTableCollection, SharedTableData.SharedTableEntry>),
events,
RaiseTableEntryRemoved_Method);
}
public static Action<AssetTableCollection, AssetTable, AssetTableEntry> Create_AssetTableEntryAdded_Method_Delegate(
LocalizationEditorEvents events)
{
return (Action<AssetTableCollection, AssetTable, AssetTableEntry>)
Delegate.CreateDelegate(typeof(Action<AssetTableCollection, AssetTable, AssetTableEntry>),
events,
RaiseAssetTableEntryAdded_Method);
}
public static Action<AssetTableCollection, AssetTable, AssetTableEntry, string> Create_AssetTableEntryRemoved_Method_Delegate(
LocalizationEditorEvents events)
{
return (Action<AssetTableCollection, AssetTable, AssetTableEntry, string>)
Delegate.CreateDelegate(typeof(Action<AssetTableCollection, AssetTable, AssetTableEntry, string>),
events,
RaiseAssetTableEntryRemoved_Method);
}
public static Action<TableEntryReference, Type, string> Create_AssetTableCollection_SetEntryAssetType_PrivateMethod_Delegate(AssetTableCollection collection)
{
return (Action<TableEntryReference, Type, string>) Delegate.CreateDelegate(typeof(Action<TableEntryReference, Type, string>),
collection,
AssetTableCollection_SetEntryAssetType_PrivateMethod);
}
public static Action<TableEntryReference, string> Create_AssetTableCollection_RemoveEntryAssetType_PrivateMethod_Delegate(AssetTableCollection collection)
{
return (Action<TableEntryReference, string>) Delegate.CreateDelegate(typeof(Action<TableEntryReference, string>),
collection,
AssetTableCollection_RemoveEntryAssetType_PrivateMethod);
}
#if SIRENIX_INTERNAL
public static void EnsureInit() { }
#endif
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b54903c48e344164a8ef89c56ba21f22
timeCreated: 1704797678

View File

@@ -0,0 +1,223 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationSyntaxHighlighter.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Text;
using Sirenix.OdinInspector.Modules.Localization.Editor.Configs;
using Sirenix.Utilities;
using UnityEditor;
using UnityEngine;
using UnityEngine.Localization.SmartFormat;
using UnityEngine.Localization.SmartFormat.Core.Parsing;
using UnityEngine.Localization.SmartFormat.Core.Settings;
namespace Sirenix.OdinInspector.Modules.Localization.Editor.Internal
{
public static class OdinLocalizationSyntaxHighlighter
{
private const string END_RICH_COLOR = "</color>";
private static readonly SmartFormatter Formatter;
private static readonly StringBuilder Buffer;
// TODO: if this works nicely, maybe implement a custom solution that doesn't rely on SmartFormatter
static OdinLocalizationSyntaxHighlighter()
{
Formatter = Smart.CreateDefaultSmartFormat();
Formatter.Settings.ParseErrorAction = ErrorAction.MaintainTokens;
Formatter.Settings.FormatErrorAction = ErrorAction.MaintainTokens;
Buffer = new StringBuilder();
}
public static string HighlightAsRichText(string source)
{
Format format;
try
{
format = Formatter.Parser.ParseFormat(source, Formatter.GetNotEmptyFormatterExtensionNames());
}
catch (Exception)
{
return source;
}
Buffer.Clear();
int expectedSize = source.Length;
AppendToBuffer(format, source, ref expectedSize);
// NOTE: we fallback to source in-case any obvious discrepancies are happening.
return Buffer.Length != expectedSize ? source : Buffer.ToString();
}
public static string GetErrorMessage(string source, out bool foundError, out Exception exception)
{
Formatter.Settings.ParseErrorAction = ErrorAction.OutputErrorInResult;
Formatter.Settings.FormatErrorAction = ErrorAction.OutputErrorInResult;
Format format;
try
{
format = Formatter.Parser.ParseFormat(source, Formatter.GetNotEmptyFormatterExtensionNames());
}
catch (Exception e)
{
foundError = true;
exception = e;
Debug.LogException(e);
return $"Unity Formatter threw {ObjectNames.NicifyVariableName(e.GetType().GetNiceName())}: '{e.Message}'\nCheck the console for more information.";
}
exception = null;
Buffer.Clear();
int expectedSize = source.Length;
AppendToBuffer(format, source, ref expectedSize);
foundError = expectedSize != Buffer.Length;
Formatter.Settings.ParseErrorAction = ErrorAction.MaintainTokens;
Formatter.Settings.FormatErrorAction = ErrorAction.MaintainTokens;
return Buffer.ToString();
}
private static void AppendColorAsRichTag(Color color, ref int expectedSize)
{
var color32 = (Color32) color;
var richTag = $"<color=#{color32.r:X2}{color32.g:X2}{color32.b:X2}>";
expectedSize += richTag.Length;
Buffer.Append(richTag);
}
private static bool IsLastBufferEqual(string expected)
{
if (expected.Length > Buffer.Length)
{
return false;
}
int bufferIndex = Buffer.Length - 1;
for (int i1 = expected.Length - 1; i1 >= 0; i1--, bufferIndex--)
{
if (Buffer[bufferIndex] != expected[i1])
{
return false;
}
}
return true;
}
private static void AppendToBuffer(Format format, string source, ref int expectedSize)
{
for (var i = 0; i < format.Items.Count; i++)
{
if (Buffer.Length == source.Length)
{
break;
}
FormatItem item = format.Items[i];
if (!(item is Placeholder placeholder))
{
Buffer.Append(item.RawText);
continue;
}
AppendColorAsRichTag(OdinLocalizationConfig.Instance.placeholderColor, ref expectedSize);
Buffer.Append('{');
for (var j = 0; j < placeholder.Selectors.Count; j++)
{
Selector selector = placeholder.Selectors[j];
AppendColorAsRichTag(OdinLocalizationConfig.Instance.selectorColor, ref expectedSize);
string op = selector.Operator;
if (!string.IsNullOrEmpty(op))
{
Buffer.Append(selector.Operator);
}
Buffer.Append(selector.RawText);
Buffer.Append(END_RICH_COLOR);
expectedSize += END_RICH_COLOR.Length;
}
if (placeholder.Alignment != 0)
{
Buffer.Append(',');
Buffer.Append(placeholder.Alignment);
}
if (!string.IsNullOrEmpty(placeholder.FormatterName))
{
Buffer.Append(':');
AppendColorAsRichTag(OdinLocalizationConfig.Instance.formatterColor, ref expectedSize);
Buffer.Append(placeholder.FormatterName);
if (!string.IsNullOrEmpty(placeholder.FormatterOptions))
{
Buffer.Append('(');
Buffer.Append(placeholder.FormatterOptions);
Buffer.Append(')');
}
Buffer.Append(END_RICH_COLOR);
expectedSize += END_RICH_COLOR.Length;
}
if (placeholder.Format != null)
{
Format nextFormat = placeholder.Format;
bool showColon;
if (nextFormat.startIndex > 0)
{
showColon = nextFormat.baseString[nextFormat.startIndex - 1] == ':';
}
else
{
showColon = true;
}
if (showColon)
{
Buffer.Append(':');
}
AppendToBuffer(nextFormat, source, ref expectedSize);
}
Buffer.Append('}');
Buffer.Append(END_RICH_COLOR);
expectedSize += END_RICH_COLOR.Length;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: edfba57bc4284142a39f658cc44f70c6
timeCreated: 1704797761

View File

@@ -0,0 +1,39 @@
//-----------------------------------------------------------------------
// <copyright file="LocalizationSupport.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
#if UNITY_EDITOR
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
using UnityEngine.Localization;
using Sirenix.OdinInspector.Editor;
using System;
using Sirenix.Utilities.Editor;
public class LocalizedReferenceResolver : OdinPropertyResolver<LocalizedReference>
{
public override int ChildNameToIndex(string name)
{
throw new NotSupportedException();
}
public override int ChildNameToIndex(ref StringSlice name)
{
throw new NotSupportedException();
}
public override InspectorPropertyInfo GetChildInfo(int childIndex)
{
throw new NotSupportedException();
}
protected override int GetChildCount(LocalizedReference value)
{
return 0;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46db7b2a049564544ad19a2e480a1333
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,25 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationConstants.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using Sirenix.OdinInspector.Modules.Localization.Editor.Configs;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public static class OdinLocalizationConstants
{
public static int AssetPreviewRowHeight => OdinLocalizationConfig.Instance.assetRowHeight;
public const int DEFAULT_COLUMN_WIDTH = 300;
public const int MIN_COLUMN_WIDTH = 200;
public const int COLUMN_HEIGHT = 38;
public const int ROW_MENU_WIDTH = 28;
public const int ROW_HEIGHT = 30;
public const int EMPTY_ASSET_ROW_HEIGHT = ROW_HEIGHT; //96;
public const int ASSET_ROW_HEIGHT = 104; //96;
public const int ENTRY_PADDING = 4;
public const int TOOLBAR_HEIGHT = 28;
public const int DRAG_HANDLE_WIDTH = 27;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cd2728f4c9e842db8d6b5e8c18cce611
timeCreated: 1706411619

View File

@@ -0,0 +1,29 @@
{
"name": "Sirenix.OdinInspector.Modules.UnityLocalization.Editor",
"references": [
"Unity.Localization",
"Sirenix.Serialization",
"Sirenix.OdinInspector.Attributes",
"Sirenix.OdinInspector.Editor",
"Sirenix.Utilities.Editor",
"Unity.Localization.Editor",
"Sirenix.Utilities",
"Unity.Addressables.Editor"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [
"Sirenix.Serialization.dll",
"Sirenix.OdinInspector.Attributes.dll",
"Sirenix.OdinInspector.Editor.dll",
"Sirenix.Utilities.Editor.dll"
],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3656feb4b010c5941972fb7b3b9eec3e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 835e118525d8475f939f4105af066578
timeCreated: 1704796852

View File

@@ -0,0 +1,663 @@
//-----------------------------------------------------------------------
// <copyright file="OdinLocalizationEditorWindow.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Internal;
using Sirenix.OdinInspector.Modules.Localization.Editor.Configs;
using Sirenix.OdinInspector.Modules.Localization.Editor.Internal;
using Sirenix.Serialization;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEditor.Localization;
using UnityEngine;
using UnityEngine.Localization;
namespace Sirenix.OdinInspector.Modules.Localization.Editor
{
public class OdinLocalizationCreateTableMenu
{
[Serializable]
public class LocaleItem
{
[HideInInspector]
public Locale Locale;
[HideLabel]
public bool Enabled;
}
public enum TableCollectionType
{
StringTableCollection,
AssetTableCollection
}
private string FolderPath => string.IsNullOrEmpty(this.Folder) ? "Assets" : $"Assets/{this.Folder}";
[ValidateInput(nameof(ValidateName), "@this." + nameof(nameErrorMessage))]
[VerticalGroup("Split/Left")]
[PropertySpace(SpaceAfter = 2, SpaceBefore = 2)]
public string Name;
[EnableIf("@this." + nameof(EnableFolder))]
[InfoBox("The directory is not found, this will create a new directory on creation.", visibleIfMemberName: nameof(ShowFolderInfoBox))]
[HorizontalGroup("Split")]
[VerticalGroup("Split/Left")]
[FolderPath(ParentFolder = "Assets")]
public string Folder;
[VerticalGroup("Split/Left")]
[PropertySpace(SpaceAfter = 2, SpaceBefore = 2)]
[HideLabel]
[EnumToggleButtons]
public TableCollectionType Type;
[EnableIf(nameof(EnableCreateIf))]
[VerticalGroup("Split/Left")]
[PropertySpace(SpaceBefore = 4)]
[Button(ButtonSizes.Large)]
public void Create()
{
var localizationWindow = EditorWindow.focusedWindow as OdinLocalizationEditorWindow;
if (!this.HasAnyLocaleSelected())
{
if (localizationWindow)
{
localizationWindow.ShowToast(ToastPosition.BottomLeft,
SdfIconType.ExclamationOctagonFill,
"At least 1 Locale must be selected.",
new Color(0.68f, 0.2f, 0.2f),
5.0f);
}
return;
}
if (!Directory.Exists(this.FolderPath))
{
Directory.CreateDirectory(this.FolderPath);
}
var collectionLocales = new List<Locale>(this.Locales.Count);
foreach (LocaleItem localeItem in this.Locales)
{
if (localeItem.Enabled)
{
collectionLocales.Add(localeItem.Locale);
}
}
switch (this.Type)
{
case TableCollectionType.StringTableCollection:
LocalizationEditorSettings.CreateStringTableCollection(this.Name, this.FolderPath, collectionLocales);
break;
case TableCollectionType.AssetTableCollection:
LocalizationEditorSettings.CreateAssetTableCollection(this.Name, this.FolderPath, collectionLocales);
break;
default:
throw new ArgumentOutOfRangeException();
}
if (localizationWindow)
{
string typeNiceName;
switch (this.Type)
{
case TableCollectionType.StringTableCollection:
typeNiceName = "String Table Collection";
break;
case TableCollectionType.AssetTableCollection:
typeNiceName = "Asset Table Collection";
break;
default:
throw new ArgumentOutOfRangeException();
}
localizationWindow.ShowToast(ToastPosition.BottomLeft,
SdfIconType.Check2,
$"{typeNiceName} '{this.Name}' created at: {this.FolderPath}.",
new Color(0.29f, 0.57f, 0.42f),
16.0f);
}
}
[HorizontalGroup("Split")]
[VerticalGroup("Split/Right")]
[InlineProperty]
[ListDrawerSettings(ListElementLabelName = "@this.Locale.LocaleName",
HideAddButton = true,
HideRemoveButton = true,
DefaultExpandedState = true,
ShowFoldout = false,
ShowItemCount = false,
DraggableItems = false)]
public List<LocaleItem> Locales = new List<LocaleItem>();
[HorizontalGroup("Split/Right/Split")]
[Button]
public void LocaleGenerator()
{
try
{
TwoWaySerializationBinder.Default.BindToType("UnityEditor.Localization.UI.LocaleGeneratorWindow, Unity.Localization.Editor")
.GetMethod("ShowWindow", BindingFlags.Static | BindingFlags.Public)
.Invoke(null, null);
}
catch (NullReferenceException nullReferenceException)
{
Debug.LogError($"[Odin]: Failed to find LocaleGeneratorWindow.ShowWindow.\n{nullReferenceException.Message}");
}
}
[HorizontalGroup("Split/Right/Split")]
[Button]
public void SelectNone()
{
for (var i = 0; i < this.Locales.Count; i++)
{
this.Locales[i].Enabled = false;
}
}
[HorizontalGroup("Split/Right/Split")]
[Button]
public void SelectAll()
{
for (var i = 0; i < this.Locales.Count; i++)
{
this.Locales[i].Enabled = true;
}
}
[HideInInspector]
internal bool EnableFolder = true;
private string nameErrorMessage = string.Empty;
private bool ValidateName(string name)
{
if (string.IsNullOrEmpty(name))
{
this.nameErrorMessage = $"{nameof(this.Name)} can't be empty.";
return false;
}
Type collectionType;
switch (this.Type)
{
case TableCollectionType.StringTableCollection:
collectionType = typeof(StringTableCollection);
break;
case TableCollectionType.AssetTableCollection:
collectionType = typeof(AssetTableCollection);
break;
default:
throw new ArgumentOutOfRangeException();
}
bool isTableNameValid = OdinLocalizationEditorSettings.IsTableNameValid(collectionType, name, out string localizationErrorMsg);
if (isTableNameValid)
{
return true;
}
this.nameErrorMessage = localizationErrorMsg;
return false;
}
private bool ShowFolderInfoBox()
{
if (string.IsNullOrEmpty(this.Folder))
{
return false;
}
return !Directory.Exists(this.FolderPath);
}
private bool EnableCreateIf() => this.Locales.Count > 0 && this.ValidateName(this.Name);
private bool HasAnyLocaleSelected()
{
for (var i = 0; i < this.Locales.Count; i++)
{
if (this.Locales[i].Enabled)
{
return true;
}
}
return false;
}
}
public class OdinLocalizationEditorWindow : OdinMenuEditorWindow, IDisposable
{
public enum RightMenuTopTabs
{
[LabelText(SdfIconType.Braces)]
Metadata,
[LabelText(SdfIconType.GearFill)]
Settings
}
public enum RightMenuBottomTabs
{
[LabelText(SdfIconType.FlagFill)]
Locale,
#if false
[LabelText(SdfIconType.BorderWidth)]
Template,
#endif
[LabelText(SdfIconType.GearFill)]
Settings
}
public class WindowState : IDisposable
{
public static string EditorPrefsKey = "OdinLocalizationEditorWindow_EditorPrefs";
public RightMenuTopTabs CurrentTopTab;
public RightMenuBottomTabs CurrentBottomTab;
public PropertyTree MetadataTree = null;
public bool ShowSharedMetadata = true;
public float LeftMenuWidth;
public float RightMenuWidth;
public float RightMenuTopPanelHeight;
public float LastOpenRightMenuWidth;
public void Save()
{
EditorPrefs.SetFloat($"{EditorPrefsKey}_LeftMenuWidth", this.LeftMenuWidth);
EditorPrefs.SetFloat($"{EditorPrefsKey}_RightMenuWidth", this.RightMenuWidth);
EditorPrefs.SetFloat($"{EditorPrefsKey}_RightMenuTopHeight", this.RightMenuTopPanelHeight);
EditorPrefs.SetFloat($"{EditorPrefsKey}_LastOpenRightMenuWidth", this.LastOpenRightMenuWidth);
}
public void Load()
{
this.LeftMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_LeftMenuWidth", 300);
this.RightMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_RightMenuWidth", 300);
this.RightMenuTopPanelHeight = EditorPrefs.GetFloat($"{EditorPrefsKey}_RightMenuTopHeight");
this.LastOpenRightMenuWidth = EditorPrefs.GetFloat($"{EditorPrefsKey}_LastOpenRightMenuWidth");
}
public void Dispose()
{
this.MetadataTree?.Dispose();
this.MetadataTree = null;
}
}
[MenuItem("Tools/Odin/Localization Editor", priority = 10_100)]
public static void OpenFromMenu()
{
var wnd = GetWindow<OdinLocalizationEditorWindow>();
wnd.MenuWidth = 300.0f;
}
public WindowState State;
private object lastSelection;
protected override void Initialize()
{
this.State = new WindowState();
this.State.Load();
}
protected override void OnDestroy()
{
base.OnDestroy();
this.DisposeActiveCollection();
this.State.Dispose();
}
protected override void OnDisable()
{
base.OnDisable();
this.State.Save();
this.DisposeActiveCollection();
this.State.Dispose();
}
protected override void OnImGUI()
{
if (LocalizationEditorSettings.ActiveLocalizationSettings == null)
{
Rect popupPosition = this.position.SetPosition(Vector2.zero).AlignCenter(360, 160);
if (EditorGUIUtility.isProSkin)
{
//OdinLocalizationGUI.DrawRoundGlowRect(popupPosition.Expand(54), FancyColor.CreateHex(0x323232));
OdinLocalizationGUI.DrawRoundBlur6(popupPosition, new Color(0, 0, 0, 0.025f));
SirenixEditorGUI.DrawRoundRect(popupPosition, FancyColor.CreateHex(0x383838), 5.0f);
}
else
{
OdinLocalizationGUI.DrawRoundBlur6(popupPosition, new Color(0, 0, 0, 0.02f));
SirenixEditorGUI.DrawRoundRect(popupPosition, new FancyColor(0.84f), 5.0f); //, new Color(0, 0, 0, 0.2f), 1);
SirenixEditorGUI.DrawRoundRect(popupPosition.AlignBottom(32 + 12 + 8 + 6), new Color(1, 1, 1, 0.2f), 0.0f, 0.0f, 5.0f, 5.0f);
}
popupPosition = popupPosition.Padding(12);
Rect buttonsArea = popupPosition.TakeFromBottom(32);
popupPosition.height -= 16;
GUIStyle labelStyle = EditorGUIUtility.isProSkin ? SirenixGUIStyles.WhiteLabelCentered : SirenixGUIStyles.BlackLabelCentered;
if (EditorGUIUtility.isProSkin)
{
GUI.Label(popupPosition, "No Localization Settings found in project.", labelStyle);
}
else
{
GUIHelper.PushColor(new Color(1, 1, 1, 0.75f));
GUI.Label(popupPosition, "No Localization Settings found in project.", SirenixGUIStyles.BlackLabelCentered);
}
if (OdinLocalizationGUI.OverlaidButton(buttonsArea.AlignCenter(120), "Create", labelStyle: labelStyle, invert: true))
{
if (OdinLocalizationEditorSettings.CreateDefaultLocalizationSettingsAsset())
{
this.ShowToast(ToastPosition.BottomLeft,
SdfIconType.GearWide,
"Default Localization Settings created.",
new Color(0.13f, 0.26f, 0.39f),
8.0f);
}
}
if (!EditorGUIUtility.isProSkin)
{
GUIHelper.PopColor();
}
this.Repaint();
return;
}
base.OnImGUI();
}
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree
{
Config =
{
AutoHandleKeyboardNavigation = false,
DrawSearchToolbar = true
},
DefaultMenuStyle =
{
Height = 28,
AlignTriangleLeft = true,
TrianglePadding = 0.0f
}
};
this.MenuBackgroundColor = OdinLocalizationGUI.MenuBackground;
if (LocalizationEditorSettings.ActiveLocalizationSettings == null)
{
return tree;
}
var createMenu = new OdinLocalizationCreateTableMenu();
tree.Add("Create Table", createMenu, SdfIconType.Plus);
tree.Add("User Config", OdinLocalizationConfig.Instance, SdfIconType.GearFill);
#if true
tree.Selection.SelectionChanged += type =>
{
switch (type)
{
case SelectionChangedType.ItemAdded:
if (this.lastSelection != null)
{
switch (this.lastSelection)
{
case OdinAssetTableCollectionEditor assetCollection:
{
assetCollection.DetachEvents();
break;
}
case OdinStringTableCollectionEditor stringCollection:
{
stringCollection.DetachEvents();
break;
}
}
this.State.MetadataTree?.Dispose();
this.State.MetadataTree = null;
}
switch (tree.Selection.SelectedValue)
{
case OdinAssetTableCollectionEditor assetCollection:
{
assetCollection.OnSelectInWindow();
if (assetCollection.SelectionType == OdinTableSelectionType.TableEntry && this.State.CurrentTopTab == RightMenuTopTabs.Metadata)
{
assetCollection.UpdateMetadataViewForEntry(assetCollection.CurrentSelectedEntry);
}
break;
}
case OdinStringTableCollectionEditor stringCollection:
{
stringCollection.OnSelectInWindow();
if (stringCollection.SelectionType == OdinTableSelectionType.TableEntry && this.State.CurrentTopTab == RightMenuTopTabs.Metadata)
{
stringCollection.UpdateMetadataViewForEntry(stringCollection.CurrentSelectedEntry);
}
break;
}
case OdinLocalizationCreateTableMenu createTableMenu:
createTableMenu.Locales.Clear();
foreach (Locale locale in LocalizationEditorSettings.GetLocales())
{
createTableMenu.Locales.Add(new OdinLocalizationCreateTableMenu.LocaleItem {Locale = locale, Enabled = true});
}
break;
}
this.lastSelection = this.MenuTree.Selection.SelectedValue;
break;
}
};
#endif
string[] collectionGUIDs = AssetDatabase.FindAssets($"t:{nameof(LocalizationTableCollection)}");
for (var i = 0; i < collectionGUIDs.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(collectionGUIDs[i]);
var collection = AssetDatabase.LoadAssetAtPath<LocalizationTableCollection>(assetPath);
AssetTableCollection assetTableCollection = LocalizationEditorSettings.GetAssetTableCollection(collection.TableCollectionNameReference);
if (assetTableCollection != null)
{
var guiCollection = new OdinAssetTableCollectionEditor(assetTableCollection, this, this.State);
assetPath = assetPath.Replace(".asset", string.Empty);
if (assetPath.StartsWith("Assets/"))
{
assetPath = assetPath.Remove(0, "Assets/".Length);
}
tree.Add(assetPath, guiCollection, SdfIconType.Table);
continue;
}
StringTableCollection stringTableCollection = LocalizationEditorSettings.GetStringTableCollection(collection.TableCollectionNameReference);
if (stringTableCollection != null)
{
var guiCollection = new OdinStringTableCollectionEditor(stringTableCollection, this, this.State);
assetPath = assetPath.Replace(".asset", string.Empty);
if (assetPath.StartsWith("Assets/"))
{
assetPath = assetPath.Remove(0, "Assets/".Length);
}
tree.Add(assetPath, guiCollection, SdfIconType.LayoutTextWindow);
continue;
}
}
foreach (OdinMenuItem treeMenuItem in tree.EnumerateTree())
{
if (treeMenuItem.Value != null)
{
if (treeMenuItem.Value is OdinAssetTableCollectionEditor assetEditor)
{
treeMenuItem.Name = assetEditor.Collection.SharedData.TableCollectionName;
assetEditor.MenuItem = treeMenuItem;
treeMenuItem.OnDrawItem += item =>
{
if (Event.current.OnMouseDown(item.Rect, 0, false))
{
if (Event.current.clickCount > 1)
{
EditorGUIUtility.PingObject(assetEditor.Collection);
}
}
};
continue;
}
if (treeMenuItem.Value is OdinStringTableCollectionEditor stringEditor)
{
treeMenuItem.Name = stringEditor.Collection.SharedData.TableCollectionName;
stringEditor.MenuItem = treeMenuItem;
treeMenuItem.OnDrawItem += item =>
{
if (Event.current.OnMouseDown(item.Rect, 0, false))
{
if (Event.current.clickCount > 1)
{
EditorGUIUtility.PingObject(stringEditor.Collection);
}
}
};
continue;
}
continue;
}
treeMenuItem.Value = createMenu;
treeMenuItem.SdfIcon = SdfIconType.FolderFill;
treeMenuItem.OnDrawItem += item =>
{
Rect addTableRect = item.Rect.AlignRight(20).SubX(14);
bool isMouseOver = Event.current.IsMouseOver(addTableRect);
if (EditorGUIUtility.isProSkin)
{
SdfIcons.DrawIcon(addTableRect.AlignCenter(16, 16),
SdfIconType.Plus,
isMouseOver ? new Color(1, 1, 1, 0.8f) : new Color(1, 1, 1, 0.4f));
}
else
{
SdfIcons.DrawIcon(addTableRect.AlignCenter(16, 16),
SdfIconType.Plus,
isMouseOver ? new Color(0, 0, 0, 0.8f) : new Color(0, 0, 0, 0.4f));
}
if (Event.current.OnMouseDown(item.Rect, 0, false))
{
createMenu.Folder = treeMenuItem.GetFullPath();
}
};
}
return tree;
}
public void Dispose()
{
this.DisposeActiveCollection();
this.State.Dispose();
}
private void DisposeActiveCollection()
{
if (this.MenuTree == null)
{
return;
}
switch (this.MenuTree.Selection.SelectedValue)
{
case OdinAssetTableCollectionEditor assetCollection:
assetCollection.DetachEvents();
break;
case OdinStringTableCollectionEditor stringCollection:
stringCollection.DetachEvents();
break;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a7cd6c96ce4b4c2c98fae9f89eedc690
timeCreated: 1704796865

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b2477452e56429547b05666cbaaf3502
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
//-----------------------------------------------------------------------
// <copyright file="LocalizedStringFormatter.cs" company="Sirenix ApS">
// Copyright (c) Sirenix ApS. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
using Sirenix.Serialization;
using System.Reflection;
using UnityEngine.Localization;
using System;
#if !ODIN_INSPECTOR_EDITOR_ONLY
[assembly: RegisterFormatter(typeof(Sirenix.OdinInspector.Modules.Localization.LocalizedStringFormatter))]
#endif
namespace Sirenix.OdinInspector.Modules.Localization
{
#if !ODIN_INSPECTOR_EDITOR_ONLY
public class LocalizedStringFormatter : ReflectionOrEmittedBaseFormatter<LocalizedString>
{
private static readonly FieldInfo m_LocalVariables_Field;
static LocalizedStringFormatter()
{
m_LocalVariables_Field = typeof(LocalizedString).GetField("m_LocalVariables", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (m_LocalVariables_Field == null)
{
DefaultLoggers.DefaultLogger.LogError("Could not find field 'UnityEngine.LocalizedString.m_LocalVariables'" +
" - the internals of the Localization package have changed, and deserialization of Odin-serialized" +
" LocalizedString instances may be broken in some cases.");
}
}
protected override LocalizedString GetUninitializedObject()
{
return new LocalizedString();
}
protected override void DeserializeImplementation(ref LocalizedString value, IDataReader reader)
{
base.DeserializeImplementation(ref value, reader);
if (m_LocalVariables_Field != null && value != null)
{
var localVariablesList = m_LocalVariables_Field.GetValue(value);
// This list is not allowed to be null!
if (localVariablesList == null)
{
localVariablesList = Activator.CreateInstance(m_LocalVariables_Field.FieldType);
m_LocalVariables_Field.SetValue(value, localVariablesList);
}
}
}
}
#endif
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93ff93294e9339b40b68190c779d0009
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
{
"name": "Sirenix.OdinInspector.Modules.UnityLocalization",
"references": [ "Unity.Localization", "Sirenix.Serialization", "Sirenix.Serialization.Config" ],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"autoReferenced": true,
"overrideReferences": false,
"precompiledReferences": [ "Sirenix.Serialization.dll", "Sirenix.Serialization.Config.dll" ],
"defineConstraints": [ "!ODIN_INSPECTOR_EDITOR_ONLY" ]
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 09320d9ddd505f84d9a6e5203cf75192
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,62 @@
ManifestVersion: 1
ModuleID: Unity.Localization
ModuleVersion: 2.0.0.5
ModuleFiles:
Editor.meta
Runtime.meta
Editor/Configs.meta
Editor/GUI.meta
Editor/Internal.meta
Editor/LocalizationSupport.cs
Editor/LocalizationSupport.cs.meta
Editor/OdinLocalizationConstants.cs
Editor/OdinLocalizationConstants.cs.meta
Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef
Editor/Sirenix.OdinInspector.Modules.UnityLocalization.Editor.asmdef.meta
Editor/Windows.meta
Editor/Configs/OdinLocalizationConfig.asset
Editor/Configs/OdinLocalizationConfig.asset.meta
Editor/Configs/OdinLocalizationConfig.cs
Editor/Configs/OdinLocalizationConfig.cs.meta
Editor/GUI/LocalizationMetadata.cs
Editor/GUI/LocalizationMetadata.cs.meta
Editor/GUI/OdinAssetTableCollectionEditor.cs
Editor/GUI/OdinAssetTableCollectionEditor.cs.meta
Editor/GUI/OdinGUITable.cs
Editor/GUI/OdinGUITable.cs.meta
Editor/GUI/OdinGUITableCollection.cs
Editor/GUI/OdinGUITableCollection.cs.meta
Editor/GUI/OdinSharedEntryCollection.cs
Editor/GUI/OdinSharedEntryCollection.cs.meta
Editor/GUI/OdinStringTableCollectionEditor.cs
Editor/GUI/OdinStringTableCollectionEditor.cs.meta
Editor/GUI/OdinTableCollectionEditor.cs
Editor/GUI/OdinTableCollectionEditor.cs.meta
Editor/GUI/OdinTemplateMetadata.cs
Editor/GUI/OdinTemplateMetadata.cs.meta
Editor/GUI/Toggle.cs
Editor/GUI/Toggle.cs.meta
Editor/Internal/OdinLocalizationAssetCache.cs
Editor/Internal/OdinLocalizationAssetCache.cs.meta
Editor/Internal/OdinLocalizationEditorSettings.cs
Editor/Internal/OdinLocalizationEditorSettings.cs.meta
Editor/Internal/OdinLocalizationEvents.cs
Editor/Internal/OdinLocalizationEvents.cs.meta
Editor/Internal/OdinLocalizationGUI.cs
Editor/Internal/OdinLocalizationGUI.cs.meta
Editor/Internal/OdinLocalizationGUITextures.cs
Editor/Internal/OdinLocalizationGUITextures.cs.meta
Editor/Internal/OdinLocalizationMetadataRegistry.cs
Editor/Internal/OdinLocalizationMetadataRegistry.cs.meta
Editor/Internal/OdinLocalizationReflectionValidator.cs
Editor/Internal/OdinLocalizationReflectionValidator.cs.meta
Editor/Internal/OdinLocalizationReflectionValues.cs
Editor/Internal/OdinLocalizationReflectionValues.cs.meta
Editor/Internal/OdinLocalizationSyntaxHighlighter.cs
Editor/Internal/OdinLocalizationSyntaxHighlighter.cs.meta
Editor/Windows/OdinLocalizationEditorWindow.cs
Editor/Windows/OdinLocalizationEditorWindow.cs.meta
Runtime/LocalizedStringFormatter.cs
Runtime/LocalizedStringFormatter.cs.meta
Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef
Runtime/Sirenix.OdinInspector.Modules.UnityLocalization.asmdef.meta

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6dd57bba224954bb890cd5ccb1aa3747
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: