// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using FlaxEditor.GUI.Tabs; using FlaxEditor.GUI.Tree; using FlaxEditor.Scripting; using FlaxEditor.Tools; using FlaxEditor.Tools.Foliage; using FlaxEditor.Tools.Terrain; using FlaxEditor.Utilities; using FlaxEditor.Viewport.Modes; using FlaxEngine; using FlaxEngine.GUI; namespace FlaxEditor.Windows { /// /// Objects spawning tab. Supports searching actor types and prefabs for spawning them into the level. /// /// public class SpawnTab : Tab { private class Item : TreeNode { private DragData _dragData; private List _highlights; public Item(string text, DragData dragData = null) : this(text, dragData, SpriteHandle.Invalid) { } private Item(string text, DragData dragData, SpriteHandle icon) : base(false, icon, icon) { Text = text; Height = 20; TextMargin = new Margin(-5.0f, 2.0f, 2.0f, 2.0f); _dragData = dragData; } public void SetHighlights(List highlights) { _highlights = highlights; } /// public override void Draw() { base.Draw(); // Draw all highlights if (_highlights != null) { var style = Style.Current; var color = style.ProgressNormal * 0.6f; for (int i = 0; i < _highlights.Count; i++) Render2D.FillRectangle(_highlights[i], color); } } /// protected override void DoDragDrop() { if (_dragData != null) DoDragDrop(_dragData); } /// public override void OnDestroy() { _dragData = null; _highlights = null; base.OnDestroy(); } } private TextBox _searchBox; private ContainerControl _groupSearch; /// /// The editor instance. /// public readonly Editor Editor; /// /// Initializes a new instance of the class. /// /// The icon. /// The editor instance. public SpawnTab(SpriteHandle icon, Editor editor) : base(string.Empty, icon) { Editor = editor; Selected += tab => Editor.Windows.EditWin.Viewport.SetActiveMode(); ScriptsBuilder.ScriptsReload += OnScriptsReload; var actorGroups = new Tabs { Orientation = Orientation.Vertical, UseScroll = true, AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, TabsSize = new Vector2(120, 32), Parent = this, }; _groupSearch = CreateGroupWithList(actorGroups, "Search", 26); _searchBox = new TextBox { AnchorPreset = AnchorPresets.HorizontalStretchTop, WatermarkText = "Search...", Parent = _groupSearch.Parent.Parent, Bounds = new Rectangle(4, 4, actorGroups.Width - 8, 18), }; _searchBox.TextChanged += OnSearchBoxTextChanged; var groupBasicModels = CreateGroupWithList(actorGroups, "Basic Models"); groupBasicModels.AddChild(CreateEditorAssetItem("Cube", "Primitives/Cube.flax")); groupBasicModels.AddChild(CreateEditorAssetItem("Sphere", "Primitives/Sphere.flax")); groupBasicModels.AddChild(CreateEditorAssetItem("Plane", "Primitives/Plane.flax")); groupBasicModels.AddChild(CreateEditorAssetItem("Cylinder", "Primitives/Cylinder.flax")); groupBasicModels.AddChild(CreateEditorAssetItem("Cone", "Primitives/Cone.flax")); groupBasicModels.AddChild(CreateEditorAssetItem("Capsule", "Primitives/Capsule.flax")); var groupLights = CreateGroupWithList(actorGroups, "Lights"); groupLights.AddChild(CreateActorItem("Directional Light", typeof(DirectionalLight))); groupLights.AddChild(CreateActorItem("Point Light", typeof(PointLight))); groupLights.AddChild(CreateActorItem("Spot Light", typeof(SpotLight))); groupLights.AddChild(CreateActorItem("Sky Light", typeof(SkyLight))); var groupVisuals = CreateGroupWithList(actorGroups, "Visuals"); groupVisuals.AddChild(CreateActorItem("Camera", typeof(Camera))); groupVisuals.AddChild(CreateActorItem("Environment Probe", typeof(EnvironmentProbe))); groupVisuals.AddChild(CreateActorItem("Skybox", typeof(Skybox))); groupVisuals.AddChild(CreateActorItem("Sky", typeof(Sky))); groupVisuals.AddChild(CreateActorItem("Exponential Height Fog", typeof(ExponentialHeightFog))); groupVisuals.AddChild(CreateActorItem("PostFx Volume", typeof(PostFxVolume))); groupVisuals.AddChild(CreateActorItem("Decal", typeof(Decal))); groupVisuals.AddChild(CreateActorItem("Particle Effect", typeof(ParticleEffect))); var groupPhysics = CreateGroupWithList(actorGroups, "Physics"); groupPhysics.AddChild(CreateActorItem("Rigid Body", typeof(RigidBody))); groupPhysics.AddChild(CreateActorItem("Character Controller", typeof(CharacterController))); groupPhysics.AddChild(CreateActorItem("Box Collider", typeof(BoxCollider))); groupPhysics.AddChild(CreateActorItem("Sphere Collider", typeof(SphereCollider))); groupPhysics.AddChild(CreateActorItem("Capsule Collider", typeof(CapsuleCollider))); groupPhysics.AddChild(CreateActorItem("Mesh Collider", typeof(MeshCollider))); groupPhysics.AddChild(CreateActorItem("Fixed Joint", typeof(FixedJoint))); groupPhysics.AddChild(CreateActorItem("Distance Joint", typeof(DistanceJoint))); groupPhysics.AddChild(CreateActorItem("Slider Joint", typeof(SliderJoint))); groupPhysics.AddChild(CreateActorItem("Spherical Joint", typeof(SphericalJoint))); groupPhysics.AddChild(CreateActorItem("Hinge Joint", typeof(HingeJoint))); groupPhysics.AddChild(CreateActorItem("D6 Joint", typeof(D6Joint))); var groupOther = CreateGroupWithList(actorGroups, "Other"); groupOther.AddChild(CreateActorItem("Animated Model", typeof(AnimatedModel))); groupOther.AddChild(CreateActorItem("Bone Socket", typeof(BoneSocket))); groupOther.AddChild(CreateActorItem("CSG Box Brush", typeof(BoxBrush))); groupOther.AddChild(CreateActorItem("Audio Source", typeof(AudioSource))); groupOther.AddChild(CreateActorItem("Audio Listener", typeof(AudioListener))); groupOther.AddChild(CreateActorItem("Empty Actor", typeof(EmptyActor))); groupOther.AddChild(CreateActorItem("Scene Animation", typeof(SceneAnimationPlayer))); groupOther.AddChild(CreateActorItem("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume))); groupOther.AddChild(CreateActorItem("Nav Mesh Link", typeof(NavLink))); var groupGui = CreateGroupWithList(actorGroups, "GUI"); groupGui.AddChild(CreateActorItem("UI Control", typeof(UIControl))); groupGui.AddChild(CreateActorItem("UI Canvas", typeof(UICanvas))); groupGui.AddChild(CreateActorItem("Text Render", typeof(TextRender))); actorGroups.SelectedTabIndex = 1; } private void OnScriptsReload() { // Prevent any references to actor types from the game assemblies that will be reloaded _searchBox.Clear(); _groupSearch.DisposeChildren(); _groupSearch.PerformLayout(); } private void OnSearchBoxTextChanged() { // Skip events during setup or init stuff if (IsLayoutLocked) return; var filterText = _searchBox.Text; _groupSearch.LockChildrenRecursive(); _groupSearch.DisposeChildren(); foreach (var actorType in Editor.CodeEditing.Actors.Get()) { var text = actorType.Name; QueryFilterHelper.Range[] ranges; if (!QueryFilterHelper.Match(filterText, text, out ranges)) continue; var item = _groupSearch.AddChild(CreateActorItem(CustomEditors.CustomEditorsUtil.GetPropertyNameUI(text), actorType)); item.TooltipText = actorType.TypeName; var attributes = actorType.GetAttributes(false); var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); if (tooltipAttribute != null) { item.TooltipText += '\n'; item.TooltipText += tooltipAttribute.Text; } var highlights = new List(ranges.Length); var style = Style.Current; var font = style.FontSmall; var textRect = item.TextRect; for (int i = 0; i < ranges.Length; i++) { var start = font.GetCharPosition(text, ranges[i].StartIndex); var end = font.GetCharPosition(text, ranges[i].EndIndex); highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height)); } item.SetHighlights(highlights); } _groupSearch.UnlockChildrenRecursive(); PerformLayout(); PerformLayout(); } private Item CreateEditorAssetItem(string name, string path) { path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor", path); return new Item(name, GUI.Drag.DragItems.GetDragData(path)); } private Item CreateActorItem(string name, Type type) { return new Item(name, GUI.Drag.DragActorType.GetDragData(type)); } private Item CreateActorItem(string name, ScriptType type) { return new Item(name, GUI.Drag.DragActorType.GetDragData(type)); } private ContainerControl CreateGroupWithList(Tabs parentTabs, string title, float topOffset = 0) { var tab = parentTabs.AddTab(new Tab(title)); var panel = new Panel(ScrollBars.Both) { AnchorPreset = AnchorPresets.StretchAll, Offsets = new Margin(0, 0, topOffset, 0), Parent = tab }; var tree = new Tree(false) { AnchorPreset = AnchorPresets.HorizontalStretchTop, IsScrollable = true, Parent = panel }; return tree; } } /// /// A helper utility window with bunch of tools used during scene editing. /// /// public class ToolboxWindow : EditorWindow { /// /// Gets the tabs control used by this window. Can be used to add custom toolbox modes. /// public Tabs TabsControl { get; private set; } /// /// The objects spawning tab. /// public SpawnTab Spawn; /// /// The vertex painting tab. /// public VertexPaintingTab VertexPaint; /// /// The foliage editing tab. /// public FoliageTab Foliage; /// /// The terrain editing tab. /// public CarveTab Carve; /// /// Initializes a new instance of the class. /// /// The editor. public ToolboxWindow(Editor editor) : base(editor, true, ScrollBars.None) { Title = "Toolbox"; } /// public override void OnInit() { float tabSize = 48 * Editor.Options.Options.Interface.IconsScale; TabsControl = new Tabs { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, TabsSize = new Vector2(tabSize, tabSize), Parent = this }; TabsControl.AddTab(Spawn = new SpawnTab(Editor.Icons.Add48, Editor)); TabsControl.AddTab(VertexPaint = new VertexPaintingTab(Editor.Icons.Paint48, Editor)); TabsControl.AddTab(Foliage = new FoliageTab(Editor.Icons.Foliage48, Editor)); TabsControl.AddTab(Carve = new CarveTab(Editor.Icons.Mountain48, Editor)); TabsControl.SelectedTabIndex = 0; } } }