using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.IO;
using BansheeEngine;
namespace BansheeEditor
{
///
/// Available tools in the scene view.
///
public enum SceneViewTool
{
View,
Move,
Rotate,
Scale
}
///
/// Pivot mode used by the scene view tools.
///
public enum HandlePivotMode
{
Center,
Pivot
}
///
/// Coordinate mode used by the scene view tools.
///
public enum HandleCoordinateMode
{
Local,
World
}
///
/// Manages various generic and global settings relating to the editor.
///
public class EditorApplication
{
///
/// Determines the active tool shown in the scene view.
///
public static SceneViewTool ActiveSceneTool
{
get { return EditorSettings.ActiveSceneTool; }
set { EditorSettings.ActiveSceneTool = value; }
}
///
/// Determines the coordinate mode used by the tools in the scene view.
///
public static HandleCoordinateMode ActiveCoordinateMode
{
get { return EditorSettings.ActiveCoordinateMode; }
set { EditorSettings.ActiveCoordinateMode = value; }
}
///
/// Determines the pivot mode used by the tools in the scene view.
///
public static HandlePivotMode ActivePivotMode
{
get { return EditorSettings.ActivePivotMode; }
set { EditorSettings.ActivePivotMode = value; }
}
///
/// Camera used for rendering the scene view.
///
public static Camera SceneViewCamera
{
get { return EditorWindow.GetWindow().Camera; }
}
///
/// Absolute path to the folder containing the currently open project.
///
public static string ProjectPath { get { return Internal_GetProjectPath(); } }
///
/// Name of the currently open project.
///
public static string ProjectName { get { return Internal_GetProjectName(); } }
///
/// Checks is any project currently loaded.
///
public static bool IsProjectLoaded { get { return Internal_GetProjectLoaded(); } }
///
/// Returns the path where the script compiler is located at.
///
internal static string CompilerPath { get { return Internal_GetCompilerPath(); } }
///
/// Returns the path to the folder where the builtin script assemblies are located at.
///
internal static string BuiltinAssemblyPath { get { return Internal_GetBuiltinAssemblyPath(); } }
///
/// Returns the path to the folder where the custom script assemblies are located at.
///
internal static string ScriptAssemblyPath { get { return Internal_GetScriptAssemblyPath(); } }
///
/// Returns the path to the folder where the .NET framework assemblies are located at.
///
internal static string FrameworkAssemblyPath { get { return Internal_GetFrameworkAssemblyPath(); } }
///
/// Name of the builtin assembly containing engine specific types.
///
internal static string EngineAssembly { get { return Internal_GetEngineAssemblyName(); } }
///
/// Name of the builtin assembly containing editor specific types.
///
internal static string EditorAssembly { get { return Internal_GetEditorAssemblyName(); } }
///
/// Name of the custom assembly compiled from non-editor scripts within the project.
///
internal static string ScriptGameAssembly { get { return Internal_GetScriptGameAssemblyName(); } }
///
/// Name of the custom assembly compiled from editor scripts within the project.
///
internal static string ScriptEditorAssembly { get { return Internal_GetScriptEditorAssemblyName(); } }
private static EditorApplication instance;
private static FolderMonitor monitor;
///
/// Constructs a new editor application. Called at editor start-up by the runtime.
///
internal EditorApplication()
{
instance = this;
// Register controls
InputConfiguration inputConfig = VirtualInput.KeyConfig;
inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.W);
inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.S);
inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.A);
inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.D);
inputConfig.RegisterButton(SceneCamera.MoveForwardBinding, ButtonCode.Up);
inputConfig.RegisterButton(SceneCamera.MoveBackBinding, ButtonCode.Down);
inputConfig.RegisterButton(SceneCamera.MoveLeftBinding, ButtonCode.Left);
inputConfig.RegisterButton(SceneCamera.MoveRightBinding, ButtonCode.Right);
inputConfig.RegisterButton(SceneCamera.FastMoveBinding, ButtonCode.LeftShift);
inputConfig.RegisterButton(SceneCamera.RotateBinding, ButtonCode.MouseRight);
inputConfig.RegisterAxis(SceneCamera.HorizontalAxisBinding, InputAxis.MouseX);
inputConfig.RegisterAxis(SceneCamera.VerticalAxisBinding, InputAxis.MouseY);
inputConfig.RegisterButton(SceneWindow.ToggleProfilerOverlayBinding, ButtonCode.P, ButtonModifier.CtrlAlt);
inputConfig.RegisterButton(SceneWindow.ViewToolBinding, ButtonCode.Q);
inputConfig.RegisterButton(SceneWindow.MoveToolBinding, ButtonCode.W);
inputConfig.RegisterButton(SceneWindow.RotateToolBinding, ButtonCode.E);
inputConfig.RegisterButton(SceneWindow.ScaleToolBinding, ButtonCode.R);
inputConfig.RegisterButton(SceneWindow.DuplicateBinding, ButtonCode.D, ButtonModifier.Ctrl);
}
///
/// Triggered when the folder monitor detects an asset in the monitored folder was modified.
///
/// Path to the modified file or folder.
private static void OnAssetModified(string path)
{
ProjectLibrary.Refresh(path);
}
///
/// Called 60 times per second by the runtime.
///
internal void OnEditorUpdate()
{
ProjectLibrary.Update();
}
///
/// Opens a dialog that allows the user to select a new prefab to load as the current scene. If current scene
/// is modified the user is offered a chance to save it.
///
[MenuItem("File/Open Scene", ButtonModifier.Ctrl, ButtonCode.L, 10050, true)]
private static void LoadScene()
{
string[] scenePaths;
if (BrowseDialog.OpenFile(ProjectLibrary.ResourceFolder, "", false, out scenePaths))
{
if (scenePaths.Length > 0)
LoadScene(scenePaths[0]);
}
}
///
/// Opens a dialog to allows the user to select a location where to save the current scene. If scene was previously
/// saved it is instead automatically saved at the last location.
///
[MenuItem("File/Save Scene", ButtonModifier.Ctrl, ButtonCode.S, 10049)]
[ToolbarItem("Save Scene", ToolbarIcon.SaveScene, "", 1998)]
private static void SaveScene()
{
if (!string.IsNullOrEmpty(Scene.ActiveSceneUUID))
{
string scenePath = ProjectLibrary.GetPath(Scene.ActiveSceneUUID);
Internal_SaveScene(scenePath);
}
else
SaveSceneAs();
}
///
/// Opens a dialog to allows the user to select a location where to save the current scene.
///
[MenuItem("File/Save Scene As", 10048)]
private static void SaveSceneAs()
{
string scenePath = "";
if (BrowseDialog.SaveFile(ProjectLibrary.ResourceFolder, "*.prefab", out scenePath))
{
if (!PathEx.IsPartOf(scenePath, ProjectLibrary.ResourceFolder))
DialogBox.Open("Error", "The location must be inside the Resources folder of the project.",
DialogBox.Type.OK);
else
{
// TODO - If path points to an existing non-scene asset or folder I should delete it otherwise
// Internal_SaveScene will silently fail.
Scene.ActiveSceneUUID = Internal_SaveScene(scenePath + ".prefab");
}
}
}
///
/// Loads a prefab as the current scene at the specified path. If current scene is modified the user is offered a
/// chance to save it.
///
/// Path to a valid prefab, relative to the resource folder.
public static void LoadScene(string path)
{
Action continueLoad =
(scenePath) =>
{
Scene.Load(path);
ProjectSettings.LastOpenScene = scenePath;
ProjectSettings.Save();
};
Action dialogCallback =
(result) =>
{
if (result == DialogBox.ResultType.Yes)
{
SaveScene();
continueLoad(path);
}
else if (result == DialogBox.ResultType.No)
continueLoad(path);
};
if (Scene.IsModified())
{
DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
DialogBox.Type.YesNoCancel, dialogCallback);
}
else
continueLoad(path);
}
///
/// Checks does the folder at the provieded path contain a valid project.
///
/// Absolute path to the root project folder.
/// True if the folder contains a valid project.
public static bool IsValidProject(string path)
{
return Internal_IsValidProject(path);
}
///
/// Contains a new project in the provided folder.
///
/// Absolute path to the folder to create the project in. Name of this folder will be used as the
/// project's name.
public static void CreateProject(string path)
{
Internal_CreateProject(path);
}
///
/// Opens a Project Window allowing you to browse for or create a project.
///
[MenuItem("File/Open Project", 10100)]
[ToolbarItem("Open Project", ToolbarIcon.OpenProject, "", 2000)]
public static void BrowseForProject()
{
ProjectWindow.Open();
}
///
/// Saves all data in the currently open project.
///
[MenuItem("File/Save Project", 10099)]
[ToolbarItem("Save Project", ToolbarIcon.SaveProject, "", 1999)]
public static void SaveProject()
{
// TODO - Save dirty resources
Internal_SaveProject();
}
///
/// Loads the project at the specified path. This method executes asynchronously and will trigger
/// when done.
///
/// Absolute path to the project's root folder.
public static void LoadProject(string path)
{
if (IsProjectLoaded && path == ProjectPath)
return;
if (!Internal_IsValidProject(path))
{
Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project.");
return;
}
if (IsProjectLoaded)
UnloadProject();
Internal_LoadProject(path); // Triggers OnProjectLoaded when done
}
///
/// Opens a file or a folder in the default external application.
///
/// Absolute path to the file or folder to open.
public static void OpenExternally(string path)
{
Internal_OpenExternally(path);
}
///
/// Marks a resource as dirty so that it may be saved the next time the project is saved. Optionally you may also
/// call to save it immediately.
///
/// Resource to mark as dirty
public static void SetDirty(Resource resource)
{
// TODO - Not implemented
}
///
/// Triggered when method completes.
///
private static void OnProjectLoaded()
{
if (!IsProjectLoaded)
{
ProjectWindow.Open();
return;
}
string projectPath = ProjectPath;
RecentProject[] recentProjects = EditorSettings.RecentProjects;
bool foundPath = false;
for (int i = 0; i < recentProjects.Length; i++)
{
if (PathEx.Compare(recentProjects[i].path, projectPath))
{
recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
EditorSettings.RecentProjects = recentProjects;
foundPath = true;
break;
}
}
if (!foundPath)
{
List extendedRecentProjects = new List();
extendedRecentProjects.AddRange(recentProjects);
RecentProject newProject = new RecentProject();
newProject.path = projectPath;
newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
extendedRecentProjects.Add(newProject);
EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
}
EditorSettings.LastOpenProject = projectPath;
EditorSettings.Save();
ProjectLibrary.Refresh();
monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
monitor.OnAdded += OnAssetModified;
monitor.OnRemoved += OnAssetModified;
monitor.OnModified += OnAssetModified;
if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
Scene.Load(ProjectSettings.LastOpenScene);
}
///
/// Unloads the currently loaded project. Offers the user a chance to save the current scene if it is modified.
/// Automatically saves all project data before unloading.
///
private static void UnloadProject()
{
Action continueUnload =
() =>
{
Scene.Clear();
if (monitor != null)
{
monitor.Destroy();
monitor = null;
}
LibraryWindow window = EditorWindow.GetWindow();
if(window != null)
window.Reset();
Internal_UnloadProject();
};
Action dialogCallback =
(result) =>
{
if (result == DialogBox.ResultType.Yes)
SaveScene();
continueUnload();
};
if (Scene.IsModified())
{
DialogBox.Open("Warning", "You current scene has modifications. Do you wish to save them first?",
DialogBox.Type.YesNoCancel, dialogCallback);
}
else
continueUnload();
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetProjectPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetProjectName();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool Internal_GetProjectLoaded();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetCompilerPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetBuiltinAssemblyPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetScriptAssemblyPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetFrameworkAssemblyPath();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetEngineAssemblyName();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetEditorAssemblyName();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetScriptGameAssemblyName();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_GetScriptEditorAssemblyName();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string Internal_SaveScene(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool Internal_IsValidProject(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_SaveProject();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_LoadProject(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_UnloadProject();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_CreateProject(string path);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void Internal_OpenExternally(string path);
}
}