// Copyright (c) Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Xml; using FlaxEditor.CustomEditors; using FlaxEditor.SceneGraph; using FlaxEditor.Viewport; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Windows { /// /// Window used to present collection of selected object(s) properties in a grid. Supports Undo/Redo operations. /// /// /// public class PropertiesWindow : SceneEditorWindow, IPresenterOwner { private IEnumerable undoRecordObjects; private readonly Dictionary _actorScrollValues = new Dictionary(); private bool _lockObjects = false; /// public override bool UseLayoutData => true; /// /// The editor. /// public readonly CustomEditorPresenter Presenter; /// /// Indication of if the scale is locked. /// public bool ScaleLinked = false; /// /// Indication of if UI elements should size relative to the pivot point. /// public bool UIPivotRelative = true; /// /// Indication of if the properties window is locked on specific objects. /// public bool LockSelection { get => _lockObjects; set { if (value == _lockObjects) return; _lockObjects = value; if (!value) OnSelectionChanged(); } } /// public ISceneEditingContext SceneContext => Editor.Windows.EditWin; /// /// Initializes a new instance of the class. /// /// The editor. public PropertiesWindow(Editor editor) : base(editor, true, ScrollBars.Vertical) { Title = "Properties"; Icon = editor.Icons.Build64; AutoFocus = true; Presenter = new CustomEditorPresenter(editor.Undo, null, this); Presenter.Panel.Parent = this; Presenter.GetUndoObjects += GetUndoObjects; Presenter.Features |= FeatureFlags.CacheExpandedGroups; VScrollBar.ValueChanged += OnScrollValueChanged; Editor.SceneEditing.SelectionChanged += OnSelectionChanged; } /// public override void OnSceneLoaded(Scene scene, Guid sceneId) { base.OnSceneLoaded(scene, sceneId); // Clear scroll values if new scene is loaded non additively if (Level.ScenesCount > 1) return; _actorScrollValues.Clear(); if (LockSelection) { LockSelection = false; Presenter.Deselect(); } } private void OnScrollValueChanged() { if (Editor.SceneEditing.SelectionCount > 1) return; // Clear first 10 scroll values to keep the memory down. Dont need to cache very single value in a scene. We could expose this as a editor setting in the future. if (_actorScrollValues.Count >= 20) { int i = 0; foreach (var e in _actorScrollValues) { if (i >= 10) break; _actorScrollValues.Remove(e.Key); i += 1; } } _actorScrollValues[Editor.SceneEditing.Selection[0].ID] = VScrollBar.TargetValue; } private IEnumerable GetUndoObjects(CustomEditorPresenter customEditorPresenter) { return undoRecordObjects; } private void OnSelectionChanged() { if (LockSelection) return; // Update selected objects // TODO: use cached collection for less memory allocations undoRecordObjects = Editor.SceneEditing.Selection.ConvertAll(x => x.UndoRecordObject).Distinct(); var objects = Editor.SceneEditing.Selection.ConvertAll(x => x.EditableObject).Distinct(); Presenter.Select(objects); // Set scroll value of window if it exists if (Editor.SceneEditing.SelectionCount == 1) VScrollBar.TargetValue = _actorScrollValues.GetValueOrDefault(Editor.SceneEditing.Selection[0].ID, 0); } /// public override void OnLayoutSerialize(XmlWriter writer) { writer.WriteAttributeString("ScaleLinked", ScaleLinked.ToString()); writer.WriteAttributeString("UIPivotRelative", UIPivotRelative.ToString()); } /// public override void OnLayoutDeserialize(XmlElement node) { if (bool.TryParse(node.GetAttribute("ScaleLinked"), out bool value1)) ScaleLinked = value1; if (bool.TryParse(node.GetAttribute("UIPivotRelative"), out value1)) UIPivotRelative = value1; } /// public EditorViewport PresenterViewport => Editor.Windows.EditWin.Viewport; /// public void Select(List nodes) { Editor.SceneEditing.Select(nodes); } } }