// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; using System.Runtime.Serialization.Formatters.Binary; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Surface { class AttributesEditor : ContextMenuBase { private CustomEditorPresenter _presenter; private byte[] _oldData; private class Proxy { [Collection(OverrideEditorTypeName = "FlaxEditor.Surface.AttributesEditor+ValuesEditor", Spacing = 10)] [EditorDisplay(null, EditorDisplayAttribute.InlineStyle)] public Attribute[] Value; } public sealed class ValuesEditor : ObjectSwitcherEditor { private OptionType[] _options; /// protected override OptionType[] Options => _options; /// public override void Initialize(LayoutElementsContainer layout) { var options = (IList)Presenter.Panel.Tag; _options = new OptionType[options.Count]; for (int i = 0; i < options.Count; i++) { var type = options[i]; _options[i] = new OptionType(Utilities.Utils.GetPropertyNameUI(type.Name), type, Creator); } base.Initialize(layout); } /// protected override void Deinitialize() { base.Deinitialize(); _options = null; } private object Creator(Type type) { var ctor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Utils.GetEmptyArray(), null); return ctor.Invoke(Utils.GetEmptyArray()); } } /// /// The action fired when parameters editing finishes and value gets accepted to be set. /// public Action Edited; /// /// Initializes a new instance of the class. /// /// The attributes list to edit. /// The allowed attribute types to use. public AttributesEditor(Attribute[] attributes, IList attributeType) { // Context menu dimensions const float width = 340.0f; const float height = 370.0f; Size = new Float2(width, height); // Title var title = new Label(2, 2, width - 4, 23.0f) { Font = new FontReference(Style.Current.FontLarge), Text = "Edit attributes", Parent = this }; // Buttons float buttonsWidth = (width - 16.0f) * 0.5f; float buttonsHeight = 20.0f; var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight) { Text = "Cancel", Parent = this }; cancelButton.Clicked += Hide; var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight) { Text = "OK", Parent = this }; okButton.Clicked += OnOkButtonClicked; // Actual panel var panel1 = new Panel(ScrollBars.Vertical) { Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f), Parent = this }; var editor = new CustomEditorPresenter(null); editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop; editor.Panel.IsScrollable = true; editor.Panel.Parent = panel1; editor.Panel.Tag = attributeType; _presenter = editor; using (var stream = new MemoryStream()) { // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); var formatter = new BinaryFormatter(); #pragma warning disable SYSLIB0011 formatter.Serialize(stream, attributes); #pragma warning restore SYSLIB0011 _oldData = stream.ToArray(); } editor.Select(new Proxy { Value = attributes, }); } private void OnOkButtonClicked() { var newValue = ((Proxy)_presenter.Selection[0]).Value; for (int i = 0; i < newValue.Length; i++) { if (newValue[i] == null) { MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } } using (var stream = new MemoryStream()) { // Ensure we are in the correct load context (https://github.com/dotnet/runtime/issues/42041) using var ctx = AssemblyLoadContext.EnterContextualReflection(typeof(Editor).Assembly); var formatter = new BinaryFormatter(); #pragma warning disable SYSLIB0011 formatter.Serialize(stream, newValue); #pragma warning restore SYSLIB0011 var newData = stream.ToArray(); if (!_oldData.SequenceEqual(newData)) { Edited?.Invoke(newValue); } } Hide(); } /// protected override void OnShow() { Focus(); base.OnShow(); } /// public override void Hide() { if (!Visible) return; Focus(null); base.Hide(); } /// public override bool OnKeyDown(KeyboardKeys key) { if (key == KeyboardKeys.Escape) { Hide(); return true; } return base.OnKeyDown(key); } /// public override void OnDestroy() { _presenter = null; _oldData = null; Edited = null; base.OnDestroy(); } } }