Browse Source

Merge pull request #103 from ThunderBeastGames/Editor-Work

Editor Work
JoshEngebretson 10 years ago
parent
commit
c49bef7d2a
100 changed files with 10525 additions and 170 deletions
  1. 2 2
      .gitignore
  2. 1 0
      CMake/Modules/AtomicAndroid.cmake
  3. 4 2
      CMake/Modules/AtomicIOS.cmake
  4. 2 0
      CMake/Modules/AtomicLinux.cmake
  5. 2 0
      CMake/Modules/AtomicMac.cmake
  6. 2 0
      CMake/Modules/AtomicWeb.cmake
  7. 2 1
      CMake/Modules/AtomicWindows.cmake
  8. 20 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/main.js
  9. 61 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/editorStrings.js
  10. 15 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/hierarchyframe.js
  11. 162 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/mainframe.js
  12. 31 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/projectframe.js
  13. 8 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/ui.js
  14. 37 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/utils.js
  15. 70 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/AtomicWork.d.ts
  16. 73 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/editor/Editor.ts
  17. 9 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/main.ts
  18. 541 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/acorn.js
  19. 109 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/beautify.js
  20. 67 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/jsutils.js
  21. 27 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/tsconfig.json
  22. 66 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/EditorStrings.ts
  23. 121 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrame.ts
  24. 78 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrameMenu.ts
  25. 316 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ProjectFrame.ts
  26. 187 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ResourceFrame.ts
  27. 36 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ScriptWidget.ts
  28. 14 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/UIEvents.ts
  29. 34 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/modal/MessageModal.ts
  30. 0 0
      Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/modal/ModalOps.ts
  31. 9 9
      Rakefile
  32. 2 1
      Source/Atomic/Core/Object.h
  33. 0 121
      Source/Atomic/Engine/DebugHud.h
  34. 64 9
      Source/Atomic/UI/UI.cpp
  35. 9 0
      Source/Atomic/UI/UIButton.cpp
  36. 2 0
      Source/Atomic/UI/UIButton.h
  37. 2 2
      Source/Atomic/UI/UIEvents.h
  38. 34 0
      Source/Atomic/UI/UIFontDescription.cpp
  39. 31 0
      Source/Atomic/UI/UIFontDescription.h
  40. 9 0
      Source/Atomic/UI/UILayout.cpp
  41. 19 0
      Source/Atomic/UI/UILayout.h
  42. 267 0
      Source/Atomic/UI/UIListView.cpp
  43. 45 0
      Source/Atomic/UI/UIListView.h
  44. 1 1
      Source/Atomic/UI/UIMenuWindow.h
  45. 214 0
      Source/Atomic/UI/UIMenubar.cpp
  46. 44 0
      Source/Atomic/UI/UIMenubar.h
  47. 50 0
      Source/Atomic/UI/UIMessageWindow.cpp
  48. 28 0
      Source/Atomic/UI/UIMessageWindow.h
  49. 230 0
      Source/Atomic/UI/UISceneView.cpp
  50. 107 0
      Source/Atomic/UI/UISceneView.h
  51. 1 1
      Source/Atomic/UI/UISelectItem.cpp
  52. 4 4
      Source/Atomic/UI/UISelectItem.h
  53. 95 0
      Source/Atomic/UI/UISelectList.cpp
  54. 45 0
      Source/Atomic/UI/UISelectList.h
  55. 39 0
      Source/Atomic/UI/UISkinImage.cpp
  56. 26 0
      Source/Atomic/UI/UISkinImage.h
  57. 65 0
      Source/Atomic/UI/UITabContainer.cpp
  58. 31 0
      Source/Atomic/UI/UITabContainer.h
  59. 122 6
      Source/Atomic/UI/UIWidget.cpp
  60. 21 1
      Source/Atomic/UI/UIWidget.h
  61. 1 1
      Source/AtomicEditor/Source/AEJavascript.h
  62. 1 1
      Source/AtomicEditor/Source/Javascript/JSErrorChecker.h
  63. 1 1
      Source/AtomicEditor/Source/Player/AEPlayerApplication.cpp
  64. 0 3
      Source/AtomicEditor/Source/UI/UIMenubar.h
  65. 152 0
      Source/AtomicEditorWork/Application/AEEditorApp.cpp
  66. 40 0
      Source/AtomicEditorWork/Application/AEEditorApp.h
  67. 39 0
      Source/AtomicEditorWork/Application/AEPlayerApp.cpp
  68. 30 0
      Source/AtomicEditorWork/Application/AEPlayerApp.h
  69. 122 0
      Source/AtomicEditorWork/Application/Main.cpp
  70. 67 0
      Source/AtomicEditorWork/CMakeLists.txt
  71. 620 0
      Source/AtomicEditorWork/Editors/JSResourceEditor.cpp
  72. 66 0
      Source/AtomicEditorWork/Editors/JSResourceEditor.h
  73. 128 0
      Source/AtomicEditorWork/Editors/ResourceEditor.cpp
  74. 64 0
      Source/AtomicEditorWork/Editors/ResourceEditor.h
  75. 437 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/Gizmo3D.cpp
  76. 155 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/Gizmo3D.h
  77. 183 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.cpp
  78. 70 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.h
  79. 348 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/SceneView3D.cpp
  80. 79 0
      Source/AtomicEditorWork/Editors/SceneEditor3D/SceneView3D.h
  81. 364 0
      Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.cpp
  82. 47 0
      Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.h
  83. 319 0
      Source/AtomicEditorWork/Inspector/UIInspectorFrame.cpp
  84. 67 0
      Source/AtomicEditorWork/Inspector/UIInspectorFrame.h
  85. 18 0
      Source/AtomicEditorWork/JSTest/MyJSClass.cpp
  86. 29 0
      Source/AtomicEditorWork/JSTest/MyJSClass.h
  87. 26 0
      Source/AtomicEditorWork/Javascript/AEEditorJS.cpp
  88. 0 0
      Source/AtomicEditorWork/Javascript/AEEditorJS.h
  89. 961 0
      Source/AtomicEditorWork/Javascript/JSAST.cpp
  90. 967 0
      Source/AtomicEditorWork/Javascript/JSAST.h
  91. 2 2
      Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.cpp
  92. 360 0
      Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.h
  93. 3 2
      Source/AtomicEditorWork/Javascript/JSASTVisitor.cpp
  94. 382 0
      Source/AtomicEditorWork/Javascript/JSASTVisitor.h
  95. 190 0
      Source/AtomicEditorWork/Javascript/JSAutocomplete.cpp
  96. 56 0
      Source/AtomicEditorWork/Javascript/JSAutocomplete.h
  97. 26 0
      Source/AtomicEditorWork/Javascript/JSErrorChecker.cpp
  98. 33 0
      Source/AtomicEditorWork/Javascript/JSErrorChecker.h
  99. 303 0
      Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.cpp
  100. 56 0
      Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.h

+ 2 - 2
.gitignore

@@ -1,8 +1,8 @@
 
 
 *.user
 *.user
 *.log
 *.log
-Bin/Atomic.d.ts
-Bin/Atomic.js
+Bin/*.d.ts
+Bin/*.js
 Bin/*.pak
 Bin/*.pak
 Bin/NativePluginSDK/*
 Bin/NativePluginSDK/*
 Source/Atomic/Javascript/Modules/*
 Source/Atomic/Javascript/Modules/*

+ 1 - 0
CMake/Modules/AtomicAndroid.cmake

@@ -1,4 +1,5 @@
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "ANDROID")
 
 
 add_definitions(-DATOMIC_PLATFORM_ANDROID)
 add_definitions(-DATOMIC_PLATFORM_ANDROID)
 add_definitions(-DATOMIC_OPENGL -DKNET_UNIX)
 add_definitions(-DATOMIC_OPENGL -DKNET_UNIX)

+ 4 - 2
CMake/Modules/AtomicIOS.cmake

@@ -1,5 +1,7 @@
 include (BundleUtilities)
 include (BundleUtilities)
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "IOS")
+
 add_definitions (-DIOS -DATOMIC_PLATFORM_IOS -DATOMIC_OPENGL -DKNET_UNIX)
 add_definitions (-DIOS -DATOMIC_PLATFORM_IOS -DATOMIC_OPENGL -DKNET_UNIX)
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -std=gnu++0x")
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -std=gnu++0x")
 
 
@@ -7,10 +9,10 @@ set (CMAKE_OSX_ARCHITECTURES $(ARCHS_STANDARD_INCLUDING_64_BIT))
 set (CMAKE_XCODE_EFFECTIVE_PLATFORMS -iphoneos -iphonesimulator)
 set (CMAKE_XCODE_EFFECTIVE_PLATFORMS -iphoneos -iphonesimulator)
 
 
 # Set Base SDK to "Latest iOS"
 # Set Base SDK to "Latest iOS"
-set (CMAKE_OSX_SYSROOT iphoneos)    
+set (CMAKE_OSX_SYSROOT iphoneos)
 
 
 # Obtain iOS sysroot path
 # Obtain iOS sysroot path
-execute_process (COMMAND xcodebuild -version -sdk ${CMAKE_OSX_SYSROOT} Path OUTPUT_VARIABLE IOS_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE)   
+execute_process (COMMAND xcodebuild -version -sdk ${CMAKE_OSX_SYSROOT} Path OUTPUT_VARIABLE IOS_SYSROOT OUTPUT_STRIP_TRAILING_WHITESPACE)
 
 
 set (CMAKE_FIND_ROOT_PATH ${IOS_SYSROOT})
 set (CMAKE_FIND_ROOT_PATH ${IOS_SYSROOT})
 
 

+ 2 - 0
CMake/Modules/AtomicLinux.cmake

@@ -1,5 +1,7 @@
 include(AtomicDesktop)
 include(AtomicDesktop)
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "LINUX")
+
 add_definitions(-DATOMIC_PLATFORM_LINUX -DATOMIC_OPENGL -DKNET_UNIX -DHAVE_INT64_T)
 add_definitions(-DATOMIC_PLATFORM_LINUX -DATOMIC_OPENGL -DKNET_UNIX -DHAVE_INT64_T)
 
 
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -std=gnu++0x")
 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -std=gnu++0x")

+ 2 - 0
CMake/Modules/AtomicMac.cmake

@@ -2,6 +2,8 @@
 include (BundleUtilities)
 include (BundleUtilities)
 include(AtomicDesktop)
 include(AtomicDesktop)
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "MACOSX")
+
 add_definitions(-DATOMIC_PLATFORM_OSX -DATOMIC_OPENGL -DKNET_UNIX)
 add_definitions(-DATOMIC_PLATFORM_OSX -DATOMIC_OPENGL -DKNET_UNIX)
 
 
 list (APPEND ATOMIC_LINK_LIBRARIES GLEW)
 list (APPEND ATOMIC_LINK_LIBRARIES GLEW)

+ 2 - 0
CMake/Modules/AtomicWeb.cmake

@@ -1,4 +1,6 @@
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "WEB")
+
 add_definitions(-DATOMIC_PLATFORM_WEB)
 add_definitions(-DATOMIC_PLATFORM_WEB)
 add_definitions(-DATOMIC_OPENGL -Wno-warn-absolute-paths -DATOMIC_TBUI)
 add_definitions(-DATOMIC_OPENGL -Wno-warn-absolute-paths -DATOMIC_TBUI)
 
 

+ 2 - 1
CMake/Modules/AtomicWindows.cmake

@@ -1,7 +1,8 @@
 
 
-
 include(AtomicDesktop)
 include(AtomicDesktop)
 
 
+set (JAVASCRIPT_BINDINGS_PLATFORM "WINDOWS")
+
 #set (CMAKE_DEBUG_POSTFIX _d)
 #set (CMAKE_DEBUG_POSTFIX _d)
 
 
 set (D3DCOMPILER_47_DLL ${CMAKE_SOURCE_DIR}/Build/Windows/Binaries/x64/D3DCompiler_47.dll)
 set (D3DCOMPILER_47_DLL ${CMAKE_SOURCE_DIR}/Build/Windows/Binaries/x64/D3DCompiler_47.dll)

+ 20 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/main.js

@@ -0,0 +1,20 @@
+
+var ui = require("./ui/ui");
+
+var env = ToolCore.getToolEnvironment();
+
+print(env.rootSourceDir);
+
+var system = ToolCore.getToolSystem();
+
+system.loadProject("/Users/josh/Dev/atomic/AtomicExamples/NewSpaceGame");
+
+var cool = new Editor.MyJSClass();
+print(cool.aha);
+
+
+( function() {
+
+
+
+}());

+ 61 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/editorStrings.js

@@ -0,0 +1,61 @@
+
+
+
+exports.RevealInFinder = 0;
+exports.ShortcutUndo = 1;
+exports.ShortcutRedo = 2;
+exports.ShortcutCut = 3;
+exports.ShortcutCopy = 4;
+exports.ShortcutPaste = 5;
+exports.ShortcutSelectAll = 6;
+exports.ShortcutFind = 7;
+exports.ShortcutFindNext = 8;
+exports.ShortcutFindPrev = 9;
+exports.ShortcutBeautify = 10;
+exports.ShortcutCloseFile = 11;
+exports.ShortcutSaveFile = 12;
+exports.ShortcutPlay = 13;
+exports.ShortcutBuild = 14;
+exports.ShortcutBuildSettings = 15;
+
+exports.str = function(value) {
+
+  var string = strings[value];
+
+  if (typeof string === 'undefined') {
+      return "";
+  }
+
+  return string;
+}
+
+var strings = {};
+
+var shortcutKey = "⌘";
+
+strings[exports.RevealInFinder] = "Reveal in Finder";
+
+// Mac
+strings[exports.ShortcutRedo] ="⇧⌘Z";
+strings[exports.ShortcutFindNext] = "⌘G";
+strings[exports.ShortcutFindPrev] = "⇧⌘G";
+strings[exports.ShortcutBuildSettings] = "⇧⌘B";
+
+
+// General
+strings[exports.ShortcutUndo] = shortcutKey + "Z";
+
+strings[exports.ShortcutCut] = shortcutKey + "X";
+strings[exports.ShortcutCopy] = shortcutKey + "C";
+strings[exports.ShortcutPaste] = shortcutKey + "V";
+strings[exports.ShortcutSelectAll] = shortcutKey + "A";
+strings[exports.ShortcutFind] = shortcutKey + "F";
+
+strings[exports.ShortcutBeautify] = shortcutKey + "I";
+
+strings[exports.ShortcutSaveFile] = shortcutKey + "S";
+strings[exports.ShortcutCloseFile] = shortcutKey + "W";
+
+strings[exports.ShortcutPlay] = shortcutKey + "P";
+
+strings[exports.ShortcutBuild] = shortcutKey +"B";

+ 15 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/hierarchyframe.js

@@ -0,0 +1,15 @@
+var UI = Atomic.UI;
+var UIWidget = Atomic.UIWidget;
+
+var mainframe = require("./mainframe").mainframe;
+
+var hierarchyframe = exports.hierarchyframe = new UIWidget();
+
+hierarchyframe.gravity = UI.GRAVITY_TOP_BOTTOM;
+
+// load the UI
+hierarchyframe.load("AtomicEditor/editor/ui/hierarchyframe.tb.txt");
+
+// snap the frame into place
+var hierarchycontainer = mainframe.getWidget("hierarchycontainer");
+hierarchycontainer.addChild(hierarchyframe);

+ 162 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/mainframe.js

@@ -0,0 +1,162 @@
+var graphics = Atomic.getGraphics();
+var UI = Atomic.UI;
+var UIWidget = Atomic.UIWidget;
+var UIMenuWindow = Atomic.UIMenuWindow;
+var editorStrings = require("./editorStrings");
+var utils = require("./utils");
+var ui = require("./ui");
+
+// Create the main frame
+var mainframe = exports.mainframe = new UIWidget();
+
+ui.view.addChild(mainframe);
+
+// Set it to be the size of the entire window
+mainframe.setSize(graphics.width, graphics.height);
+
+// load the UI
+mainframe.load("AtomicEditor/editor/ui/mainframe.tb.txt");
+
+// Subscribe to graphics subsystems screen mode switching, so we can adjust the widget size
+mainframe.subscribeToEvent(graphics, "ScreenMode", function(data) {
+
+  mainframe.setSize(data.width, data.height);
+
+});
+
+mainframe.handleMenuAtomicEditor = function(data) {
+
+  var target = data.target;
+
+  if (target && target.id == "menu atomic editor popup") {
+
+    if (data.refid == "quit") {
+
+      // todo, send a request for file saves, etc
+      Atomic.getEngine().exit();
+
+      return true;
+
+    }
+
+  }
+
+  if (target && target.id == "menu edit popup") {
+
+    if (data.refid == "edit play") {
+
+      new ToolCore.PlayCmd().run();
+
+      return true;
+
+    }
+
+  }
+
+
+}
+
+mainframe.handleMenuBarEvent = function(data) {
+
+  if (data.type == UI.EVENT_TYPE_CLICK) {
+
+    if (mainframe.handleMenuAtomicEditor(data)) return true;
+
+    var target = data.target;
+
+    var src = srcLookup[target.id];
+
+    if (src) {
+
+      var menu = new UIMenuWindow(target, target.id + " popup");
+      menu.show(src);
+      return true;
+
+    }
+
+  }
+
+  return false;
+
+}
+
+mainframe.subscribeToEvent(mainframe, "WidgetEvent", function(data) {
+
+  if (mainframe.handleMenuBarEvent(data)) return true;
+
+  return false;
+
+});
+
+var srcLookup = {};
+
+var editorItems = {
+  "About Atomic Editor": "about atomic editor",
+  "-1" : null,
+  "Manage License" : "manage license",
+  "-2" : null,
+  "Check for Updates" : "check update",
+  "-3" : null,
+  "Quit" : "quit"
+};
+
+var editItems = {
+
+  "Undo" : ["edit undo", editorStrings.ShortcutUndo],
+  "Redo" : ["edit redo", editorStrings.ShortcutRedo],
+  "-1" : null,
+  "Cut" : ["edit cut", editorStrings.ShortcutCut],
+  "Copy" : ["edit copy", editorStrings.ShortcutCopy],
+  "Paste" : ["edit paste", editorStrings.ShortcutPaste],
+  "Select All" : ["edit select all", editorStrings.ShortcutSelectAll],
+  "-2" : null,
+  "Find" : ["edit find", editorStrings.ShortcutFind],
+  "Find Next" : ["edit find next", editorStrings.ShortcutFindNext],
+  "Find Prev" : ["edit find prev", editorStrings.ShortcutFindPrev],
+  "-3" : null,
+  "Format Code" : ["edit format code", editorStrings.ShortcutBeautify],
+  "-4" : null,
+  "Play" : ["edit play", editorStrings.ShortcutPlay]
+};
+
+var fileItems = {
+  "New Project" : "new project",
+  "Open Project" : "open project",
+  "Save Project" : "save project",
+  "-1" : null,
+  "Close Project" : "close project",
+  "-2" : null,
+  "Save File" : ["save file", editorStrings.ShortcutSaveFile],
+  "Close File" : ["close file", editorStrings.ShortcutCloseFile],
+
+};
+
+var buildItems = {
+  "Build" : ["build project", editorStrings.ShortcutBuild],
+  "-1" : null,
+  "Build Settings" : ["build project settings", editorStrings.ShortcutBuildSettings],
+};
+
+var toolsItems = {
+  "Tiled Map Editor" : "tools tiles"
+};
+
+var helpItems = {
+  "API Documentation" : "help api",
+  "-1" : null,
+  "Forums" : "help forums",
+  "-2" : null,
+  "Atomic Game Engine on GitHub" : "help github"
+};
+
+var developerItems = {
+  "Set 1920x1080 Resolution" : "developer resolution"
+};
+
+srcLookup["menu atomic editor"] = utils.createMenuItemSource(editorItems);
+srcLookup["menu file"] = utils.createMenuItemSource(fileItems);
+srcLookup["menu edit"] = utils.createMenuItemSource(editItems);
+srcLookup["menu build"] = utils.createMenuItemSource(buildItems);
+srcLookup["menu tools"] = utils.createMenuItemSource(toolsItems);
+srcLookup["menu help"] = utils.createMenuItemSource(helpItems);
+srcLookup["menu developer"] = utils.createMenuItemSource(developerItems);

+ 31 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/projectframe.js

@@ -0,0 +1,31 @@
+var UI = Atomic.UI;
+var UIWidget = Atomic.UIWidget;
+var UIListView = Atomic.UIListView;
+var fileSystem = Atomic.getFileSystem();
+
+var mainframe = require("./mainframe").mainframe;
+
+var projectframe = exports.projectframe = new UIWidget();
+
+projectframe.gravity = UI.GRAVITY_TOP_BOTTOM;
+
+// load the UI
+projectframe.load("AtomicEditor/editor/ui/projectframe.tb.txt");
+
+// snap the project frame into place
+var projectviewcontainer = mainframe.getWidget("projectviewcontainer");
+projectviewcontainer.addChild(projectframe);
+
+var foldercontainer = projectframe.getWidget("foldercontainer");
+
+var folderList = new UIListView();
+
+folderList.id = "folderList";
+
+foldercontainer.addChild(folderList);
+
+//folderList.addItem("Resources", "Folder.icon", "myid")
+
+var folders = fileSystem.scanDir("/", "", Atomic.SCAN_DIRS, false);
+
+print(folders);

+ 8 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/ui.js

@@ -0,0 +1,8 @@
+
+// create the view first so it exists before requiring additional modules
+var view = new Atomic.UIView();
+exports.view = view;
+
+require("./mainframe");
+require("./projectframe");
+require("./hierarchyframe");

+ 37 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/javascript/ui/utils.js

@@ -0,0 +1,37 @@
+
+var editorStrings = require("./editorStrings");
+
+var UIMenuItemSource = Atomic.UIMenuItemSource;
+var UIMenuItem = Atomic.UIMenuItem;
+
+exports.createMenuItemSource = function(items) {
+
+  var src = new UIMenuItemSource();
+
+  for (var key in items) {
+    if (items.hasOwnProperty(key)) {
+
+      var value = items[key];
+
+      if (typeof value === 'string') {
+
+        src.addItem(new UIMenuItem(key, value));
+
+      } else if (value == null) {
+
+        src.addItem(new UIMenuItem(key));
+
+      } else if (typeof value === 'object' && value.length == 2) {
+
+        src.addItem(new UIMenuItem(key, value[0], editorStrings.str(value[1])));
+
+      }
+
+
+    }
+
+  }
+
+  return src;
+
+}

+ 70 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/AtomicWork.d.ts

@@ -0,0 +1,70 @@
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/Atomic.d.ts" />
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/ToolCore.d.ts" />
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/Editor.d.ts" />
+
+declare module Atomic {
+
+    export interface PathInfo {
+
+      pathName: string;
+      fileName: string;
+      ext: string;
+      
+    }
+
+    /*
+        export enum UIWidgetEventType {
+
+            EVENT_TYPE_CLICK,
+            EVENT_TYPE_LONG_CLICK,
+        EVENT_TYPE_POINTER_DOWN,
+        EVENT_TYPE_POINTER_UP,
+        EVENT_TYPE_POINTER_MOVE,
+        EVENT_TYPE_RIGHT_POINTER_DOWN,
+        EVENT_TYPE_RIGHT_POINTER_UP,
+        EVENT_TYPE_WHEEL,
+        EVENT_TYPE_CHANGED,
+        EVENT_TYPE_KEY_DOWN,
+        EVENT_TYPE_KEY_UP,
+        EVENT_TYPE_SHORTCUT,
+        EVENT_TYPE_CONTEXT_MENU,
+        EVENT_TYPE_FILE_DROP,
+        EVENT_TYPE_TAB_CHANGED,
+        EVENT_TYPE_CUSTOM
+        }
+    */
+    export interface UIWidgetEvent {
+
+        handler: UIWidget;
+        target: UIWidget;
+        type: number; /*UIWidgetEventType*/
+        x: number;
+        y: number;
+        deltax: number;
+        deltay: number;
+        count: number;
+        key: number;
+        specialkey: number;
+        modifierkeys: number;
+        refid: string;
+        touch: boolean;
+    }
+
+    export function getArguments(): Array<string>;
+    export function getEngine(): Engine;
+    export function getInput(): Input;
+    export function getGraphics(): Graphics;
+    export function getFileSystem(): FileSystem;
+
+    export function getParentPath(path: string): string;
+    export function addTrailingSlash(path: string): string;
+		export function getExtension(path: string): string;
+
+    export function splitPath(path: string): PathInfo;
+
+}
+
+declare module ToolCore {
+    export function getToolEnvironment(): ToolEnvironment;
+    export function getToolSystem(): ToolSystem;
+}

+ 73 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/editor/Editor.ts

@@ -0,0 +1,73 @@
+
+import MainFrame = require("../ui/MainFrame");
+import UIEvents = require("../ui/UIEvents");
+
+class Editor extends Atomic.ScriptObject {
+
+    project: ToolCore.Project;
+    view: Atomic.UIView;
+    mainframe: MainFrame;
+
+    static instance: Editor;
+
+    loadProject(projectPath: string): boolean {
+
+        var system = ToolCore.getToolSystem();
+
+        if (system.project) {
+
+            this.sendEvent(UIEvents.MessageModalEvent,
+                { type: "error", title: "Project already loaded", message: "Project already loaded" });
+
+            return false;
+
+        }
+
+        return system.loadProject(projectPath);
+
+    }
+
+    parseArguments() {
+
+        var args = Atomic.getArguments();
+
+        var idx = 0;
+
+        while (idx < args.length) {
+
+            if (args[idx] == "--project") {
+
+                this.loadProject(args[idx + 1]);
+
+            }
+
+            idx++;
+
+        }
+
+    }
+
+    constructor() {
+
+        super();
+
+        Editor.instance = this;
+
+        var graphics = Atomic.getGraphics();
+
+        this.view = new Atomic.UIView();
+
+        this.mainframe = new MainFrame();
+
+        this.view.addChild(this.mainframe);
+
+        // set initial size
+        this.mainframe.setSize(graphics.width, graphics.height);
+
+        this.parseArguments();
+
+    }
+
+}
+
+export = Editor;

+ 9 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/main.ts

@@ -0,0 +1,9 @@
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/Atomic.d.ts" />
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/ToolCore.d.ts" />
+/// <reference path="/Users/josh/Dev/thunderbeast/AtomicGameEngine/Bin/Editor.d.ts" />
+
+/// <reference path="./AtomicWork.d.ts" />
+
+import __Editor = require("./editor/Editor");
+
+var TheEditor = new __Editor();

File diff suppressed because it is too large
+ 541 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/acorn.js


File diff suppressed because it is too large
+ 109 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/beautify.js


+ 67 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/modules/jsutils.js

@@ -0,0 +1,67 @@
+__atomic_acorn = require('./acorn');
+__atomic_beautify = require('./beautify');
+
+exports.parseToJSON = function (source) {
+
+	var start = new Date().getTime();
+
+	var comments = [];
+
+	var ast = __atomic_acorn.parse( source, {
+    	// collect ranges for each node
+    	ranges: true,
+    	locations: true,
+    	onComment: comments,
+	});
+
+	var end = new Date().getTime();
+	var time = end - start;
+
+	// print('Acorn parse time: ' + time);
+
+	ast["comments"] = comments;
+
+	start = new Date().getTime();
+	ast = JSON.stringify(ast);
+	end = new Date().getTime();
+	time = end - start;
+
+	// print('JSON.stringify time: ' + time);
+
+	return ast;
+
+}
+
+
+exports.parseErrorCheck = function(source) {
+
+
+	try {
+
+		__atomic_acorn.parse( source, {
+	    	ranges: true,
+	    	locations: true,
+		});
+
+	} catch(e) {
+
+        //if (!(e instanceof __atomic_acorn.SyntaxError)) throw e;
+
+        if (!(e instanceof SyntaxError))
+        	return false; // rethrow?
+
+        return e;
+
+    }
+
+
+	return false;
+
+}
+
+// TODO: options
+exports.jsBeautify = function (source) {
+
+	return __atomic_beautify.js_beautify(source);
+
+}

+ 27 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/tsconfig.json

@@ -0,0 +1,27 @@
+{
+    "compilerOptions": {
+        "target": "es5",
+        "module": "commonjs",
+        "declaration": false,
+        "noImplicitAny": false,
+        "removeComments": true,
+        "noLib": false
+    },
+    "filesGlob": [
+        "./**/*.ts"
+    ],
+    "files": [
+        "./AtomicWork.d.ts",
+        "./editor/Editor.ts",
+        "./main.ts",
+        "./ui/EditorStrings.ts",
+        "./ui/MainFrame.ts",
+        "./ui/MainFrameMenu.ts",
+        "./ui/ProjectFrame.ts",
+        "./ui/ResourceFrame.ts",
+        "./ui/ScriptWidget.ts",
+        "./ui/UIEvents.ts",
+        "./ui/modal/MessageModal.ts",
+        "./ui/modal/ModalOps.ts"
+    ]
+}

+ 66 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/EditorStrings.ts

@@ -0,0 +1,66 @@
+
+export enum StringID {
+
+    RevealInFinder,
+    ShortcutUndo,
+    ShortcutRedo,
+    ShortcutCut,
+    ShortcutCopy,
+    ShortcutPaste,
+    ShortcutSelectAll,
+    ShortcutFind,
+    ShortcutFindNext,
+    ShortcutFindPrev,
+    ShortcutBeautify,
+    ShortcutCloseFile,
+    ShortcutSaveFile,
+    ShortcutPlay,
+    ShortcutBuild,
+    ShortcutBuildSettings
+}
+
+export class EditorString {
+
+    public static GetString(id: StringID): string {
+
+        return EditorString.lookup[id];
+
+    }
+
+    private static lookup: { [id: number]: string; } = {};
+
+    private static Ctor = (() => {
+
+        var lookup = EditorString.lookup;
+
+        var shortcutKey = "⌘";
+
+        lookup[StringID.RevealInFinder] = "Reveal in Finder";
+
+        // Mac
+        lookup[StringID.ShortcutRedo] = "⇧⌘Z";
+        lookup[StringID.ShortcutFindNext] = "⌘G";
+        lookup[StringID.ShortcutFindPrev] = "⇧⌘G";
+        lookup[StringID.ShortcutBuildSettings] = "⇧⌘B";
+
+        // General
+        lookup[StringID.ShortcutUndo] = shortcutKey + "Z";
+
+        lookup[StringID.ShortcutCut] = shortcutKey + "X";
+        lookup[StringID.ShortcutCopy] = shortcutKey + "C";
+        lookup[StringID.ShortcutPaste] = shortcutKey + "V";
+        lookup[StringID.ShortcutSelectAll] = shortcutKey + "A";
+        lookup[StringID.ShortcutFind] = shortcutKey + "F";
+
+        lookup[StringID.ShortcutBeautify] = shortcutKey + "I";
+
+        lookup[StringID.ShortcutSaveFile] = shortcutKey + "S";
+        lookup[StringID.ShortcutCloseFile] = shortcutKey + "W";
+
+        lookup[StringID.ShortcutPlay] = shortcutKey + "P";
+
+        lookup[StringID.ShortcutBuild] = shortcutKey + "B";
+
+    })();
+
+}

+ 121 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrame.ts

@@ -0,0 +1,121 @@
+import menubar = require("./MainFrameMenu");
+import ProjectFrame = require("./ProjectFrame");
+import ResourceFrame = require("./ResourceFrame");
+import MessageModal = require("./modal/MessageModal");
+import UIEvents = require("./UIEvents");
+
+import ScriptWidget = require("./ScriptWidget");
+
+var UI = Atomic.UI;
+
+class MainFrame extends ScriptWidget {
+
+    projectframe: ProjectFrame;
+    resourceframe: ResourceFrame;
+    inspectorframe: Editor.InspectorFrame;
+
+    inspectorlayout: Atomic.UILayout;
+
+    private messagemodal: MessageModal.MessageModal = new MessageModal.MessageModal();
+
+    constructor() {
+
+        super();
+
+        this.load("AtomicEditor/editor/ui/mainframe.tb.txt");
+
+        this.inspectorlayout = <Atomic.UILayout> this.getWidget("inspectorlayout");
+
+        this.inspectorframe = new Editor.InspectorFrame();
+        this.inspectorframe.load("AtomicEditor/editor/ui/inspectorframe.tb.txt");
+        this.inspectorlayout.addChild(this.inspectorframe);
+
+        this.projectframe = new ProjectFrame(this);
+        this.resourceframe = new ResourceFrame(this);
+
+        this.showInspectorFrame(false);
+
+        this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
+
+    }
+
+    showInspectorFrame(show: boolean) {
+
+        if (show) {
+
+            this.inspectorlayout.visibility = UI.VISIBILITY_VISIBLE;
+            this.inspectorframe.visibility = UI.VISIBILITY_VISIBLE;
+
+        } else {
+
+            this.inspectorframe.visibility = UI.VISIBILITY_GONE;
+            this.inspectorlayout.visibility = UI.VISIBILITY_GONE;
+
+        }
+
+
+    }
+
+    onEventClick(target: Atomic.UIWidget, refid: string): boolean {
+
+        if (this.handlePopupMenu(target, refid))
+            return true;
+
+        var src = menubar.getMenuItemSource(target.id);
+
+        if (src) {
+
+            var menu = new Atomic.UIMenuWindow(target, target.id + " popup");
+            menu.show(src);
+            return true;
+
+        }
+
+        return false;
+
+    }
+
+
+    handlePopupMenu(target: Atomic.UIWidget, refid: string): boolean {
+
+        if (target.id == "menu atomic editor popup") {
+
+            if (refid == "quit")
+                Atomic.getEngine().exit();
+
+        }
+
+        if (target.id == "menu edit popup") {
+
+            if (refid == "edit play") {
+
+                new ToolCore.PlayCmd().run();
+                return true;
+
+            }
+
+            return false;
+
+        }
+    }
+
+    handleResourceEditorChanged(data):void {
+
+        var editor = <Editor.ResourceEditor> data.editor;
+
+        if (editor) {
+
+            this.showInspectorFrame(editor.requiresInspector());
+
+        } else {
+
+            this.showInspectorFrame(false);
+
+        }
+
+    }
+
+
+}
+
+export = MainFrame;

+ 78 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/MainFrameMenu.ts

@@ -0,0 +1,78 @@
+
+import strings = require("./EditorStrings");
+
+var UIMenuItemSource = Atomic.UIMenuItemSource;
+var UIMenuItem = Atomic.UIMenuItem;
+
+var StringID = strings.StringID;
+
+var editorItems = {
+    "About Atomic Editor": "about atomic editor",
+    "-1": null,
+    "Manage License": "manage license",
+    "-2": null,
+    "Check for Updates": "check update",
+    "-3": null,
+    "Quit": "quit"
+};
+
+var editItems = {
+
+    "Undo": ["edit undo", StringID.ShortcutUndo],
+    "Redo": ["edit redo", StringID.ShortcutRedo],
+    "-1": null,
+    "Cut": ["edit cut", StringID.ShortcutCut],
+    "Copy": ["edit copy", StringID.ShortcutCopy],
+    "Paste": ["edit paste", StringID.ShortcutPaste],
+    "Select All": ["edit select all", StringID.ShortcutSelectAll],
+    "-2": null,
+    "Find": ["edit find", StringID.ShortcutFind],
+    "Find Next": ["edit find next", StringID.ShortcutFindNext],
+    "Find Prev": ["edit find prev", StringID.ShortcutFindPrev],
+    "-3": null,
+    "Format Code": ["edit format code", StringID.ShortcutBeautify],
+    "-4": null,
+    "Play": ["edit play", StringID.ShortcutPlay]
+
+};
+
+var srcLookup = {};
+
+srcLookup["menu atomic editor"] = createMenuItemSource(editorItems);
+srcLookup["menu edit"] = createMenuItemSource(editItems);
+
+export function getMenuItemSource(id: string): Atomic.UIMenuItemSource {
+    return srcLookup[id];
+}
+
+function createMenuItemSource(items: any): Atomic.UIMenuItemSource {
+
+    var src = new UIMenuItemSource();
+
+    for (var key in items) {
+
+        if (items.hasOwnProperty(key)) {
+
+            var value = items[key];
+
+            if (typeof value === 'string') {
+
+                src.addItem(new UIMenuItem(key, value));
+
+            } else if (value == null) {
+
+                src.addItem(new UIMenuItem(key));
+
+            } else if (typeof value === 'object' && value.length == 2) {
+
+                src.addItem(new UIMenuItem(key, value[0], strings.EditorString.GetString(value[1])));
+
+            }
+
+        }
+
+    }
+
+    return src;
+
+}

+ 316 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ProjectFrame.ts

@@ -0,0 +1,316 @@
+
+
+import ScriptWidget = require("./ScriptWidget");
+import Editor = require("../editor/Editor");
+
+var UI = Atomic.UI;
+
+class ProjectFrame extends ScriptWidget {
+
+    folderList: Atomic.UIListView;
+
+    constructor(parent: Atomic.UIWidget) {
+
+        super();
+
+        this.load("AtomicEditor/editor/ui/projectframe.tb.txt");
+
+        this.gravity = UI.GRAVITY_TOP_BOTTOM;
+
+        var projectviewcontainer = parent.getWidget("projectviewcontainer");
+
+        projectviewcontainer.addChild(this);
+
+        var foldercontainer = this.getWidget("foldercontainer");
+
+        var folderList = this.folderList = new Atomic.UIListView();
+
+        folderList.rootList.id = "folderList_";
+
+        foldercontainer.addChild(folderList);
+
+        // events
+        this.subscribeToEvent("ProjectLoaded", (data) => this.handleProjectLoaded(data));
+
+    }
+
+
+    handleProjectLoaded(data) {
+
+        this.refresh();
+
+    }
+
+    handleWidgetEvent(data): boolean {
+
+        if (data.type == Atomic.UI.EVENT_TYPE_CLICK) {
+
+            var fs = Atomic.getFileSystem();
+
+            if (data.target && data.target.id.length) {
+
+                var id = data.target.id;
+
+                if (id == "folderList_") {
+
+                    var list = <Atomic.UISelectList> data.target;
+
+                    var selectedId = list.selectedItemID;
+
+                    if (fs.dirExists(selectedId)) {
+                        this.selectCurrentContentFolder(selectedId);
+                    }
+
+                    return true;
+
+                }
+
+                if (id == "..") {
+                    var parentPath = Atomic.getParentPath(this.currentContentFolder);
+                    this.selectCurrentContentFolder(parentPath);
+                    return true;
+                }
+
+                if (fs.dirExists(id)) {
+                    this.selectCurrentContentFolder(id);
+                    return true;
+                }
+
+                if (fs.fileExists(id)) {
+                    this.sendEvent("EditResource", { "path": id });
+                    return true;
+                }
+
+            }
+
+        }
+
+        return false;
+
+    }
+
+
+    refresh() {
+
+        var cfolder = this.currentContentFolder;
+        this.currentContentFolder = "";
+        this.refreshFolders();
+
+    }
+
+    private refreshContent(fullpath: string) {
+
+        var system = ToolCore.getToolSystem();
+        var project = system.project;
+
+        this.currentContentFolder = fullpath;
+
+        var folders = new Array<string>();
+        var content = new Array<string>();
+
+        this.scanContentDirForContent(folders, content, fullpath);
+
+        var container: Atomic.UILayout = <Atomic.UILayout> this.getWidget("contentcontainer");
+
+        container.deleteAllChildren();
+
+        if (fullpath != project.resourcePath) {
+
+            var lp = new Atomic.UILayoutParams();
+            lp.height = 20;
+
+            var fd = new Atomic.UIFontDescription();
+            fd.id = "Vera";
+            fd.size = 11;
+
+            var button = new Atomic.UIButton();
+            button.gravity = UI.GRAVITY_LEFT;
+            button.text = "..                     ";
+            button.id = "..";
+            button.skinBg = "TBButton.flat";
+            button.layoutParams = lp;
+            button.fontDescription = fd;
+
+            container.addChild(button);
+
+        }
+
+        for (var folder of folders) {
+
+            var contentID = Atomic.addTrailingSlash(fullpath + folder);
+            container.addChild(this.createButtonLayout(contentID, folder));
+        }
+
+        for (var c of content) {
+
+            var contentID = Atomic.addTrailingSlash(fullpath) + c;
+            container.addChild(this.createButtonLayout(contentID, c));
+        }
+
+
+    }
+
+    private recursiveAddFolder(parentItemID: number, fullpath: string, folderName: string) {
+
+        var folderList = this.folderList;
+
+        var childItemID = folderList.addChildItem(parentItemID, folderName, "Folder.icon", fullpath);
+
+        var dirs = new Array<string>();
+
+        this.scanDirForFolders(dirs, fullpath);
+
+        for (var folder of dirs) {
+            this.recursiveAddFolder(childItemID, fullpath + folder + "/", folder);
+        }
+
+    }
+
+    private refreshFolders() {
+
+        var system = ToolCore.getToolSystem();
+        var project = system.project;
+
+        var folderList = this.folderList;
+
+        folderList.deleteAllItems();
+
+        var resourcesID = folderList.addRootItem("Resources", "Folder.icon", project.resourcePath);
+
+        var dirs = new Array<string>();
+
+        this.scanDirForFolders(dirs, project.resourcePath);
+
+        for (var folder of dirs) {
+            this.recursiveAddFolder(resourcesID, project.resourcePath + folder + "/", folder);
+        }
+
+        folderList.setExpanded(resourcesID, true);
+        this.refreshContent(project.resourcePath);
+        folderList.rootList.value = 0;
+
+
+    }
+
+    private selectCurrentContentFolder(folder: string) {
+
+        this.folderList.selectItemByID(folder);
+        if (this.currentContentFolder != folder)
+            this.refreshContent(folder);
+    }
+
+    private endsWith(str: string, suffix: string): boolean {
+        return str && str.indexOf(suffix, str.length - suffix.length) !== -1;
+    }
+
+    private scanDirForFolders(dirs: Array<string>, fullpath: string) {
+
+        var fileSystem = Atomic.getFileSystem();
+
+        dirs.length = 0;
+
+        var folders = fileSystem.scanDir(fullpath, "", Atomic.SCAN_DIRS, false);
+
+        for (var folder of folders) {
+
+            if (folder == "." || folder == ".." || this.endsWith(folder, "/..") || this.endsWith(folder, "/."))
+                continue;
+
+            dirs.push(folder);
+
+        }
+
+    }
+
+
+    private scanContentDirForContent(folders: Array<string>, content: Array<string>, fullPath: string) {
+
+        var fileSystem = Atomic.getFileSystem();
+
+        folders.length = content.length = 0;
+
+        var _folders = fileSystem.scanDir(fullPath, "", Atomic.SCAN_DIRS, false);
+
+        for (var folder of _folders) {
+
+            if (folder == "." || folder == ".." || this.endsWith(folder, "/..") || this.endsWith(folder, "/."))
+                continue;
+
+            folders.push(folder);
+
+        }
+
+        var _content = fileSystem.scanDir(fullPath, "", Atomic.SCAN_FILES, false);
+
+        for (var c of _content) {
+
+            content.push(c);
+
+        }
+
+    }
+
+    private createButtonLayout(fullpath: string, text: string): Atomic.UILayout {
+
+        var system = ToolCore.getToolSystem();
+        var project = system.project;
+        var fs = Atomic.getFileSystem();
+
+        var pathinfo = Atomic.splitPath(fullpath);
+
+        var bitmapID = "Folder.icon";
+
+        if (fs.fileExists(fullpath)) {
+            bitmapID = "FileBitmap";
+        }
+
+        if (pathinfo.ext == ".js") {
+            if (project.isComponentsDirOrFile(fullpath)) {
+                bitmapID = "ComponentBitmap";
+            }
+            else {
+                bitmapID = "JavascriptBitmap";
+            }
+        }
+
+        var blayout = new Atomic.UILayout();
+
+        blayout.gravity = UI.GRAVITY_LEFT;
+
+        var spacer = new Atomic.UIWidget();
+        spacer.rect = [0, 0, 8, 8];
+        blayout.addChild(spacer);
+
+        var button = new Atomic.UIButton();
+
+        var lp = new Atomic.UILayoutParams;
+        lp.height = 20;
+
+        var fd = new Atomic.UIFontDescription();
+        fd.id = "Vera";
+        fd.size = 11;
+
+        button.gravity = UI.GRAVITY_LEFT;
+
+        var image = new Atomic.UISkinImage(bitmapID);
+        image.rect = [0, 0, 12, 12];
+        image.gravity = UI.GRAVITY_RIGHT;
+        blayout.addChild(image);
+
+        button.id = fullpath;
+        button.layoutParams = lp;
+        button.fontDescription = fd;
+        button.text = text;
+        button.skinBg = "TBButton.flat";
+        blayout.addChild(button);
+
+        return blayout;
+    }
+
+
+    private currentContentFolder: string;
+
+
+}
+
+export = ProjectFrame;

+ 187 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ResourceFrame.ts

@@ -0,0 +1,187 @@
+import ScriptWidget = require("./ScriptWidget");
+import UIEvents = require("./UIEvents");
+
+var UI = Atomic.UI;
+
+// the root content of editor widgets (rootContentWidget property) are extended with an editor field
+// so we can access the editor they belong to from the widget itself
+interface EditorRootContentWidget extends Atomic.UIWidget {
+    editor: Editor.ResourceEditor;
+}
+
+class ResourceFrame extends ScriptWidget {
+
+    tabcontainer: Atomic.UITabContainer;
+    resourceLayout: Atomic.UILayout;
+    resourceViewContainer: Atomic.UILayout;
+    currentResourceEditor: Editor.ResourceEditor;
+
+    // editors have a rootCotentWidget which is what is a child of the tab container
+
+    // editors can be looked up by the full path of what they are editing
+    editors: { [path: string]: Editor.ResourceEditor; } = {};
+
+    show(value: boolean) {
+
+        if (value) {
+
+        }
+
+    }
+
+    handleEditResource(ev: UIEvents.EditorResourceEvent) {
+
+        var path = ev.path;
+
+        if (this.editors[path]) {
+
+            return;
+
+        }
+
+        var ext = Atomic.getExtension(path);
+
+        var editor: Editor.ResourceEditor = null;
+
+        if (ext == ".js") {
+
+            editor = new Editor.JSResourceEditor(path, this.tabcontainer);
+
+        } else if (ext == ".scene") {
+
+            editor = new Editor.SceneEditor3D(path, this.tabcontainer);
+
+        }
+
+        if (editor) {
+
+            // cast and add editor lookup on widget itself
+            (<EditorRootContentWidget> editor.rootContentWidget).editor = editor;
+
+            this.editors[path] = editor;
+            this.tabcontainer.currentPage = this.tabcontainer.numPages - 1;
+            editor.setFocus();
+        }
+
+
+    }
+
+    navigateToResource(fullpath: string, lineNumber = -1, tokenPos: number = -1) {
+
+        if (!this.editors[fullpath]) {
+            return;
+        }
+
+        var editor = this.editors[fullpath];
+        var root = this.tabcontainer.contentRoot;
+
+        var i = 0;
+
+        for (var child = root.firstChild; child; child = child.next, i++) {
+            if (editor.rootContentWidget == child) {
+                break;
+            }
+        }
+
+        if (i < this.tabcontainer.numPages) {
+
+            this.tabcontainer.currentPage = i;
+
+            editor.setFocus();
+
+            // this cast could be better
+            var ext = Atomic.getExtension(fullpath);
+
+            if (ext == ".js" && lineNumber != -1) {
+                (<Editor.JSResourceEditor>editor).gotoLineNumber(lineNumber);
+            }
+            else if (ext == ".js" && tokenPos != -1) {
+                (<Editor.JSResourceEditor>editor).gotoTokenPos(tokenPos);
+            }
+
+        }
+
+    }
+
+    handleCloseResourceEditor(data) {
+
+        var editor = <Editor.ResourceEditor> data.editor;
+        var navigate = <boolean> data.navigateToAvailableResource;
+
+        // remove from lookup
+        delete this.editors[editor.fullPath];
+
+        var root = this.tabcontainer.contentRoot;
+
+        root.removeChild(editor.rootContentWidget);
+
+        this.tabcontainer.currentPage = -1;
+
+        if (navigate) {
+
+            var keys = Object.keys(this.editors);
+
+            if (keys.length) {
+
+                this.navigateToResource(keys[keys.length - 1]);
+
+            }
+
+        }
+
+    }
+
+    handleResourceEditorChanged(data) {
+
+        var editor = <Editor.ResourceEditor> data.editor;
+        this.currentResourceEditor = editor;
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
+
+        if (ev.type == Atomic.UI.EVENT_TYPE_TAB_CHANGED && ev.target == this.tabcontainer) {
+            var w = <EditorRootContentWidget> this.tabcontainer.currentPageWidget;
+
+            if (w && w.editor) {
+
+                if (this.currentResourceEditor != w.editor) {
+
+                    this.sendEvent(UIEvents.ResourceEditorChanged, { editor: w.editor });
+
+                }
+
+            }
+
+        }
+
+        // bubble
+        return false;
+
+    }
+
+    constructor(parent: Atomic.UIWidget) {
+
+        super();
+
+        this.load("AtomicEditor/editor/ui/resourceframe.tb.txt");
+
+        this.gravity = UI.GRAVITY_ALL;
+
+        this.resourceViewContainer = <Atomic.UILayout> parent.getWidget("resourceviewcontainer");
+        this.tabcontainer = <Atomic.UITabContainer> this.getWidget("tabcontainer");
+        this.resourceLayout = <Atomic.UILayout> this.getWidget("resourcelayout");
+
+        this.resourceViewContainer.addChild(this);
+
+        this.subscribeToEvent(UIEvents.EditResource, (data) => this.handleEditResource(data));
+        this.subscribeToEvent(UIEvents.CloseResourceEditor, (data) => this.handleCloseResourceEditor(data));
+        this.subscribeToEvent(UIEvents.ResourceEditorChanged, (data) => this.handleResourceEditorChanged(data));
+
+        this.subscribeToEvent("WidgetEvent", (data) => this.handleWidgetEvent(data));
+
+    }
+
+}
+
+export = ResourceFrame;

+ 36 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/ScriptWidget.ts

@@ -0,0 +1,36 @@
+
+
+class ScriptWidget extends Atomic.UIWidget {
+
+    constructor() {
+
+        super();
+
+        // JS way of binding method
+        // this.subscribeToEvent(this, "WidgetEvent", this.handleWidgetEvent.bind(this));
+
+        // TypeScript-ey
+        this.subscribeToEvent(this, "WidgetEvent", (data) => this.handleWidgetEvent(data));
+
+    }
+
+    onEventClick(target: Atomic.UIWidget, refid: string): boolean {
+
+        return false;
+
+    }
+
+    handleWidgetEvent(ev: Atomic.UIWidgetEvent): void {
+
+        if (ev.type == Atomic.UI.EVENT_TYPE_CLICK) {
+
+            this.onEventClick(ev.target, ev.refid);
+            return;
+
+        }
+
+    }
+
+}
+
+export = ScriptWidget;

+ 14 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/UIEvents.ts

@@ -0,0 +1,14 @@
+
+export const MessageModalEvent = "MessageModalEvent";
+
+export const EditResource = "EditResource";
+
+export interface EditorResourceEvent {
+
+  // The full path to the resource to edit
+  path: string;
+
+}
+
+export const CloseResourceEditor = "CloseResourceEditor";
+export const ResourceEditorChanged = "ResourceEditorChanged";

+ 34 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/modal/MessageModal.ts

@@ -0,0 +1,34 @@
+
+import UIEvents = require("../UIEvents");
+import Editor = require("../../editor/Editor");
+
+export class MessageModal extends Atomic.ScriptObject
+{
+
+  showErrorWindow(title:string, message:string):void {
+
+    var mainframe = Editor.getEditor().mainframe;
+
+    new Atomic.UIMessageWindow(mainframe, "modal_error").show(title, message, 640, 360);
+
+  }
+
+  constructor() {
+
+    super();
+
+    // Subscribe to graphics subsystems screen mode switching, so we can adjust the widget size
+    this.subscribeToEvent(UIEvents.MessageModalEvent, (data) => {
+
+      if (data.type == "error") {
+
+        this.showErrorWindow(data.title, data.message);
+
+      }
+
+    });
+
+
+  }
+
+}

+ 0 - 0
Data/AtomicEditor/Resources/EditorData/AtomicEditor/typescript/ui/modal/ModalOps.ts


+ 9 - 9
Rakefile

@@ -21,11 +21,11 @@ CMAKE_IOS_BUILD_FOLDER = "#{ARTIFACTS_FOLDER}/IOS_Build"
 CMAKE_WEB_BUILD_FOLDER = "#{ARTIFACTS_FOLDER}/Web_Build"
 CMAKE_WEB_BUILD_FOLDER = "#{ARTIFACTS_FOLDER}/Web_Build"
 CMAKE_LINUX_BUILD_FOLDER = "#{ARTIFACTS_FOLDER}/Linux_Build"
 CMAKE_LINUX_BUILD_FOLDER = "#{ARTIFACTS_FOLDER}/Linux_Build"
 
 
-JSBIND_BIN_MACOSX = "#{CMAKE_MACOSX_BUILD_FOLDER}/Source/AtomicJS/JSBind/Release/JSBind"
+ATOMICTOOL_BIN_MACOSX = "#{CMAKE_MACOSX_BUILD_FOLDER}/Source/AtomicTool/Release/AtomicTool"
 
 
 namespace :build  do
 namespace :build  do
 
 
-  task :macosx_jsbind do
+  task :macosx_atomictool do
 
 
     if !Dir.exists?("#{CMAKE_MACOSX_BUILD_FOLDER}")
     if !Dir.exists?("#{CMAKE_MACOSX_BUILD_FOLDER}")
       FileUtils.mkdir_p(CMAKE_MACOSX_BUILD_FOLDER)
       FileUtils.mkdir_p(CMAKE_MACOSX_BUILD_FOLDER)
@@ -34,7 +34,7 @@ namespace :build  do
     Dir.chdir(CMAKE_MACOSX_BUILD_FOLDER) do
     Dir.chdir(CMAKE_MACOSX_BUILD_FOLDER) do
 
 
       sh "cmake ../../ -G Xcode -DCMAKE_BUILD_TYPE=Release"
       sh "cmake ../../ -G Xcode -DCMAKE_BUILD_TYPE=Release"
-      sh "xcodebuild -target JSBind -configuration Release"
+      sh "xcodebuild -target AtomicTool -configuration Release"
 
 
     end
     end
 
 
@@ -93,14 +93,14 @@ namespace :build  do
 
 
 
 
   #IOS, dependent on macosx for JSBind
   #IOS, dependent on macosx for JSBind
-  task :ios =>  "build:macosx_jsbind" do
+  task :ios =>  "build:macosx_atomictool" do
 
 
       if !Dir.exists?("#{CMAKE_IOS_BUILD_FOLDER}")
       if !Dir.exists?("#{CMAKE_IOS_BUILD_FOLDER}")
         FileUtils.mkdir_p(CMAKE_IOS_BUILD_FOLDER)
         FileUtils.mkdir_p(CMAKE_IOS_BUILD_FOLDER)
       end
       end
 
 
       Dir.chdir(CMAKE_IOS_BUILD_FOLDER) do
       Dir.chdir(CMAKE_IOS_BUILD_FOLDER) do
-        sh "#{JSBIND_BIN_MACOSX} #{$RAKE_ROOT} IOS"
+        sh "#{ATOMICTOOL_BIN_MACOSX} bind #{$RAKE_ROOT} Source/AtomicJS/Packages/Atomic/ IOS"
         sh "cmake -DIOS=1 -DCMAKE_BUILD_TYPE=Release -G Xcode ../../"
         sh "cmake -DIOS=1 -DCMAKE_BUILD_TYPE=Release -G Xcode ../../"
         # the -s option adds $KEYCHAIN to the search scope, while the -d option adds $KEYCHAIN to the system domain; both are needed
         # the -s option adds $KEYCHAIN to the search scope, while the -d option adds $KEYCHAIN to the system domain; both are needed
         sh "security -v list-keychains -d system -s /Users/jenkins/Library/Keychains/codesign.keychain"
         sh "security -v list-keychains -d system -s /Users/jenkins/Library/Keychains/codesign.keychain"
@@ -110,7 +110,7 @@ namespace :build  do
 
 
   end
   end
 
 
-  task :android =>  "build:macosx_jsbind" do
+  task :android =>  "build:macosx_atomictool" do
 
 
       if !Dir.exists?("#{CMAKE_ANDROID_BUILD_FOLDER}")
       if !Dir.exists?("#{CMAKE_ANDROID_BUILD_FOLDER}")
         FileUtils.mkdir_p(CMAKE_ANDROID_BUILD_FOLDER)
         FileUtils.mkdir_p(CMAKE_ANDROID_BUILD_FOLDER)
@@ -118,21 +118,21 @@ namespace :build  do
 
 
       Dir.chdir(CMAKE_ANDROID_BUILD_FOLDER) do
       Dir.chdir(CMAKE_ANDROID_BUILD_FOLDER) do
 
 
-        sh "#{JSBIND_BIN_MACOSX} #{$RAKE_ROOT} ANDROID"
+        sh "#{ATOMICTOOL_BIN_MACOSX} bind #{$RAKE_ROOT} Source/AtomicJS/Packages/Atomic/ ANDROID"
         sh "cmake -DCMAKE_TOOLCHAIN_FILE=#{$RAKE_ROOT}/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../"
         sh "cmake -DCMAKE_TOOLCHAIN_FILE=#{$RAKE_ROOT}/CMake/Toolchains/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../"
         sh "make -j4"
         sh "make -j4"
       end
       end
 
 
   end
   end
 
 
-  task :web => "build:macosx_jsbind" do
+  task :web => "build:macosx_atomictool" do
 
 
       if !Dir.exists?("#{CMAKE_WEB_BUILD_FOLDER}")
       if !Dir.exists?("#{CMAKE_WEB_BUILD_FOLDER}")
         FileUtils.mkdir_p(CMAKE_WEB_BUILD_FOLDER)
         FileUtils.mkdir_p(CMAKE_WEB_BUILD_FOLDER)
       end
       end
 
 
       Dir.chdir(CMAKE_WEB_BUILD_FOLDER) do
       Dir.chdir(CMAKE_WEB_BUILD_FOLDER) do
-        sh "#{JSBIND_BIN_MACOSX} #{$RAKE_ROOT} WEB"
+        sh "#{ATOMICTOOL_BIN_MACOSX} bind #{$RAKE_ROOT} Source/AtomicJS/Packages/Atomic/ WEB"
         sh "cmake -DEMSCRIPTEN=1 -DATOMIC_BUILD_2D=1 -DCMAKE_TOOLCHAIN_FILE=#{$RAKE_ROOT}/CMake/Toolchains/emscripten.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../"
         sh "cmake -DEMSCRIPTEN=1 -DATOMIC_BUILD_2D=1 -DCMAKE_TOOLCHAIN_FILE=#{$RAKE_ROOT}/CMake/Toolchains/emscripten.toolchain.cmake -DCMAKE_BUILD_TYPE=Release ../../"
         sh "make -j4"
         sh "make -j4"
       end
       end

+ 2 - 1
Source/Atomic/Core/Object.h

@@ -106,7 +106,8 @@ public:
     /// Return object category. Categories are (optionally) registered along with the object factory. Return an empty string if the object category is not registered.
     /// Return object category. Categories are (optionally) registered along with the object factory. Return an empty string if the object category is not registered.
     const String& GetCategory() const;
     const String& GetCategory() const;
 
 
-    bool IsObject() const { return true; }
+    virtual bool IsObject() const { return true; }
+    static const Atomic::String& GetTypeNameStatic() { static const Atomic::String typeNameStatic("Object"); return typeNameStatic; }
     
     
 protected:
 protected:
     /// Execution context.
     /// Execution context.

+ 0 - 121
Source/Atomic/Engine/DebugHud.h

@@ -1,124 +1,3 @@
 #ifdef __disabled
 #ifdef __disabled
 
 
-//
-// Copyright (c) 2008-2014 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "../Core/Object.h"
-#include "../Core/Timer.h"
-
-namespace Atomic
-{
-
-class Engine;
-class Font;
-class Text;
-class XMLFile;
-
-static const unsigned DEBUGHUD_SHOW_NONE = 0x0;
-static const unsigned DEBUGHUD_SHOW_STATS = 0x1;
-static const unsigned DEBUGHUD_SHOW_MODE = 0x2;
-static const unsigned DEBUGHUD_SHOW_PROFILER = 0x4;
-static const unsigned DEBUGHUD_SHOW_ALL = 0x7;
-
-/// Displays rendering stats and profiling information.
-class ATOMIC_API DebugHud : public Object
-{
-    OBJECT(DebugHud);
-
-public:
-    /// Construct.
-    DebugHud(Context* context);
-    /// Destruct.
-    ~DebugHud();
-
-    /// Update. Called by HandlePostUpdate().
-    void Update();
-    /// Set UI elements' style from an XML file.
-    void SetDefaultStyle(XMLFile* style);
-    /// Set elements to show.
-    void SetMode(unsigned mode);
-    /// Set maximum profiler block depth, default unlimited.
-    void SetProfilerMaxDepth(unsigned depth);
-    /// Set profiler accumulation interval in seconds.
-    void SetProfilerInterval(float interval);
-    /// Set whether to show 3D geometry primitive/batch count only. Default false.
-    void SetUseRendererStats(bool enable);
-    /// Toggle elements.
-    void Toggle(unsigned mode);
-    /// Toggle all elements.
-    void ToggleAll();
-
-    /// Return the UI style file.
-    XMLFile* GetDefaultStyle() const;
-    /// Return rendering stats text.
-    Text* GetStatsText() const { return statsText_; }
-    /// Return rendering mode text.
-    Text* GetModeText() const { return modeText_; }
-    /// Return profiler text.
-    Text* GetProfilerText() const { return profilerText_; }
-    /// Return currently shown elements.
-    unsigned GetMode() const { return mode_; }
-    /// Return maximum profiler block depth.
-    unsigned GetProfilerMaxDepth() const { return profilerMaxDepth_; }
-    /// Return profiler accumulation interval in seconds
-    float GetProfilerInterval() const;
-
-    /// Return whether showing 3D geometry primitive/batch count only.
-    bool GetUseRendererStats() const { return useRendererStats_; }
-    /// Set application-specific stats.
-    void SetAppStats(const String& label, const Variant& stats);
-    /// Set application-specific stats.
-    void SetAppStats(const String& label, const String& stats);
-    /// Reset application-specific stats. Return true if it was erased successfully.
-    bool ResetAppStats(const String& label);
-    /// Clear all application-specific stats.
-    void ClearAppStats();
-
-private:
-    /// Handle logic post-update event. The HUD texts are updated here.
-    void HandlePostUpdate(StringHash eventType, VariantMap& eventData);
-
-    /// Rendering stats text.
-    SharedPtr<Text> statsText_;
-    /// Rendering mode text.
-    SharedPtr<Text> modeText_;
-    /// Profiling information text.
-    SharedPtr<Text> profilerText_;
-    /// Hashmap containing application specific stats.
-    HashMap<String, String> appStats_;
-    /// Profiler timer.
-    Timer profilerTimer_;
-    /// Profiler max block depth.
-    unsigned profilerMaxDepth_;
-    /// Profiler accumulation interval.
-    unsigned profilerInterval_;
-    /// Show 3D geometry primitive/batch count flag.
-    bool useRendererStats_;
-    /// Current shown-element mode.
-    unsigned mode_;
-};
-
-}
-
 #endif
 #endif

+ 64 - 9
Source/Atomic/UI/UI.cpp

@@ -8,7 +8,10 @@
 #include <TurboBadger/tb_node_tree.h>
 #include <TurboBadger/tb_node_tree.h>
 #include <TurboBadger/tb_widgets_reader.h>
 #include <TurboBadger/tb_widgets_reader.h>
 #include <TurboBadger/tb_window.h>
 #include <TurboBadger/tb_window.h>
+#include <TurboBadger/tb_message_window.h>
 #include <TurboBadger/tb_editfield.h>
 #include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_select.h>
+#include <TurboBadger/tb_tab_container.h>
 #include <TurboBadger/image/tb_image_widget.h>
 #include <TurboBadger/image/tb_image_widget.h>
 
 
 void register_tbbf_font_renderer();
 void register_tbbf_font_renderer();
@@ -36,6 +39,11 @@ using namespace tb;
 #include "UIImageWidget.h"
 #include "UIImageWidget.h"
 #include "UIClickLabel.h"
 #include "UIClickLabel.h"
 #include "UICheckBox.h"
 #include "UICheckBox.h"
+#include "UISelectList.h"
+#include "UIMessageWindow.h"
+#include "UISkinImage.h"
+#include "UITabContainer.h"
+#include "UISceneView.h"
 
 
 namespace tb
 namespace tb
 {
 {
@@ -68,6 +76,9 @@ UI::~UI()
 {
 {
     if (initialized_)
     if (initialized_)
     {
     {
+        TBFile::SetReaderFunction(0);
+        TBID::tbidRegisterCallback = 0;
+
         tb::TBWidgetsAnimationManager::Shutdown();
         tb::TBWidgetsAnimationManager::Shutdown();
 
 
         widgetWrap_.Clear();
         widgetWrap_.Clear();
@@ -78,8 +89,6 @@ UI::~UI()
     }
     }
 
 
     uiContext_ = 0;
     uiContext_ = 0;
-    TBFile::SetReaderFunction(0);
-    TBID::tbidRegisterCallback = 0;
 }
 }
 
 
 void UI::Shutdown()
 void UI::Shutdown()
@@ -440,19 +449,15 @@ void UI::WrapWidget(UIWidget* widget, tb::TBWidget* tbwidget)
 
 
 UIWidget* UI::WrapWidget(tb::TBWidget* widget)
 UIWidget* UI::WrapWidget(tb::TBWidget* widget)
 {
 {
+    if (!widget)
+        return NULL;
+
     if (widgetWrap_.Contains(widget))
     if (widgetWrap_.Contains(widget))
         return widgetWrap_[widget];
         return widgetWrap_[widget];
 
 
     // switch this to use a factory?
     // switch this to use a factory?
 
 
     // this is order dependent as we're using IsOfType which also works if a base class
     // this is order dependent as we're using IsOfType which also works if a base class
-    if (widget->IsOfType<TBLayout>())
-    {
-        UILayout* layout = new UILayout(context_, false);
-        layout->SetWidget(widget);
-        widgetWrap_[widget] = layout;
-        return layout;
-    }
 
 
     if (widget->IsOfType<TBButton>())
     if (widget->IsOfType<TBButton>())
     {
     {
@@ -482,6 +487,14 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
         return editfield;
         return editfield;
     }
     }
 
 
+    if (widget->IsOfType<TBSkinImage>())
+    {
+        UISkinImage* skinimage = new UISkinImage(context_, "", false);
+        skinimage->SetWidget(widget);
+        widgetWrap_[widget] = skinimage;
+        return skinimage;
+    }
+
     if (widget->IsOfType<TBImageWidget>())
     if (widget->IsOfType<TBImageWidget>())
     {
     {
         UIImageWidget* imagewidget = new UIImageWidget(context_, false);
         UIImageWidget* imagewidget = new UIImageWidget(context_, false);
@@ -505,6 +518,47 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
         return nwidget;
         return nwidget;
     }
     }
 
 
+    if (widget->IsOfType<TBSelectList>())
+    {
+        UISelectList* nwidget = new UISelectList(context_, false);
+        nwidget->SetWidget(widget);
+        widgetWrap_[widget] = nwidget;
+        return nwidget;
+    }
+
+    if (widget->IsOfType<TBMessageWindow>())
+    {
+        UIMessageWindow* nwidget = new UIMessageWindow(context_, NULL, "", false);
+        nwidget->SetWidget(widget);
+        widgetWrap_[widget] = nwidget;
+        return nwidget;
+    }
+
+    if (widget->IsOfType<TBTabContainer>())
+    {
+        UITabContainer* nwidget = new UITabContainer(context_, false);
+        nwidget->SetWidget(widget);
+        widgetWrap_[widget] = nwidget;
+        return nwidget;
+    }
+
+    if (widget->IsOfType<SceneViewWidget>())
+    {
+        UISceneView* nwidget = new UISceneView(context_, false);
+        nwidget->SetWidget(widget);
+        widgetWrap_[widget] = nwidget;
+        return nwidget;
+    }
+
+
+    if (widget->IsOfType<TBLayout>())
+    {
+        UILayout* layout = new UILayout(context_, false);
+        layout->SetWidget(widget);
+        widgetWrap_[widget] = layout;
+        return layout;
+    }
+
     if (widget->IsOfType<TBWidget>())
     if (widget->IsOfType<TBWidget>())
     {
     {
         UIWidget* nwidget = new UIWidget(context_, false);
         UIWidget* nwidget = new UIWidget(context_, false);
@@ -513,6 +567,7 @@ UIWidget* UI::WrapWidget(tb::TBWidget* widget)
         return nwidget;
         return nwidget;
     }
     }
 
 
+
     return 0;
     return 0;
 }
 }
 
 

+ 9 - 0
Source/Atomic/UI/UIButton.cpp

@@ -25,6 +25,15 @@ UIButton::UIButton(Context* context, bool createWidget) : UIWidget(context, fals
 
 
 UIButton::~UIButton()
 UIButton::~UIButton()
 {
 {
+
+}
+
+void UIButton::SetSqueezable(bool value)
+{
+    if (!widget_)
+        return;
+
+    ((TBButton*)widget_)->SetSqueezable(value);
 }
 }
 
 
 bool UIButton::OnEvent(const tb::TBWidgetEvent &ev)
 bool UIButton::OnEvent(const tb::TBWidgetEvent &ev)

+ 2 - 0
Source/Atomic/UI/UIButton.h

@@ -16,6 +16,8 @@ public:
     UIButton(Context* context, bool createWidget = true);
     UIButton(Context* context, bool createWidget = true);
     virtual ~UIButton();
     virtual ~UIButton();
 
 
+    void SetSqueezable(bool value);
+
 protected:
 protected:
 
 
     virtual bool OnEvent(const tb::TBWidgetEvent &ev);
     virtual bool OnEvent(const tb::TBWidgetEvent &ev);

+ 2 - 2
Source/Atomic/UI/UIEvents.h

@@ -40,7 +40,7 @@ EVENT(E_WIDGETEVENT, WidgetEvent)
     PARAM(P_KEY, Key);                   // int
     PARAM(P_KEY, Key);                   // int
     PARAM(P_SPECIALKEY, SpecialKey);     // enum SPECIAL_KEY
     PARAM(P_SPECIALKEY, SpecialKey);     // enum SPECIAL_KEY
     PARAM(P_MODIFIERKEYS, ModifierKeys); // enum MODIFIER_KEYS
     PARAM(P_MODIFIERKEYS, ModifierKeys); // enum MODIFIER_KEYS
-    PARAM(P_REFID, RefID);                     // unsigned (TBID)
+    PARAM(P_REFID, RefID);               // string (TBID)
     PARAM(P_TOUCH, Touch);               // bool
     PARAM(P_TOUCH, Touch);               // bool
 
 
     // EventHandled can be set by event receivers to stop event bubble
     // EventHandled can be set by event receivers to stop event bubble
@@ -61,7 +61,7 @@ EVENT(E_WIDGETDELETED, WidgetDeleted)
 EVENT(E_POPUPMENUSELECT, PopupMenuSelect)
 EVENT(E_POPUPMENUSELECT, PopupMenuSelect)
 {
 {
     PARAM(P_BUTTON, Button);             // UIButton that created popup
     PARAM(P_BUTTON, Button);             // UIButton that created popup
-    PARAM(P_REFID, RefID);             // unsigned tbid
+    PARAM(P_REFID, RefID);             // string tbid
 }
 }
 
 
 
 

+ 34 - 0
Source/Atomic/UI/UIFontDescription.cpp

@@ -0,0 +1,34 @@
+#include <TurboBadger/tb_widgets.h>
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_layout.h>
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UIWidget.h"
+#include "UIFontDescription.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UIFontDescription::UIFontDescription(Context* context) : Object(context)
+{
+}
+
+UIFontDescription::~UIFontDescription()
+{
+
+}
+
+void UIFontDescription::SetId(const String& id)
+{
+    desc_.SetID(TBIDC(id.CString()));
+}
+
+void UIFontDescription::SetSize(int size)
+{
+    desc_.SetSize(size);
+}
+
+}

+ 31 - 0
Source/Atomic/UI/UIFontDescription.h

@@ -0,0 +1,31 @@
+
+
+#pragma once
+
+#include "UIWidget.h"
+
+namespace Atomic
+{
+
+class UIFontDescription : public Object
+{
+    OBJECT(UIFontDescription)
+
+public:
+
+    UIFontDescription(Context* context);
+    virtual ~UIFontDescription();
+
+    void SetId(const String& id);
+    void SetSize(int size);
+
+    tb::TBFontDescription* GetTBFontDescription() { return &desc_; }
+
+private:
+
+    tb::TBFontDescription desc_;
+
+};
+
+
+}

+ 9 - 0
Source/Atomic/UI/UILayout.cpp

@@ -13,6 +13,15 @@ using namespace tb;
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+UILayoutParams::UILayoutParams(Context* context) : Object(context)
+{
+}
+
+UILayoutParams::~UILayoutParams()
+{
+}
+
+
 UILayout::UILayout(Context* context, bool createWidget) : UIWidget(context, false)
 UILayout::UILayout(Context* context, bool createWidget) : UIWidget(context, false)
 {
 {
     if (createWidget)
     if (createWidget)

+ 19 - 0
Source/Atomic/UI/UILayout.h

@@ -6,6 +6,25 @@
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+class UILayoutParams : public Object
+{
+    OBJECT(UILayoutParams)
+
+public:
+
+    UILayoutParams(Context* context);
+    virtual ~UILayoutParams();
+
+    void SetHeight(int height) { params_.SetHeight(height); }
+
+    tb::LayoutParams* GetTBLayoutParams() { return &params_; }
+
+private:
+
+    tb::LayoutParams params_;
+
+};
+
 
 
 class UILayout : public UIWidget
 class UILayout : public UIWidget
 {
 {

+ 267 - 0
Source/Atomic/UI/UIListView.cpp

@@ -0,0 +1,267 @@
+
+#include <TurboBadger/tb_menu_window.h>
+#include <TurboBadger/tb_select.h>
+
+#include "UIListView.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+class ListViewItemSource;
+class ListViewItemWidget;
+
+class ListViewItem : public TBGenericStringItem
+{
+    bool expanded_;
+
+public:
+    ListViewItem(const char *str, const TBID &id, const char* icon,  ListViewItemSource* source)
+        : TBGenericStringItem(str, id), source_(source), parent_(0),
+          depth_(0), widget_(0), expanded_(false), icon_(icon)
+    {
+
+    }
+
+    ListViewItem* AddChild(const char* text, const char* icon, const TBID &id);
+
+    bool GetExpanded() { return expanded_; }
+
+    void SetExpanded(bool expanded)
+    {
+        expanded_ = expanded;
+        if (!expanded_)
+        {
+            for (unsigned i = 0; i < children_.Size(); i ++)
+                children_[i]->SetExpanded(expanded);
+        }
+        else
+        {
+            ListViewItem* p = parent_;
+            while (p)
+            {
+                p->expanded_ = true;
+                p = p->parent_;
+            }
+        }
+    }
+
+    ListViewItemSource* source_;
+    ListViewItem* parent_;
+    int depth_;
+    PODVector<ListViewItem*> children_;
+    ListViewItemWidget* widget_;
+    String icon_;
+};
+
+class ListViewItemWidget : public TBLayout
+{
+public:
+    ListViewItemWidget(ListViewItem *item, ListViewItemSource *source, TBSelectItemViewer *sourceviewer, int index);
+    virtual bool OnEvent(const TBWidgetEvent &ev);
+private:
+    ListViewItemSource *source_;
+    TBSelectItemViewer *sourceviewer_;
+    int index_;
+    ListViewItem* item_;
+};
+
+class ListViewItemSource : public TBSelectItemSourceList<ListViewItem>
+{
+public:
+    TBSelectList* list_;
+    ListViewItemSource(TBSelectList* list) : list_(list) {}
+    virtual ~ListViewItemSource() {}
+    virtual bool Filter(int index, const char *filter);
+    virtual TBWidget *CreateItemWidget(int index, TBSelectItemViewer *viewer);
+};
+
+// implementation
+ListViewItem* ListViewItem::AddChild(const char *text, const char* icon, const TBID &id)
+{
+    ListViewItem* child = new ListViewItem(text, id, icon, source_);
+    child->parent_ = this;
+    child->depth_ = depth_ + 1;
+    source_->AddItem(child);
+    children_.Push(child);
+    return child;
+}
+
+
+ListViewItemWidget::ListViewItemWidget(ListViewItem *item, ListViewItemSource *source,
+                                       TBSelectItemViewer *sourceviewer, int index)
+    : source_(source)
+    , sourceviewer_(sourceviewer)
+    , index_(index)
+    , item_(item)
+{
+    SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+    SetLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP);
+    SetPaintOverflowFadeout(false);
+
+    item_->widget_ = this;
+
+    for (int i = 0; i < item->depth_; i++)
+    {
+        LayoutParams lp;
+        lp.SetWidth(6);
+        lp.SetHeight(4);
+        TBWidget* spacer = new TBWidget();
+        spacer->SetLayoutParams(lp);
+        GetContentRoot()->AddChild(spacer);
+    }
+
+    if (item->icon_.Length())
+    {
+        TBSkinImage* skinImage = new TBSkinImage(TBIDC(item->icon_.CString()));
+        skinImage->SetIgnoreInput(true);
+        GetContentRoot()->AddChild(skinImage);
+    }
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Vera"));
+    fd.SetSize(11);
+
+    TBTextField* tfield = new TBTextField();
+    tfield->SetIgnoreInput(true);
+    tfield->SetSkinBg(TBIDC("Folder"));
+    tfield->SetText(item->str);
+    tfield->SetFontDescription(fd);
+
+    SetSkinBg(TBIDC("TBSelectItem"));
+    GetContentRoot()->AddChild(tfield);
+
+    SetID(item->id);
+}
+
+bool ListViewItemWidget::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_WHEEL)
+    {
+        return false;
+    }
+
+    // get clicks this way, not sure we want to
+    if (ev.type == EVENT_TYPE_CLICK && ev.target->GetID() == item_->id)
+    {
+        item_->SetExpanded(!item_->GetExpanded());
+        source_->list_->InvalidateList();
+
+        // want to bubble
+        return false;
+    }
+
+    return TBLayout::OnEvent(ev);
+}
+
+
+bool ListViewItemSource::Filter(int index, const char *filter)
+{
+    ListViewItem* item = GetItem(index);
+
+    if (!item->parent_)
+        return true;
+
+    if (item->parent_->GetExpanded())
+        return true;
+
+    return false;
+
+}
+
+TBWidget *ListViewItemSource::CreateItemWidget(int index, TBSelectItemViewer *viewer)
+{
+    ListViewItem* item = GetItem(index);
+
+    if (TBLayout *layout = new ListViewItemWidget(item, this, viewer, index))
+        return layout;
+
+    return nullptr;
+}
+
+UIListView::UIListView(Context* context, bool createWidget) :
+    UIWidget(context, createWidget),
+    source_(0), itemLookupId_(0)
+{
+    rootList_ = new UISelectList(context);
+
+    // dummy filter so filter is called
+    rootList_->SetFilter(" ");
+
+    widget_->SetGravity(WIDGET_GRAVITY_ALL);
+    rootList_->SetGravity(WIDGET_GRAVITY_ALL);
+
+    source_ = new ListViewItemSource(rootList_->GetTBSelectList());
+
+    rootList_->GetTBSelectList()->SetSource(source_);
+
+    AddChild(rootList_);
+}
+
+UIListView::~UIListView()
+{
+
+}
+
+unsigned UIListView::AddRootItem(const String& text, const String& icon, const String& id)
+{
+    ListViewItem* item = new ListViewItem(text.CString(), TBID(id.CString()), icon.CString(), source_);
+    source_->AddItem(item);
+
+    itemLookup_[itemLookupId_++] = item;
+    return itemLookupId_ - 1;
+}
+
+unsigned UIListView::AddChildItem(unsigned parentItemID, const String& text, const String& icon, const String& id)
+{
+    if (!itemLookup_.Contains(parentItemID))
+        return -1;
+
+    ListViewItem* item = itemLookup_[parentItemID];
+
+    ListViewItem* child = item->AddChild(text.CString(), icon.CString(), TBID(id.CString()));
+
+    itemLookup_[itemLookupId_++] = child;
+    return itemLookupId_ - 1;
+
+}
+
+void UIListView::SetExpanded(unsigned itemID, bool value)
+{
+    if (!itemLookup_.Contains(itemID))
+        return;
+
+    itemLookup_[itemID]->SetExpanded(value);
+
+}
+
+
+void UIListView::DeleteAllItems()
+{
+    itemLookup_.Clear();
+    source_->DeleteAllItems();
+}
+
+
+void UIListView::SelectItemByID(const String& id)
+{
+    TBID tid = TBIDC(id.CString());
+
+    for (int i = 0; i < source_->GetNumItems(); i++)
+    {
+        ListViewItem* item = source_->GetItem(i);
+
+        if (tid == item->id)
+        {
+            item->SetExpanded(true);
+            rootList_->SetValue(i);
+            rootList_->InvalidateList();
+            return;
+        }
+
+    }
+}
+
+
+}

+ 45 - 0
Source/Atomic/UI/UIListView.h

@@ -0,0 +1,45 @@
+
+#pragma once
+
+#include "UIWidget.h"
+#include "UISelectList.h"
+
+namespace Atomic
+{
+
+class ListViewItemSource;
+class ListViewItem;
+
+class UIListView : public UIWidget
+{
+    OBJECT(UIListView);
+
+public:
+    /// Construct.
+    UIListView(Context* context, bool createWidget = true);
+
+    /// Destruct.
+    virtual ~UIListView();
+
+    unsigned AddRootItem(const String& text, const String& icon, const String& id);
+    unsigned AddChildItem(unsigned parentItemID, const String& text, const String& icon, const String& id);
+
+    void SetExpanded(unsigned itemID, bool value);
+
+    void DeleteAllItems();
+    void SelectItemByID(const String& id);
+
+    UISelectList* GetRootList() { return rootList_; }
+
+private:
+
+    SharedPtr<UISelectList> rootList_;
+    ListViewItemSource* source_;
+
+    HashMap<unsigned,ListViewItem*> itemLookup_;
+
+    unsigned itemLookupId_;
+
+};
+
+}

+ 1 - 1
Source/Atomic/UI/UIMenuWindow.h

@@ -26,7 +26,7 @@ protected:
 
 
 private:
 private:
 
 
-    tb::TBGenericStringItemSource* source_;
+    tb::TBSelectItemSource* source_;
 
 
 };
 };
 
 

+ 214 - 0
Source/Atomic/UI/UIMenubar.cpp

@@ -0,0 +1,214 @@
+
+#include <TurboBadger/tb_menu_window.h>
+#include <TurboBadger/tb_select.h>
+
+#include "UIMenubar.h"
+
+using namespace tb;
+
+namespace tb {
+
+
+// THIS MUST MATCH TBSimpleLayoutItemWidget in tb_select_item.cpp
+class TBSimpleLayoutItemWidget : public TBLayout, private TBWidgetListener
+{
+public:
+    TBSimpleLayoutItemWidget(TBID image, TBSelectItemSource *source, const char *str);
+    ~TBSimpleLayoutItemWidget();
+    virtual bool OnEvent(const TBWidgetEvent &ev);
+private:
+    TBSelectItemSource *m_source;
+    TBTextField m_textfield;
+    TBSkinImage m_image;
+    TBSkinImage m_image_arrow;
+    TBMenuWindow *m_menu; ///< Points to the submenu window if opened
+    virtual void OnWidgetDelete(TBWidget *widget);
+    void OpenSubMenu();
+    void CloseSubMenu();
+};
+
+}
+
+namespace Atomic
+{
+
+
+class MenubarItem : public TBGenericStringItem
+{
+public:
+    MenubarItem(const char *str, const TBID &id = TBID((uint32)0), const String& shortcut = String::EMPTY)
+        : TBGenericStringItem(str, id), shortcut_(shortcut)
+    {
+
+    }
+
+    MenubarItem(const char *str, TBSelectItemSource *sub_source) : TBGenericStringItem(str, sub_source) {}
+
+    String shortcut_;
+
+};
+
+class MenubarItemSource : public TBSelectItemSourceList<MenubarItem>
+{
+public:
+    virtual bool Filter(int index, const char *filter);
+    virtual TBWidget *CreateItemWidget(int index, TBSelectItemViewer *viewer);
+};
+
+class MenubarItemWidget : public TBLayout
+{
+public:
+    MenubarItemWidget(MenubarItem *item, MenubarItemSource *source, TBSelectItemViewer *source_viewer, int index);
+    virtual bool OnEvent(const TBWidgetEvent &ev);
+private:
+    MenubarItemSource *m_source;
+    TBSelectItemViewer *m_source_viewer;
+    int m_index;
+};
+
+// UI IMPLEMENTATION
+
+UIMenuItem::UIMenuItem(Context* context, const String& str, const String& id, const String& shortcut):
+    UISelectItem(context, str, id),
+    shortcut_(shortcut)
+{
+
+}
+
+UIMenuItem::~UIMenuItem()
+{
+
+}
+
+tb::TBGenericStringItem* UIMenuItem::GetTBItem()
+{
+    MenubarItem* item;
+
+    if (!subSource_)
+    {
+        item = new MenubarItem(str_.CString(), id_, shortcut_);
+    }
+    else
+    {
+        item = new MenubarItem(str_.CString(), subSource_->GetTBItemSource());
+    }
+
+    return item;
+
+}
+
+tb::TBSelectItemSource* UIMenuItemSource::GetTBItemSource()
+{
+    // caller's responsibility to clean up
+    MenubarItemSource* src = new MenubarItemSource();
+
+    for (List<SharedPtr<UISelectItem> >::Iterator itr = items_.Begin(); itr != items_.End(); itr++)
+    {
+        MenubarItem* tbitem = (MenubarItem*) (*itr)->GetTBItem();
+        src->AddItem(tbitem);
+    }
+
+    return src;
+}
+
+UIMenuItemSource::UIMenuItemSource(Context* context) : UISelectItemSource(context)
+{
+
+}
+
+UIMenuItemSource::~UIMenuItemSource()
+{
+
+}
+
+// WIDGET IMPLEMENTATION
+
+MenubarItemWidget::MenubarItemWidget(MenubarItem *item, MenubarItemSource *source,
+                                     TBSelectItemViewer *source_viewer, int index)
+    : m_source(source)
+    , m_source_viewer(source_viewer)
+    , m_index(index)
+{
+    SetSkinBg(TBIDC("TBSelectItem"));
+    SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+    SetLayoutDistributionPosition(LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP);
+    SetPaintOverflowFadeout(false);
+
+    TBWidget* root = GetContentRoot();
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Vera"));
+    fd.SetSize(12);
+
+    TBTextField* text = new TBTextField();
+    text->SetIgnoreInput(true);
+    text->SetText(item->str);
+    text->SetFontDescription(fd);
+    root->AddChild(text);
+
+    if (item->shortcut_.Length())
+    {
+        TBWidget* spacer = new TBWidget();
+        spacer->SetIgnoreInput(true);
+        spacer->SetGravity(WIDGET_GRAVITY_LEFT_RIGHT);
+        root->AddChild(spacer);
+
+        TBTextField* shortcut = new TBTextField();
+        shortcut->SetIgnoreInput(true);
+        shortcut->SetText(item->shortcut_.CString());
+        shortcut->SetFontDescription(fd);
+        shortcut->SetGravity(WIDGET_GRAVITY_RIGHT);
+        root->AddChild(shortcut);
+    }
+
+}
+
+bool MenubarItemWidget::OnEvent(const TBWidgetEvent &ev)
+{
+    if (m_source && ev.type == EVENT_TYPE_CLICK && ev.target == this)
+    {
+        return false;
+    }
+
+    return false;
+}
+
+bool MenubarItemSource::Filter(int index, const char *filter)
+{
+    return true;
+}
+
+TBWidget *MenubarItemSource::CreateItemWidget(int index, TBSelectItemViewer *viewer)
+{
+    const char *string = GetItemString(index);
+
+    TBSelectItemSource *sub_source = GetItemSubSource(index);
+    TBID image = GetItemImage(index);
+
+    if (sub_source || image)
+    {
+        if (TBSimpleLayoutItemWidget *itemwidget = new TBSimpleLayoutItemWidget(image, sub_source, string))
+        {
+            itemwidget->SetID(GetItem(index)->id);
+            return itemwidget;
+        }
+    }
+    else if (string && *string == '-')
+    {
+        if (TBSeparator *separator = new TBSeparator)
+        {
+            separator->SetGravity(WIDGET_GRAVITY_ALL);
+            separator->SetSkinBg(TBIDC("AESeparator"));
+            return separator;
+        }
+    }
+    else if (TBLayout *layout = new MenubarItemWidget(GetItem(index), this, viewer, index))
+    {
+        layout->SetID(GetItem(index)->id);
+        return layout;
+    }
+
+    return NULL;
+}
+
+}

+ 44 - 0
Source/Atomic/UI/UIMenubar.h

@@ -0,0 +1,44 @@
+
+#pragma once
+
+#include "UISelectItem.h"
+
+namespace Atomic
+{
+
+class UIMenuItemSource;
+
+class UIMenuItem : public UISelectItem
+{
+    OBJECT(UIMenuItem)
+
+public:
+
+    UIMenuItem(Context* context, const String& str = String::EMPTY, const String& id = String::EMPTY, const String& shortcut = String::EMPTY);
+    virtual ~UIMenuItem();
+
+    tb::TBGenericStringItem* GetTBItem();
+
+private:
+
+    String shortcut_;
+
+};
+
+class UIMenuItemSource : public UISelectItemSource
+{
+    OBJECT(UIMenuItemSource)
+
+public:
+
+    UIMenuItemSource(Context* context);
+    virtual ~UIMenuItemSource();
+
+    // caller's responsibility to clean up
+    tb::TBSelectItemSource *GetTBItemSource();
+
+private:
+
+};
+
+}

+ 50 - 0
Source/Atomic/UI/UIMessageWindow.cpp

@@ -0,0 +1,50 @@
+
+#include <TurboBadger/tb_widgets.h>
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_message_window.h>
+
+#include "../IO/Log.h"
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UIMessageWindow.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UIMessageWindow::UIMessageWindow(Context* context, UIWidget* target, const String& id, bool createWidget) : UIWindow(context, false)
+{
+    if (createWidget)
+    {
+        widget_ = new TBMessageWindow(target ? target->GetInternalWidget() : 0, TBIDC(id.CString()));
+        widget_->SetDelegate(this);
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+    }
+}
+
+UIMessageWindow::~UIMessageWindow()
+{
+
+}
+
+void UIMessageWindow::Show(const String &title, const String &message, int width, int height)
+{
+    if (!widget_)
+        return;
+
+    TBMessageWindowSettings settings;
+    settings.styling = true;
+    settings.dimmer = true;
+
+    ((TBMessageWindow*)widget_)->Show(title.CString(), message.CString(), &settings, width, height);
+
+}
+
+bool UIMessageWindow::OnEvent(const tb::TBWidgetEvent &ev)
+{
+    return UIWindow::OnEvent(ev);
+}
+
+}

+ 28 - 0
Source/Atomic/UI/UIMessageWindow.h

@@ -0,0 +1,28 @@
+
+#pragma once
+
+#include "UIWindow.h"
+
+namespace Atomic
+{
+
+class UIMessageWindow : public UIWindow
+{
+    OBJECT(UIMessageWindow)
+
+public:
+
+    UIMessageWindow(Context* context, UIWidget* target, const String& id, bool createWidget = true);
+    virtual ~UIMessageWindow();
+
+    void Show(const String& title, const String& message, int width, int height);
+
+protected:
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+}

+ 230 - 0
Source/Atomic/UI/UISceneView.cpp

@@ -0,0 +1,230 @@
+
+// Portions Copyright (c) 2008-2015 the Urho3D project.
+
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+
+#include <Atomic/UI/UI.h>
+#include <Atomic/UI/UIBatch.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/Engine/Engine.h>
+#include <Atomic/Graphics/Graphics.h>
+#include <Atomic/Graphics/Camera.h>
+#include <Atomic/Graphics/RenderPath.h>
+#include <Atomic/Graphics/Renderer.h>
+#include <Atomic/Core/CoreEvents.h>
+
+#include "UISceneView.h"
+using namespace tb;
+
+namespace Atomic
+{
+
+UISceneView::UISceneView(Context* context, bool createWidget) : UIWidget(context, false),
+    rttFormat_(Graphics::GetRGBFormat()),
+    autoUpdate_(false),
+    size_(-1, -1),
+    resizeRequired_(false)
+{
+
+    if (createWidget)
+    {
+        renderTexture_ = new Texture2D(context_);
+        depthTexture_ = new Texture2D(context_);
+        viewport_ = new Viewport(context_);
+
+        widget_ = new SceneViewWidget();
+        widget_->SetDelegate(this);
+        widget_->SetGravity(WIDGET_GRAVITY_ALL);
+        ((SceneViewWidget*)widget_)->sceneView_ = this;
+
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+
+
+    }
+
+   SubscribeToEvent(E_ENDFRAME, HANDLER(UISceneView, HandleEndFrame));
+}
+
+UISceneView::~UISceneView()
+{
+    // FIXME: need to refactor Light2D viewport handling
+    if (viewport_.NotNull())
+    {
+        RenderPath* renderpath = viewport_->GetRenderPath();
+        if (renderpath)
+            renderpath->RemoveCommands("Light2D");
+    }
+
+}
+
+bool UISceneView::OnEvent(const TBWidgetEvent &ev)
+{
+    return false;
+}
+
+void UISceneView::HandleEndFrame(StringHash eventType, VariantMap& eventData)
+{
+    if (resizeRequired_)
+    {
+        TBRect rect = widget_->GetRect();
+        OnResize(IntVector2(rect.w, rect.h));
+        resizeRequired_ = false;
+    }
+
+}
+
+
+void UISceneView::OnResize(const IntVector2 &newSize)
+{
+    if (newSize.x_ == size_.x_ && newSize.y_ == size_.y_)
+        return;
+
+    int width = newSize.x_;
+    int height = newSize.y_;
+
+    if (width > 0 && height > 0)
+    {
+        viewport_->SetRect(IntRect(0, 0, width, height));
+        renderTexture_->SetSize(width, height, rttFormat_, TEXTURE_RENDERTARGET);
+        depthTexture_->SetSize(width, height, Graphics::GetDepthStencilFormat(), TEXTURE_DEPTHSTENCIL);
+
+        RenderSurface* surface = renderTexture_->GetRenderSurface();
+        surface->SetViewport(0, viewport_);
+        surface->SetUpdateMode(autoUpdate_ ? SURFACE_UPDATEALWAYS : SURFACE_MANUALUPDATE);
+        surface->SetLinkedDepthStencil(depthTexture_->GetRenderSurface());
+
+        size_ = newSize;
+
+    }
+}
+
+
+void UISceneView::SetView(Scene* scene, Camera* camera)
+{
+    scene_ = scene;
+    cameraNode_ = camera ? camera->GetNode() : 0;
+
+    viewport_->SetScene(scene_);
+    viewport_->SetCamera(camera);
+    QueueUpdate();
+}
+
+void UISceneView::SetFormat(unsigned format)
+{
+    if (format != rttFormat_)
+    {
+        rttFormat_ = format;
+    }
+}
+
+void UISceneView::SetAutoUpdate(bool enable)
+{
+    if (enable != autoUpdate_)
+    {
+        autoUpdate_ = enable;
+        RenderSurface* surface = renderTexture_->GetRenderSurface();
+        if (surface)
+            surface->SetUpdateMode(autoUpdate_ ? SURFACE_UPDATEALWAYS : SURFACE_MANUALUPDATE);
+    }
+}
+
+void UISceneView::QueueUpdate()
+{
+    if (!autoUpdate_)
+    {
+        RenderSurface* surface = renderTexture_->GetRenderSurface();
+        if (surface)
+            surface->QueueUpdate();
+    }
+}
+
+Scene* UISceneView::GetScene() const
+{
+    return scene_;
+}
+
+Node* UISceneView::GetCameraNode() const
+{
+    return cameraNode_;
+}
+
+Texture2D* UISceneView::GetRenderTexture() const
+{
+    return renderTexture_;
+}
+
+Texture2D* UISceneView::GetDepthTexture() const
+{
+    return depthTexture_;
+}
+
+Viewport* UISceneView::GetViewport() const
+{
+    return viewport_;
+}
+
+SceneViewWidget::SceneViewWidget()
+{
+    vertexData_.Resize(6 * UI_VERTEX_SIZE);
+    float color;
+    ((unsigned&)color) = 0xFFFFFFFF;
+
+    float* data = &vertexData_[0];
+
+    data[2] = 0; data[3] = color; data[4] = 0; data[5] = 0;
+    data[8] = 0; data[9] = color; data[10] = 1; data[11] = 0;
+    data[14] = 0; data[15] = color; data[16] = 1; data[17] = 1;
+    data[20] = 0; data[21] = color; data[22] = 0; data[23] = 0;
+    data[26] = 0; data[27] = color; data[28] = 1; data[29] = 1;
+    data[32] = 0; data[33] = color; data[34] = 0; data[35] = 1;
+}
+
+
+void SceneViewWidget::OnPaint(const PaintProps &paint_props)
+{
+    if (sceneView_.Null())
+        return;
+
+    TBRect rect = GetRect();
+    ConvertToRoot(rect.x, rect.y);
+
+    IntVector2 size = sceneView_->GetSize();
+
+    if (size.x_ != rect.w || size.y_ != rect.h)
+    {
+        size.x_ = rect.w;
+        size.y_ = rect.h;
+        sceneView_->SetResizeRequired();
+        return;
+    }
+
+    float* data = &vertexData_[0];
+
+    data[0] = rect.x;
+    data[1] = rect.y;
+
+    data[6] = rect.x + rect.w;
+    data[7] =  rect.y;
+
+    data[12] = rect.x + rect.w;
+    data[13] = rect.y + rect.h;
+
+    data[18] = rect.x;
+    data[19] = rect.y;
+
+    data[24] = rect.x + rect.w;
+    data[25] = rect.y + rect.h;
+
+    data[30] = rect.x;
+    data[31] = rect.y + rect.h;
+
+    sceneView_->GetSubsystem<UI>()->SubmitBatchVertexData(sceneView_->GetRenderTexture(), vertexData_);
+
+}
+
+}

+ 107 - 0
Source/Atomic/UI/UISceneView.h

@@ -0,0 +1,107 @@
+
+#pragma once
+
+#include "UIWidget.h"
+
+#include <Atomic/Graphics/Texture2D.h>
+#include <Atomic/Graphics/Viewport.h>
+#include <Atomic/Scene/Scene.h>
+
+#include <TurboBadger/tb_widgets.h>
+
+using namespace tb;
+
+namespace Atomic
+{
+
+class UISceneView;
+
+class SceneViewWidget : public tb::TBWidget
+{
+    friend class UISceneView;
+
+public:
+    // For safe typecasting
+    TBOBJECT_SUBCLASS(SceneViewWidget, tb::TBWidget);
+
+    SceneViewWidget();
+
+    virtual void OnPaint(const PaintProps &paint_props);
+
+private:
+
+    WeakPtr<UISceneView> sceneView_;
+    PODVector<float> vertexData_;
+
+};
+
+
+class UISceneView : public UIWidget
+{
+    OBJECT(UISceneView)
+
+public:
+
+    UISceneView(Context* context, bool createWidget = true);
+    virtual ~UISceneView();
+
+    /// React to resize.
+    void OnResize(const IntVector2& newSize);
+
+    /// Define the scene and camera to use in rendering. When ownScene is true the View3D will take ownership of them with shared pointers.
+    void SetView(Scene* scene, Camera* camera);
+    /// Set render texture pixel format. Default is RGB.
+    void SetFormat(unsigned format);
+    /// Set render target auto update mode. Default is true.
+    void SetAutoUpdate(bool enable);
+    /// Queue manual update on the render texture.
+    void QueueUpdate();
+
+    /// Return render texture pixel format.
+    unsigned GetFormat() const { return rttFormat_; }
+    /// Return whether render target updates automatically.
+    bool GetAutoUpdate() const { return autoUpdate_; }
+    /// Return scene.
+    Scene* GetScene() const;
+    /// Return camera scene node.
+    Node* GetCameraNode() const;
+    /// Return render texture.
+    Texture2D* GetRenderTexture() const;
+    /// Return depth stencil texture.
+    Texture2D* GetDepthTexture() const;
+    /// Return viewport.
+    Viewport* GetViewport() const;
+
+    void SetResizeRequired() {resizeRequired_ = true;}
+    const IntVector2& GetSize() const { return size_; }
+
+protected:
+
+    void HandleEndFrame(StringHash eventType, VariantMap& eventData);
+
+    /// Renderable texture.
+    SharedPtr<Texture2D> renderTexture_;
+    /// Depth stencil texture.
+    SharedPtr<Texture2D> depthTexture_;
+    /// Viewport.
+    SharedPtr<Viewport> viewport_;
+    /// Scene.
+    SharedPtr<Scene> scene_;
+    /// Camera scene node.
+    SharedPtr<Node> cameraNode_;
+    /// Render texture format.
+    unsigned rttFormat_;
+    /// Render texture auto update mode.
+    bool autoUpdate_;
+
+    bool resizeRequired_;
+
+    IntVector2 size_;
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+}

+ 1 - 1
Source/Atomic/UI/UISelectItem.cpp

@@ -63,7 +63,7 @@ UISelectItemSource::~UISelectItemSource()
 
 
 }
 }
 
 
-tb::TBGenericStringItemSource* UISelectItemSource::GetTBItemSource()
+TBSelectItemSource *UISelectItemSource::GetTBItemSource()
 {
 {
     // caller's responsibility to clean up
     // caller's responsibility to clean up
     TBGenericStringItemSource* src = new TBGenericStringItemSource();
     TBGenericStringItemSource* src = new TBGenericStringItemSource();

+ 4 - 4
Source/Atomic/UI/UISelectItem.h

@@ -26,9 +26,9 @@ public:
     void SetSkinImage(const String& skinImage);
     void SetSkinImage(const String& skinImage);
     void SetSubSource(UISelectItemSource *subSource);
     void SetSubSource(UISelectItemSource *subSource);
 
 
-    tb::TBGenericStringItem* GetTBItem();
+    virtual tb::TBGenericStringItem* GetTBItem();
 
 
-private:
+protected:
 
 
     String str_;
     String str_;
 
 
@@ -53,9 +53,9 @@ public:
     void AddItem(UISelectItem* item) { items_.Push(SharedPtr<UISelectItem>(item)); }
     void AddItem(UISelectItem* item) { items_.Push(SharedPtr<UISelectItem>(item)); }
 
 
     // caller's responsibility to clean up
     // caller's responsibility to clean up
-    tb::TBGenericStringItemSource* GetTBItemSource();
+    virtual tb::TBSelectItemSource* GetTBItemSource();
 
 
-private:
+protected:
 
 
     List<SharedPtr<UISelectItem>> items_;
     List<SharedPtr<UISelectItem>> items_;
 
 

+ 95 - 0
Source/Atomic/UI/UISelectList.cpp

@@ -0,0 +1,95 @@
+
+#include <TurboBadger/tb_select.h>
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UISelectList.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UISelectList::UISelectList(Context* context, bool createWidget) : UIWidget(context, false)
+{
+    if (createWidget)
+    {
+        widget_ = new TBSelectList();
+        widget_->SetDelegate(this);
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+    }
+}
+
+UISelectList::~UISelectList()
+{
+
+}
+
+tb::TBSelectList* UISelectList::GetTBSelectList()
+{
+    return (TBSelectList*) widget_;
+}
+
+void UISelectList::SetFilter(const String& filter)
+{
+    if (!widget_)
+        return;
+
+    ((TBSelectList*)widget_)->SetFilter(filter.CString());
+}
+
+void UISelectList::SetValue(int value)
+{
+    if (!widget_)
+        return;
+
+    ((TBSelectList*)widget_)->SetValue(value);
+
+}
+
+int UISelectList::GetValue()
+{
+    if (!widget_)
+        return 0;
+
+    return ((TBSelectList*)widget_)->GetValue();
+
+}
+
+void UISelectList::InvalidateList()
+{
+    if (!widget_)
+        return;
+
+    return ((TBSelectList*)widget_)->InvalidateList();
+}
+
+String UISelectList::GetSelectedItemID()
+{
+    if (!widget_)
+        return "";
+
+    String id_;
+
+    TBID id = ((TBSelectList*)widget_)->GetSelectedItemID();
+
+    GetSubsystem<UI>()->GetTBIDString(id, id_);
+
+    return id_;
+
+}
+
+void UISelectList::SetSource(UISelectItemSource* source)
+{
+    if (!widget_)
+        return;
+
+    ((TBSelectList*)widget_)->SetSource(source ? source->GetTBItemSource() : NULL);
+}
+
+bool UISelectList::OnEvent(const tb::TBWidgetEvent &ev)
+{
+    return false;
+}
+
+}

+ 45 - 0
Source/Atomic/UI/UISelectList.h

@@ -0,0 +1,45 @@
+
+#pragma once
+
+#include "UIWidget.h"
+#include "UISelectItem.h"
+
+namespace tb
+{
+    class TBSelectList;
+}
+
+namespace Atomic
+{
+
+class UISelectList : public UIWidget
+{
+    OBJECT(UISelectList)
+
+public:
+
+    UISelectList(Context* context, bool createWidget = true);
+    virtual ~UISelectList();
+
+    void SetFilter(const String& filter);
+
+    void SetSource(UISelectItemSource* source);
+    void InvalidateList();
+
+    void SetValue(int value);
+    int GetValue();
+
+    String GetSelectedItemID();
+
+    tb::TBSelectList* GetTBSelectList();
+
+protected:
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+
+}

+ 39 - 0
Source/Atomic/UI/UISkinImage.cpp

@@ -0,0 +1,39 @@
+
+#include <TurboBadger/tb_widgets.h>
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_editfield.h>
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UISkinImage.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UISkinImage::UISkinImage(Context* context, const String& bitmapID, bool createWidget) : UIWidget(context, false)
+{
+    if (createWidget)
+    {
+        if (bitmapID.Length())
+            widget_ = new TBSkinImage(TBIDC(bitmapID.CString()));
+        else
+            widget_ = new TBSkinImage();
+
+        widget_->SetDelegate(this);
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+    }
+}
+
+UISkinImage::~UISkinImage()
+{
+
+}
+
+bool UISkinImage::OnEvent(const tb::TBWidgetEvent &ev)
+{
+    return false;
+}
+
+}

+ 26 - 0
Source/Atomic/UI/UISkinImage.h

@@ -0,0 +1,26 @@
+
+#pragma once
+
+#include "UIWidget.h"
+
+namespace Atomic
+{
+
+class UISkinImage : public UIWidget
+{
+    OBJECT(UISkinImage)
+
+public:
+
+    UISkinImage(Context* context, const String& bitmapID, bool createWidget = true);
+    virtual ~UISkinImage();
+
+protected:
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+}

+ 65 - 0
Source/Atomic/UI/UITabContainer.cpp

@@ -0,0 +1,65 @@
+
+#include <TurboBadger/tb_widgets.h>
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_tab_container.h>
+
+
+#include "UI.h"
+#include "UIEvents.h"
+#include "UITabContainer.h"
+
+using namespace tb;
+
+namespace Atomic
+{
+
+UITabContainer::UITabContainer(Context* context, bool createWidget) : UIWidget(context, false)
+{
+    if (createWidget)
+    {
+        widget_ = new TBTabContainer();
+        widget_->SetDelegate(this);
+        GetSubsystem<UI>()->WrapWidget(this, widget_);
+    }
+}
+
+UITabContainer::~UITabContainer()
+{
+
+}
+
+UIWidget* UITabContainer::GetCurrentPageWidget()
+{
+    if (!widget_)
+        return 0;
+
+    TBWidget* w = ((TBTabContainer*)widget_)->GetCurrentPageWidget();
+
+    return GetSubsystem<UI>()->WrapWidget(w);
+}
+
+int UITabContainer::GetNumPages()
+{
+    if (!widget_)
+        return 0;
+
+    return ((TBTabContainer*)widget_)->GetNumPages();
+
+}
+
+
+void UITabContainer::SetCurrentPage(int page)
+{
+    if (!widget_)
+        return;
+
+    ((TBTabContainer*)widget_)->SetCurrentPage(page);
+
+}
+
+bool UITabContainer::OnEvent(const tb::TBWidgetEvent &ev)
+{
+    return false;
+}
+
+}

+ 31 - 0
Source/Atomic/UI/UITabContainer.h

@@ -0,0 +1,31 @@
+
+#pragma once
+
+#include "UIWidget.h"
+
+namespace Atomic
+{
+
+class UITabContainer : public UIWidget
+{
+    OBJECT(UITabContainer)
+
+public:
+
+    UITabContainer(Context* context, bool createWidget = true);
+    virtual ~UITabContainer();
+
+    int GetNumPages();
+    void SetCurrentPage(int page);
+
+    UIWidget* GetCurrentPageWidget();
+
+protected:
+
+    virtual bool OnEvent(const tb::TBWidgetEvent &ev);
+
+private:
+
+};
+
+}

+ 122 - 6
Source/Atomic/UI/UIWidget.cpp

@@ -6,6 +6,8 @@
 #include "UIEvents.h"
 #include "UIEvents.h"
 #include "UI.h"
 #include "UI.h"
 #include "UIWidget.h"
 #include "UIWidget.h"
+#include "UILayout.h"
+#include "UIFontDescription.h"
 
 
 using namespace tb;
 using namespace tb;
 
 
@@ -24,8 +26,6 @@ UIWidget::UIWidget(Context* context, bool createWidget) : Object(context),
         GetSubsystem<UI>()->WrapWidget(this, widget_);
         GetSubsystem<UI>()->WrapWidget(this, widget_);
     }
     }
 
 
-    UI* ui = GetSubsystem<UI>();
-
 }
 }
 
 
 UIWidget::~UIWidget()
 UIWidget::~UIWidget()
@@ -33,12 +33,21 @@ UIWidget::~UIWidget()
 
 
 }
 }
 
 
+void UIWidget::SetIsFocusable(bool value)
+{
+    if (!widget_)
+        return;
+
+    widget_->SetIsFocusable(value);
+
+}
+
 bool UIWidget::Load(const String& filename)
 bool UIWidget::Load(const String& filename)
 {
 {
     UI* ui = GetSubsystem<UI>();
     UI* ui = GetSubsystem<UI>();
 
 
     if  (!ui->LoadResourceFile(widget_ , filename))
     if  (!ui->LoadResourceFile(widget_ , filename))
-        return false;    
+        return false;
 
 
     VariantMap eventData;
     VariantMap eventData;
     eventData[WidgetLoaded::P_WIDGET] = this;
     eventData[WidgetLoaded::P_WIDGET] = this;
@@ -68,6 +77,11 @@ void UIWidget::SetWidget(tb::TBWidget* widget)
 
 
 void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWidgetEvent &ev, VariantMap& data)
 void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWidgetEvent &ev, VariantMap& data)
 {
 {
+    UI* ui = GetSubsystem<UI>();
+    String id;
+
+    ui->GetTBIDString(ev.ref_id, id);
+
     using namespace WidgetEvent;
     using namespace WidgetEvent;
     data[P_HANDLER] = handler;
     data[P_HANDLER] = handler;
     data[P_TARGET] = target;
     data[P_TARGET] = target;
@@ -80,7 +94,7 @@ void UIWidget::ConvertEvent(UIWidget *handler, UIWidget* target, const tb::TBWid
     data[P_KEY] = ev.key;
     data[P_KEY] = ev.key;
     data[P_SPECIALKEY] = (unsigned) ev.special_key;
     data[P_SPECIALKEY] = (unsigned) ev.special_key;
     data[P_MODIFIERKEYS] = (unsigned) ev.modifierkeys;
     data[P_MODIFIERKEYS] = (unsigned) ev.modifierkeys;
-    data[P_REFID] = (unsigned) ev.ref_id;
+    data[P_REFID] = id;
     data[P_TOUCH] = (unsigned) ev.touch;
     data[P_TOUCH] = (unsigned) ev.touch;
 }
 }
 
 
@@ -154,12 +168,29 @@ IntRect UIWidget::GetRect()
     return rect;
     return rect;
 }
 }
 
 
+void UIWidget::SetRect(IntRect rect)
+{
+    if (!widget_)
+        return;
+
+    tb::TBRect tbrect;
+
+    tbrect.y = rect.top_;
+    tbrect.x = rect.left_;
+    tbrect.w = rect.right_ - rect.left_;
+    tbrect.h = rect.bottom_ - rect.top_;
+
+    widget_->SetRect(tbrect);
+
+}
+
+
 void UIWidget::SetSize(int width, int height)
 void UIWidget::SetSize(int width, int height)
 {
 {
     if (!widget_)
     if (!widget_)
         return;
         return;
 
 
-    widget_->SetSize(width, height);       
+    widget_->SetSize(width, height);
 }
 }
 
 
 void UIWidget::Invalidate()
 void UIWidget::Invalidate()
@@ -229,6 +260,41 @@ void UIWidget::Die()
 
 
 }
 }
 
 
+void UIWidget::SetLayoutParams(UILayoutParams* params)
+{
+    if (!widget_)
+        return;
+
+    widget_->SetLayoutParams(*(params->GetTBLayoutParams()));
+
+}
+
+void UIWidget::SetFontDescription(UIFontDescription* fd)
+{
+    if (!widget_)
+        return;
+
+    widget_->SetFontDescription(*(fd->GetTBFontDescription()));
+
+}
+
+void UIWidget::DeleteAllChildren()
+{
+    if (!widget_)
+        return;
+
+    widget_->DeleteAllChildren();
+}
+
+void UIWidget::SetSkinBg(const String& id)
+{
+    if (!widget_)
+        return;
+
+    widget_->SetSkinBg(TBIDC(id.CString()));
+
+}
+
 void UIWidget::RemoveChild(UIWidget* child, bool cleanup)
 void UIWidget::RemoveChild(UIWidget* child, bool cleanup)
 {
 {
     if (!widget_ || !child)
     if (!widget_ || !child)
@@ -287,7 +353,41 @@ void UIWidget::SetState(/*WIDGET_STATE*/ unsigned state, bool on)
         return;
         return;
 
 
     widget_->SetState((WIDGET_STATE) state, on);
     widget_->SetState((WIDGET_STATE) state, on);
+}
+
+void UIWidget::SetFocus()
+{
+    if (!widget_)
+        return;
 
 
+    widget_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+}
+
+void UIWidget::SetVisibility(/*WIDGET_VISIBILITY*/ unsigned visibility)
+{
+
+    if (!widget_)
+        return;
+
+    widget_->SetVisibilility((WIDGET_VISIBILITY) visibility);
+
+}
+
+UIWidget* UIWidget::GetFirstChild()
+{
+    if (!widget_)
+        return NULL;
+
+    return GetSubsystem<UI>()->WrapWidget(widget_->GetFirstChild());
+
+}
+
+UIWidget* UIWidget::GetNext()
+{
+    if (!widget_)
+        return NULL;
+
+    return GetSubsystem<UI>()->WrapWidget(widget_->GetNext());
 
 
 }
 }
 
 
@@ -355,6 +455,20 @@ bool UIWidget::OnEvent(const tb::TBWidgetEvent &ev)
 
 
         }
         }
 
 
+    }
+    else if (ev.type == EVENT_TYPE_TAB_CHANGED)
+    {
+        if (!ev.target || ui->IsWidgetWrapped(ev.target))
+        {
+            VariantMap eventData;
+            ConvertEvent(this, ui->WrapWidget(ev.target), ev, eventData);
+            SendEvent(E_WIDGETEVENT, eventData);
+
+            if (eventData[WidgetEvent::P_HANDLED].GetBool())
+                return true;
+
+        }
+
     }
     }
     else if (ev.type == EVENT_TYPE_CLICK)
     else if (ev.type == EVENT_TYPE_CLICK)
     {
     {
@@ -366,7 +480,9 @@ bool UIWidget::OnEvent(const tb::TBWidgetEvent &ev)
             {
             {
                 VariantMap eventData;
                 VariantMap eventData;
                 eventData[PopupMenuSelect::P_BUTTON] = this;
                 eventData[PopupMenuSelect::P_BUTTON] = this;
-                eventData[PopupMenuSelect::P_REFID] = (unsigned) ev.ref_id;
+                String id;
+                ui->GetTBIDString(ev.ref_id, id);
+                eventData[PopupMenuSelect::P_REFID] = id;
                 SendEvent(E_POPUPMENUSELECT, eventData);
                 SendEvent(E_POPUPMENUSELECT, eventData);
             }
             }
 
 

+ 21 - 1
Source/Atomic/UI/UIWidget.h

@@ -13,6 +13,9 @@ class TBWidget;
 namespace Atomic
 namespace Atomic
 {
 {
 
 
+class UILayoutParams;
+class UIFontDescription;
+
 /// Wraps a TurboBadger widget in our Object model
 /// Wraps a TurboBadger widget in our Object model
 class UIWidget : public Object, public tb::TBWidgetDelegate
 class UIWidget : public Object, public tb::TBWidgetDelegate
 {
 {
@@ -28,19 +31,26 @@ public:
     bool Load(const String& filename);
     bool Load(const String& filename);
 
 
     IntRect GetRect();
     IntRect GetRect();
+    void SetRect(IntRect r);
 
 
     void SetSize(int width, int height);
     void SetSize(int width, int height);
     void SetPosition(int x, int y);
     void SetPosition(int x, int y);
     void SetText(const String& text);
     void SetText(const String& text);
 
 
+    void SetSkinBg(const String& id);
+    void SetLayoutParams(UILayoutParams* params);
+    void SetFontDescription(UIFontDescription* fd);
+
     UIWidget* GetParent();
     UIWidget* GetParent();
     UIWidget* GetContentRoot();
     UIWidget* GetContentRoot();
 
 
     void RemoveChild(UIWidget* child, bool cleanup = true);
     void RemoveChild(UIWidget* child, bool cleanup = true);
 
 
+    void DeleteAllChildren();
+
     // String ID
     // String ID
     const String& GetId();
     const String& GetId();
-    void SetId(const String& id);
+    virtual void SetId(const String& id);
 
 
     void Center();
     void Center();
     void SetGravity(/*WIDGET_GRAVITY*/ unsigned gravity);
     void SetGravity(/*WIDGET_GRAVITY*/ unsigned gravity);
@@ -48,15 +58,25 @@ public:
     void SetValue(double value);
     void SetValue(double value);
     double GetValue();
     double GetValue();
 
 
+    void SetFocus();
+
     void SetState(/*WIDGET_STATE*/ unsigned state, bool on);
     void SetState(/*WIDGET_STATE*/ unsigned state, bool on);
     bool GetState(/*WIDGET_STATE*/ unsigned state);
     bool GetState(/*WIDGET_STATE*/ unsigned state);
 
 
+    void SetVisibility(/*WIDGET_VISIBILITY*/ unsigned visibility);
+
     void SetStateRaw(/*WIDGET_STATE*/ unsigned state);
     void SetStateRaw(/*WIDGET_STATE*/ unsigned state);
     /*WIDGET_STATE*/ unsigned GetStateRaw();
     /*WIDGET_STATE*/ unsigned GetStateRaw();
 
 
     void Invalidate();
     void Invalidate();
     void Die();
     void Die();
 
 
+    UIWidget* GetFirstChild();
+    UIWidget* GetNext();
+
+    void SetIsFocusable(bool value);
+
+
     // get this or child widget with id
     // get this or child widget with id
     UIWidget* GetWidget(const String& id);
     UIWidget* GetWidget(const String& id);
 
 

+ 1 - 1
Source/AtomicEditor/Source/AEJavascript.h

@@ -6,7 +6,7 @@
 
 
 #include <Atomic/Core/Object.h>
 #include <Atomic/Core/Object.h>
 
 
-typedef void duk_context;
+#include <Duktape/duktape.h>
 
 
 using namespace Atomic;
 using namespace Atomic;
 
 

+ 1 - 1
Source/AtomicEditor/Source/Javascript/JSErrorChecker.h

@@ -4,7 +4,7 @@
 
 
 #pragma once
 #pragma once
 
 
-typedef void duk_context;
+#include <Duktape/duktape.h>
 
 
 #include <Atomic/Core/Object.h>
 #include <Atomic/Core/Object.h>
 
 

+ 1 - 1
Source/AtomicEditor/Source/Player/AEPlayerApplication.cpp

@@ -221,7 +221,7 @@ void AEPlayerApplication::Start()
     vm = javascript->InstantiateVM("MainVM");
     vm = javascript->InstantiateVM("MainVM");
     vm->InitJSContext();
     vm->InitJSContext();
 
 
-    vm->SetModuleSearchPath("Modules");
+    vm->SetModuleSearchPaths("Modules");
 
 
     if (!vm->ExecuteMain())
     if (!vm->ExecuteMain())
     {
     {

+ 0 - 3
Source/AtomicEditor/Source/UI/UIMenubar.h

@@ -14,9 +14,6 @@ using namespace Atomic;
 namespace AtomicEditor
 namespace AtomicEditor
 {
 {
 
 
-class ListViewItemSource;
-class ListViewItemWidget;
-
 class MenubarItem : public TBGenericStringItem
 class MenubarItem : public TBGenericStringItem
 {
 {
 public:
 public:

+ 152 - 0
Source/AtomicEditorWork/Application/AEEditorApp.cpp

@@ -0,0 +1,152 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/Core/StringUtils.h>
+#include <Atomic/Engine/Engine.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Input/Input.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include <Atomic/UI/UI.h>
+
+#include <AtomicJS/Javascript/Javascript.h>
+
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/ToolEnvironment.h>
+
+#include "AEEditorApp.h"
+
+// Move me
+#include <Atomic/Environment/Environment.h>
+
+using namespace ToolCore;
+
+namespace ToolCore
+{
+    extern void jsapi_init_toolcore(JSVM* vm);
+}
+
+namespace AtomicEditor
+{
+
+extern void jsapi_init_editor(JSVM* vm);
+
+AEEditorApp::AEEditorApp(Context* context) :
+    Application(context)
+{
+
+}
+
+void AEEditorApp::Start()
+{
+    Input* input = GetSubsystem<Input>();
+    input->SetMouseVisible(true);
+
+    // move UI initialization to JS
+    UI* ui = GetSubsystem<UI>();
+    ui->Initialize("AtomicEditor/resources/language/lng_en.tb.txt");
+    ui->LoadSkin("AtomicEditor/resources/default_skin/skin.tb.txt", "AtomicEditor/editor/skin/skin.tb.txt");
+    ui->AddFont("AtomicEditor/resources/vera.ttf", "Vera");
+    ui->AddFont("AtomicEditor/resources/MesloLGS-Regular.ttf", "Monaco");
+    ui->SetDefaultFont("Vera", 12);
+
+    Javascript* javascript = new Javascript(context_);
+    context_->RegisterSubsystem(javascript);
+    SubscribeToEvent(E_JSERROR, HANDLER(AEEditorApp, HandleJSError));
+
+    // Instantiate and register the Javascript subsystem
+    vm_ = javascript->InstantiateVM("MainVM");
+    vm_->InitJSContext();
+    vm_->SetModuleSearchPaths("AtomicEditor/typescript");
+
+    jsapi_init_toolcore(vm_);
+    jsapi_init_editor(vm_);
+
+    SharedPtr<File> file (GetSubsystem<ResourceCache>()->GetFile("AtomicEditor/typescript/main.js"));
+
+    if (file.Null())
+    {
+        ErrorExit("Unable to load AtomicEditor/typescript/main.js");
+        return;
+    }
+
+    if (!vm_->ExecuteFile(file))
+    {
+        ErrorExit("Error executing AtomicEditor/typescript/main.js");
+        return;
+    }
+
+}
+
+void AEEditorApp::Setup()
+{
+    RegisterEnvironmentLibrary(context_);
+
+    FileSystem* filesystem = GetSubsystem<FileSystem>();
+    ToolEnvironment* env = new ToolEnvironment(context_);
+    context_->RegisterSubsystem(env);
+
+    ToolSystem* system = new ToolSystem(context_);
+    context_->RegisterSubsystem(system);
+
+
+#ifdef ATOMIC_DEV_BUILD
+
+    if (!env->InitFromJSON())
+    {
+        ErrorExit(ToString("Unable to initialize tool environment from %s", env->GetDevConfigFilename().CString()));
+        return;
+    }
+
+#endif
+
+    // env->Dump();
+
+    engineParameters_["WindowTitle"] = "AtomicEditor";
+    engineParameters_["WindowResizable"] = true;
+    engineParameters_["FullScreen"] = false;
+    engineParameters_["LogName"] = filesystem->GetAppPreferencesDir("AtomicEditor", "Logs") + "AtomicEditor.log";
+
+#ifdef ATOMIC_PLATFORM_OSX
+    engineParameters_["WindowIcon"] = "Images/AtomicLogo32.png";
+#endif
+
+#ifdef ATOMIC_DEV_BUILD
+    engineParameters_["ResourcePrefixPath"] = "";
+    String resourcePaths = env->GetCoreDataDir() + ";" +  env->GetEditorDataDir();
+    engineParameters_["ResourcePaths"] = resourcePaths;
+#else
+
+    #error ATOMIC_DEV_BUILD not defined
+
+#endif // ATOMIC_DEV_BUILD
+
+
+}
+
+void AEEditorApp::Stop()
+{
+
+}
+
+void AEEditorApp::HandleJSError(StringHash eventType, VariantMap& eventData)
+{
+    using namespace JSError;
+    //String errName = eventData[P_ERRORNAME].GetString();
+    String errMessage = eventData[P_ERRORMESSAGE].GetString();
+    String errFilename = eventData[P_ERRORFILENAME].GetString();
+    //String errStack = eventData[P_ERRORSTACK].GetString();
+    int errLineNumber = eventData[P_ERRORLINENUMBER].GetInt();
+
+    String errorString = ToString("%s - %s - Line: %i",
+                                  errFilename.CString(), errMessage.CString(), errLineNumber);
+
+    ErrorExit(errorString);
+
+}
+
+
+
+
+}

+ 40 - 0
Source/AtomicEditorWork/Application/AEEditorApp.h

@@ -0,0 +1,40 @@
+
+#pragma once
+
+#include <Atomic/Engine/Application.h>
+
+using namespace Atomic;
+
+namespace Atomic
+{
+    class JSVM;
+}
+
+namespace AtomicEditor
+{
+
+class AEEditorApp : public Application
+{
+    OBJECT(AEEditorApp);
+
+public:
+    /// Construct.
+    AEEditorApp(Context* context);
+
+    /// Setup before engine initialization. Verify that a script file has been specified.
+    virtual void Setup();
+    /// Setup after engine initialization. Load the script and execute its start function.
+    virtual void Start();
+    /// Cleanup after the main loop. Run the script's stop function if it exists.
+    virtual void Stop();
+
+private:
+
+    void HandleJSError(StringHash eventType, VariantMap& eventData);
+
+    SharedPtr<JSVM> vm_;
+
+
+};
+
+}

+ 39 - 0
Source/AtomicEditorWork/Application/AEPlayerApp.cpp

@@ -0,0 +1,39 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <Atomic/Engine/Engine.h>
+
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/ToolEnvironment.h>
+
+#include "AEPlayerApp.h"
+
+using namespace ToolCore;
+
+namespace AtomicEditor
+{
+
+AEPlayerApp::AEPlayerApp(Context* context) :
+    Application(context)
+{
+
+}
+
+void AEPlayerApp::Start()
+{
+
+}
+
+void AEPlayerApp::Setup()
+{
+
+
+}
+
+void AEPlayerApp::Stop()
+{
+
+}
+
+}

+ 30 - 0
Source/AtomicEditorWork/Application/AEPlayerApp.h

@@ -0,0 +1,30 @@
+
+#pragma once
+
+#include <Atomic/Engine/Application.h>
+
+using namespace Atomic;
+
+namespace AtomicEditor
+{
+
+class AEPlayerApp : public Application
+{
+    OBJECT(AEPlayerApp);
+
+public:
+    /// Construct.
+    AEPlayerApp(Context* context);
+
+    /// Setup before engine initialization. Verify that a script file has been specified.
+    virtual void Setup();
+    /// Setup after engine initialization. Load the script and execute its start function.
+    virtual void Start();
+    /// Cleanup after the main loop. Run the script's stop function if it exists.
+    virtual void Stop();
+
+private:
+
+};
+
+}

+ 122 - 0
Source/AtomicEditorWork/Application/Main.cpp

@@ -0,0 +1,122 @@
+
+#if defined(WIN32) && !defined(ATOMIC_WIN32_CONSOLE)
+#include <Atomic/Core/MiniDump.h>
+#include <windows.h>
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+#endif
+
+#include <Atomic/Core/ProcessUtils.h>
+#include <Atomic/IO/Log.h>
+
+#include "AEEditorApp.h"
+#include "AEPlayerApp.h"
+
+using namespace AtomicEditor;
+
+static int RunEditorApplication()
+{
+    Atomic::SharedPtr<Atomic::Context> context(new Atomic::Context());
+    Atomic::SharedPtr<AEEditorApp> application(new AEEditorApp(context));
+    return application->Run();
+}
+
+static int RunPlayerApplication()
+{
+    Atomic::SharedPtr<Atomic::Context> context(new Atomic::Context());
+    Atomic::SharedPtr<AEPlayerApp> application(new AEPlayerApp(context));
+    return application->Run();
+}
+
+
+// Define a platform-specific main function, which in turn executes the user-defined function
+
+// MSVC debug mode: use memory leak reporting
+#if defined(_MSC_VER) && defined(_DEBUG) && !defined(ATOMIC_WIN32_CONSOLE)
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
+{
+    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+    Atomic::ParseArguments(GetCommandLineW());
+
+    const Vector<String>& arguments = GetArguments();
+
+    bool runPlayer = false;
+    for (unsigned i = 0; i < arguments.Size();i++)
+    {
+        if (arguments.At(i) == "--player")
+        {
+            runPlayer = true;
+            break;
+        }
+    }
+
+    if (runPlayer)
+        return RunPlayerApplication();
+
+    return RunEditorApplication();
+
+}
+// MSVC release mode: write minidump on crash
+#elif defined(_MSC_VER) && defined(ATOMIC_MINIDUMPS) && !defined(ATOMIC_WIN32_CONSOLE)
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
+{
+    Atomic::ParseArguments(GetCommandLineW());
+    int exitCode;
+    __try
+    {
+        exitCode = function;
+    }
+    __except(Atomic::WriteMiniDump("Atomic", GetExceptionInformation()))
+    {
+    }
+    return exitCode;
+}
+// Other Win32 or minidumps disabled: just execute the function
+#elif defined(WIN32) && !defined(ATOMIC_WIN32_CONSOLE)
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd)
+{
+    Atomic::ParseArguments(GetCommandLineW());
+
+    const Vector<String>& arguments = GetArguments();
+
+    bool runPlayer = false;
+    for (unsigned i = 0; i < arguments.Size();i++)
+    {
+        if (arguments.At(i) == "--player")
+        {
+            runPlayer = true;
+            break;
+        }
+    }
+
+    if (runPlayer)
+        return RunPlayerApplication();
+
+    return RunEditorApplication();
+}
+// Linux or OS X: use main
+#else
+int main(int argc, char** argv)
+{
+    Atomic::ParseArguments(argc, argv);
+
+    const Vector<String>& arguments = GetArguments();
+
+    bool runPlayer = false;
+    for (unsigned i = 0; i < arguments.Size();i++)
+    {
+        if (arguments.At(i) == "--player")
+        {
+            runPlayer = true;
+            break;
+        }
+    }
+
+    if (runPlayer)
+        return RunPlayerApplication();
+
+    return RunEditorApplication();
+}
+#endif

+ 67 - 0
Source/AtomicEditorWork/CMakeLists.txt

@@ -0,0 +1,67 @@
+include_directories (${CMAKE_SOURCE_DIR}/Source/ThirdParty/rapidjson/include
+                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty
+                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty/nativefiledialog)
+
+
+file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
+
+# Create the JSBind files ahead of time, so they are picked up with glob
+set (JSFILES JSPackageEditor.cpp;JSModuleEditor.cpp)
+
+foreach(JSFILE ${JSFILES})
+
+  set (JSFILEPATH "${CMAKE_SOURCE_DIR}/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/Editor/${JSFILE}")
+
+  if (NOT EXISTS ${JSFILEPATH})
+    file(WRITE "${JSFILEPATH}" "// will be created by JSBind")
+  endif()
+
+endforeach()
+
+file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/Editor/*.cpp)
+
+set (SOURCE_FILES ${SOURCE_FILES} ${JAVASCRIPT_BINDINGS_SOURCE})
+
+if (APPLE)
+    set (EXE_TYPE MACOSX_BUNDLE)
+
+    #ICNS
+    set(MACOSX_BUNDLE_ICON_FILE Atomic.icns)
+    set(ATOMIC_EDITOR_ICON ${CMAKE_SOURCE_DIR}/CMake/Modules/Atomic.icns)
+    set_source_files_properties(${ATOMIC_EDITOR_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
+
+elseif(LINUX)
+
+else()
+    include_directories (${CMAKE_SOURCE_DIR}/Source/ThirdParty/libcurl/include)
+    add_definitions(-DCURL_STATICLIB)
+
+    # We want the console for now
+    #set (EXE_TYPE WIN32)
+    add_definitions(-DATOMIC_WIN32_CONSOLE)
+
+    set (SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/CMake/Modules/Atomic.rc)
+endif(APPLE)
+
+add_executable(AtomicEditorWork ${EXE_TYPE} ${SOURCE_FILES} ${ATOMIC_EDITOR_ICON})
+
+target_link_libraries(AtomicEditorWork ToolCore AtomicJS ToolCoreJS Poco nativefiledialog ${ATOMIC_LINK_LIBRARIES})
+
+if (APPLE)
+    set (TARGET_PROPERTIES MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.template)
+    target_link_libraries(AtomicEditorWork curl)
+elseif(LINUX)
+    target_link_libraries(AtomicEditorWork curl nativefiledialog ${GTK3_LIBRARIES})
+else()
+    target_link_libraries(AtomicEditorWork libcurl Iphlpapi Wldap32)
+
+    # pre-Windows 8 can't count on D3DCompiler_47.dll being on system
+    add_custom_command (TARGET AtomicEditorWork POST_BUILD
+    COMMAND ${CMAKE_COMMAND}
+    ARGS -E copy_if_different \"${D3DCOMPILER_47_DLL}\" \"$<TARGET_FILE_DIR:AtomicEditor>/D3DCompiler_47.dll\")
+
+endif()
+
+if (TARGET_PROPERTIES)
+    set_target_properties (AtomicEditorWork PROPERTIES ${TARGET_PROPERTIES})
+endif ()

+ 620 - 0
Source/AtomicEditorWork/Editors/JSResourceEditor.cpp

@@ -0,0 +1,620 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Container/ArrayPtr.h>
+#include <Atomic/UI/UI.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/File.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceCache.h>
+#include <Atomic/Core/CoreEvents.h>
+#include <AtomicJS/Javascript/JSVM.h>
+
+#include "JSResourceEditor.h"
+
+#include "../Javascript/JSAutocomplete.h"
+#include "../Javascript/JSTheme.h"
+#include "../Javascript/JSASTSyntaxColorVisitor.h"
+
+#include <TurboBadger/tb_message_window.h>
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+#include <TurboBadger/tb_style_edit_content.h>
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+JSResourceEditor ::JSResourceEditor(Context* context, const String &fullpath, UITabContainer *container) :
+    ResourceEditor(context, fullpath, container),
+    styleEdit_(0),
+    lineNumberList_(0),
+    editField_(0),
+    autocomplete_(0),
+    textDirty_(true),
+    textDelta_(0.0f),
+    modified_(false),
+    currentFindPos_(-1)
+{
+
+    TBLayout* layout = new TBLayout();
+    layout->SetLayoutSize(LAYOUT_SIZE_GRAVITY);
+    layout->SetGravity(WIDGET_GRAVITY_ALL);
+    layout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+    rootContentWidget_->GetInternalWidget()->AddChild(layout);
+
+    TBContainer* c = new TBContainer();
+    c->SetGravity(WIDGET_GRAVITY_ALL);
+
+    TBEditField* text = editField_ = new TBEditField();
+    text->SetMultiline(true);
+    text->SetWrapping(true);
+    text->SetGravity(WIDGET_GRAVITY_ALL);
+    text->SetStyling(true);
+    text->SetSkinBg(TBIDC("TextCode"));
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Monaco"));
+    fd.SetSize(12);
+    text->SetFontDescription(fd);
+
+    SharedPtr<File> jsFile(GetSubsystem<ResourceCache>()->GetFile(fullpath));
+    assert(jsFile);
+
+    String source;
+    jsFile->ReadText(source);
+
+    String json;
+
+    JSASTProgram* program = NULL;
+
+    if (ParseJavascriptToJSON(source.CString(), json))
+    {
+        program = JSASTProgram::ParseFromJSON(fullpath, json);
+    }
+
+    text->SetText(source.CString());
+
+    lineNumberList_ = new TBSelectList();
+    lineNumberList_->SetFontDescription(fd);
+    lineNumberList_->SetSkinBg(TBIDC("LineNumberSelectList"));
+    lineNumberList_->GetScrollContainer()->SetScrollMode(SCROLL_MODE_OFF);
+    //lineNumberList_->GetScrollContainer()->SetIgnoreScrollEvents(true);
+    lineNumberList_->SetGravity(WIDGET_GRAVITY_ALL);
+    LayoutParams lp;
+    lp.max_w = 48;
+    lineNumberList_->SetLayoutParams(lp);
+
+    c->AddChild(text);
+
+    layout->AddChild(lineNumberList_);
+    layout->AddChild(c);
+    layout->SetSpacing(0);
+
+    TBStyleEdit* sedit = text->GetStyleEdit();
+    TBTextTheme* theme = new TBTextTheme();
+    for (unsigned i = 0; i < TB_MAX_TEXT_THEME_COLORS; i++)
+        theme->themeColors[i] = TBColor(255, 255, 255);
+
+    theme->themeColors[JSTHEME_LITERAL_STRING].SetFromString("#E6DB74", 7);
+
+    theme->themeColors[JSTHEME_LITERAL_NUMBER].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_REGEX].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_BOOLEAN].SetFromString("#AE81FF", 7);
+    theme->themeColors[JSTHEME_LITERAL_NULL].SetFromString("#AE81FF", 7);
+
+    theme->themeColors[JSTHEME_FUNCTION].SetFromString("#66D9EF", 7);
+    theme->themeColors[JSTHEME_VAR].SetFromString("#66D9EF", 7);
+
+
+    theme->themeColors[JSTHEME_KEYWORD].SetFromString("#f92672", 7);
+    theme->themeColors[JSTHEME_OPERATOR].SetFromString("#f92672", 7);
+
+    theme->themeColors[JSTHEME_CODE].SetFromString("#a6e22e", 7);
+    theme->themeColors[JSTHEME_COMMENT].SetFromString("#75715e", 7);
+
+    theme->themeColors[JSTHEME_FUNCTIONDECLARG].SetFromString("#FF9800", 7);
+
+    sedit->SetTextTheme(theme);
+
+    sedit->text_change_listener = this;
+
+    styleEdit_ = sedit;
+    UpdateLineNumbers();
+
+    if (program)
+    {
+        JSASTSyntaxColorVisitor syntaxColor(sedit);
+        syntaxColor.visit(program);
+    }
+
+    autocomplete_ = new JSAutocomplete(text);
+    autocomplete_->UpdateLocals();
+
+    SubscribeToEvent(E_UPDATE, HANDLER(JSResourceEditor, HandleUpdate));
+
+    // FIXME: Set the size at the end of setup, so all children are updated accordingly
+    // future size changes will be handled automatically
+    IntRect rect = container_->GetContentRoot()->GetRect();
+    rootContentWidget_->SetSize(rect.Width(), rect.Height());
+}
+
+JSResourceEditor::~JSResourceEditor()
+{
+}
+
+void JSResourceEditor::UpdateLineNumbers()
+{
+    if (!styleEdit_)
+        return;
+
+    TBGenericStringItemSource* lineSource = lineNumberList_->GetDefaultSource();
+
+    int lines = lineSource->GetNumItems();
+
+    int lineCount = styleEdit_->blocks.CountLinks();
+
+    if (lines == lineCount)
+        return;
+
+    while (lines > lineCount)
+    {
+        lineSource->DeleteItem(lineSource->GetNumItems() - 1);
+        lines --;
+    }
+
+    for (int i = lines; i < lineCount; i++)
+    {
+        String sline;
+        sline.AppendWithFormat("%i  ", i + 1);
+        TBGenericStringItem* item = new TBGenericStringItem(sline.CString());
+        lineSource->AddItem(item);
+
+    }
+
+    // item widgets don't exist until ValidateList
+    lineNumberList_->ValidateList();
+
+    for (int i = 0; i < lineCount; i++)
+    {
+        TBTextField* textField = (TBTextField* )lineNumberList_->GetItemWidget(i);
+
+        if (textField)
+        {
+            textField->SetTextAlign(TB_TEXT_ALIGN_RIGHT);
+            textField->SetSkinBg(TBIDC("TBSelectItemLineNumber"));
+        }
+
+    }
+
+}
+
+void JSResourceEditor::OnChange(TBStyleEdit* styleEdit)
+{
+    textDelta_ = 0.25f;
+    textDirty_ = true;
+    modified_ = true;
+
+    String filename = GetFileNameAndExtension(fullpath_);
+    filename += "*";
+    button_->SetText(filename.CString());
+
+    autocomplete_->Hide();
+
+    TBTextFragment* fragment = 0;
+    int ofs = styleEdit_->caret.pos.ofs;
+    fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+
+    if (fragment && fragment->len && (styleEdit_->caret.pos.ofs == (fragment->ofs + fragment->len)))
+    {
+        String value(fragment->Str(), fragment->len);
+        bool hasCompletions = autocomplete_->UpdateCompletions(value);
+
+        if (hasCompletions)
+        {
+            autocomplete_->SetPosition(TBPoint(fragment->xpos, (styleEdit_->caret.y - styleEdit_->scroll_y) + fragment->line_height));
+            autocomplete_->Show();
+        }
+    }
+
+    UpdateLineNumbers();
+}
+
+bool JSResourceEditor::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_KEY_DOWN)
+    {
+        if (autocomplete_ && autocomplete_->Visible())
+        {
+            return autocomplete_->OnEvent(ev);
+        }
+
+        if (ev.special_key == TB_KEY_ESC)
+        {
+            //SendEvent(E_FINDTEXTCLOSE);
+        }
+
+    }
+
+    if (ev.type == EVENT_TYPE_SHORTCUT)
+    {
+        if (ev.ref_id == TBIDC("close"))
+        {
+            if (modified_)
+            {
+                TBMessageWindow *msg_win = new TBMessageWindow(container_->GetInternalWidget(), TBIDC("unsaved_jsmodifications_dialog"));
+                TBMessageWindowSettings settings(TB_MSG_OK_CANCEL, TBID(uint32(0)));
+                settings.dimmer = true;
+                settings.styling = true;
+                msg_win->Show("Unsaved Modifications", "There are unsaved modications.\nDo you wish to discard them and close?", &settings, 640, 360);
+            }
+            else
+            {
+                Close();
+            }
+
+        }
+
+        if (ev.ref_id == TBIDC("save") && modified_)
+        {
+            TBStr text;
+            styleEdit_->GetText(text);
+            File file(context_, fullpath_, FILE_WRITE);
+            file.Write((void*) text.CStr(), text.Length());
+            file.Close();
+
+            String filename = GetFileNameAndExtension(fullpath_);
+            button_->SetText(filename.CString());
+            modified_ = false;
+            //SendEvent(E_JAVASCRIPTSAVED);
+
+            return true;
+        }
+        else if (ev.ref_id == TBIDC("find"))
+        {
+            //using namespace FindTextOpen;
+            //SendEvent(E_FINDTEXTOPEN);
+        }
+        else if (ev.ref_id == TBIDC("findnext") || ev.ref_id == TBIDC("findprev"))
+        {
+            /*
+            String text;
+
+            FindTextWidget* finder = GetSubsystem<FindTextWidget>();
+            finder->GetFindText(text);
+
+            // TODO: get flags from finder
+            unsigned flags = FINDTEXT_FLAG_NONE;
+
+            if (ev.ref_id == TBIDC("findnext"))
+                flags |= FINDTEXT_FLAG_NEXT;
+            else if (ev.ref_id == TBIDC("findprev"))
+                flags |= FINDTEXT_FLAG_PREV;
+
+            flags |= FINDTEXT_FLAG_WRAP;
+
+            finder->Find(text, flags);
+            */
+        }
+        else if (ev.ref_id == TBIDC("beautify"))
+        {
+            TBStr text;
+            styleEdit_->GetText(text);
+
+            if (text.Length())
+            {
+                String output;
+                if (BeautifyJavascript(text.CStr(), output))
+                {
+                    if (output.Length())
+                    {
+                        styleEdit_->selection.SelectAll();
+                        styleEdit_->InsertText(output.CString(), output.Length());
+                    }
+                }
+            }
+        }
+        else if (ev.ref_id == TBIDC("cut") || ev.ref_id == TBIDC("copy") || ev.ref_id == TBIDC("paste")
+                 || ev.ref_id == TBIDC("selectall") || ev.ref_id == TBIDC("undo") || ev.ref_id == TBIDC("redo") )
+        {
+            editField_->OnEvent(ev);
+        }
+    }
+
+    if (ev.type == EVENT_TYPE_CLICK)
+    {
+        if (ev.target->GetID() == TBIDC("unsaved_jsmodifications_dialog"))
+        {
+            if (ev.ref_id == TBIDC("TBMessageWindow.ok"))
+            {
+                Close();
+            }
+            else
+            {
+                SetFocus();
+            }
+
+            return true;
+        }
+
+    }
+
+    return false;
+}
+
+void JSResourceEditor::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+
+    if (!styleEdit_)
+        return;
+
+    // sync line number
+    lineNumberList_->GetScrollContainer()->ScrollTo(0, styleEdit_->scroll_y);
+    lineNumberList_->SetValue(styleEdit_->GetCaretLine());
+
+    if (autocomplete_->Visible())
+    {
+        TBTextFragment* fragment = 0;
+        int ofs = styleEdit_->caret.pos.ofs;
+        fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+
+        if (fragment && (styleEdit_->caret.pos.ofs == (fragment->ofs + fragment->len)))
+        {
+            String value(fragment->Str(), fragment->len);
+            bool hasCompletions = autocomplete_->UpdateCompletions(value);
+
+            if (!hasCompletions)
+            {
+                autocomplete_->Hide();
+            }
+        }
+
+    }
+
+    // Timestep parameter is same no matter what event is being listened to
+    float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
+
+    if (!textDirty_)
+        return;
+
+    if (textDelta_ > 0.0f)
+    {
+        textDelta_ -= timeStep;
+        if (textDelta_ < 0.0f)
+        {
+            textDelta_ = 0.0f;
+        }
+        else
+        {
+            return;
+        }
+    }
+
+    TBStr text;
+    styleEdit_->GetText(text);
+
+    JSASTProgram* program = NULL;
+    String json;
+    if (ParseJavascriptToJSON(text.CStr(), json))
+    {
+        program = JSASTProgram::ParseFromJSON("fullpath", json);
+
+        if (program)
+        {
+            JSASTSyntaxColorVisitor syntaxColor(styleEdit_);
+            syntaxColor.visit(program);
+            delete program;
+        }
+
+    }
+
+    textDirty_ = false;
+
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+
+}
+
+void JSResourceEditor::FindTextClose()
+{
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+    styleEdit_->selection.SelectNothing();
+}
+
+bool JSResourceEditor::FindText(const String& findText, unsigned flags)
+{
+    /*
+    unsigned findLength = findText.Length();
+
+    if (!findLength)
+        return true;
+
+    TBStr _source;
+    styleEdit_->GetText(_source);
+    String source = _source.CStr();
+
+    unsigned pos = String::NPOS;
+    int startPos = currentFindPos_;
+
+    if (currentFindPos_ == -1)
+        startPos = styleEdit_->caret.GetGlobalOfs();
+    else
+    {
+        if (flags & FINDTEXT_FLAG_NEXT)
+            startPos += findLength;
+    }
+
+    if (flags & FINDTEXT_FLAG_PREV)
+    {
+        String pretext = source.Substring(0, startPos);
+        pos = pretext.FindLast(findText, String::NPOS, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+    }
+    else
+    {
+        pos = source.Find(findText, startPos, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+    }
+
+    if (pos == String::NPOS)
+    {
+        if (flags & FINDTEXT_FLAG_WRAP)
+        {
+            if (flags & FINDTEXT_FLAG_PREV)
+            {
+                pos = source.FindLast(findText, String::NPOS, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+            }
+            else
+            {
+                pos = source.Find(findText, 0, flags & FINDTEXT_FLAG_CASESENSITIVE ? true : false);
+            }
+        }
+
+        if (pos == String::NPOS)
+        {
+            styleEdit_->selection.SelectNothing();
+            return true;
+        }
+    }
+
+    currentFindPos_ = pos;
+
+    styleEdit_->caret.SetGlobalOfs((int) pos + findLength);
+
+    int height = styleEdit_->layout_height;
+
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+    styleEdit_->selection.Select(pos, pos + findLength);
+    */
+    return true;
+}
+
+void JSResourceEditor::SetFocus()
+{
+    editField_->SetFocus(WIDGET_FOCUS_REASON_UNKNOWN);
+}
+
+void JSResourceEditor::GotoTokenPos(int tokenPos)
+{
+    styleEdit_->caret.SetGlobalOfs(tokenPos);
+
+    int height = styleEdit_->layout_height;
+
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+}
+
+void JSResourceEditor::GotoLineNumber(int lineNumber)
+{
+    int line = 0;
+    TBBlock *block = NULL;
+    for (block = styleEdit_->blocks.GetFirst(); block; block = block->GetNext())
+    {
+        if (lineNumber == line)
+            break;
+        line++;
+    }
+    if (!block)
+        return;
+
+    styleEdit_->caret.Place(block, 0);
+
+    int height = styleEdit_->layout_height;
+    int newy = styleEdit_->caret.y - height/2;
+
+    styleEdit_->SetScrollPos(styleEdit_->scroll_x, newy);
+
+}
+
+
+bool JSResourceEditor::HasUnsavedModifications()
+{
+    return modified_;
+}
+
+bool JSResourceEditor::ParseJavascriptToJSON(const char* source, String& json, bool loose)
+{
+
+    JSVM* vm = JSVM::GetJSVM(NULL);
+    duk_context* ctx = vm->GetJSContext();
+
+    int top = duk_get_top(ctx);
+
+    json.Clear();
+
+    duk_get_global_string(ctx, "require");
+    duk_push_string(ctx, "AtomicEditor/typescript/modules/jsutils");
+    if (duk_pcall(ctx, 1))
+    {
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+        duk_set_top(ctx, top);
+        return false;
+    }
+
+    duk_get_prop_string(ctx, -1, "parseToJSON");
+    duk_push_string(ctx, source);
+    bool ok = true;
+
+    if (duk_pcall(ctx, 1))
+    {
+        ok = false;
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+    }
+    else
+    {
+        json = duk_to_string(ctx, -1);
+    }
+
+    duk_set_top(ctx, top);
+
+    return ok;
+}
+
+bool JSResourceEditor::BeautifyJavascript(const char* source, String& output)
+{
+    JSVM* vm = JSVM::GetJSVM(NULL);
+    duk_context* ctx = vm->GetJSContext();
+
+    int top = duk_get_top(ctx);
+
+    output.Clear();
+
+    duk_get_global_string(ctx, "require");
+    duk_push_string(ctx, "AtomicEditor/typescript/modules/jsutils");
+
+    if (duk_pcall(ctx, 1))
+    {
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+        duk_set_top(ctx, top);
+        return false;
+    }
+
+
+    duk_get_prop_string(ctx, -1, "jsBeautify");
+    duk_push_string(ctx, source);
+    bool ok = true;
+
+    if (duk_pcall(ctx, 1))
+    {
+        ok = false;
+        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
+    }
+    else
+    {
+        output = duk_to_string(ctx, -1);
+    }
+
+    // ignore result
+    duk_set_top(ctx, top);
+
+    return ok;
+
+}
+
+}

+ 66 - 0
Source/AtomicEditorWork/Editors/JSResourceEditor.h

@@ -0,0 +1,66 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include "ResourceEditor.h"
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+#include <TurboBadger/tb_select.h>
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class JSAutocomplete;
+
+class JSResourceEditor: public ResourceEditor, public TBStyleEditTextChangeListener
+{
+    OBJECT(JSResourceEditor);
+
+public:
+
+    JSResourceEditor(Context* context, const String& fullpath, UITabContainer* container);
+
+    virtual ~JSResourceEditor();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    bool FindText(const String& findText, unsigned flags);
+    void FindTextClose();
+
+    void OnChange(TBStyleEdit* styleEdit);
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);    
+
+    void GotoTokenPos(int tokenPos);
+    void GotoLineNumber(int lineNumber);
+
+    void SetFocus();
+
+    bool HasUnsavedModifications();
+
+private:
+
+    bool ParseJavascriptToJSON(const char* source, String& json, bool loose = true);
+    bool BeautifyJavascript(const char* source, String& output);
+    void UpdateLineNumbers();
+
+    tb::TBStyleEdit* styleEdit_;
+    tb::TBSelectList* lineNumberList_;
+
+    TBEditField* editField_;
+    JSAutocomplete* autocomplete_;
+
+    float textDelta_;
+    bool textDirty_;
+
+    bool modified_;
+
+    int currentFindPos_;
+
+};
+
+}

+ 128 - 0
Source/AtomicEditorWork/Editors/ResourceEditor.cpp

@@ -0,0 +1,128 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include <TurboBadger/tb_tab_container.h>
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceEvents.h>
+
+#include "ResourceEditor.h"
+
+//#include "../UI/UIMainFrame.h"
+//#include "../UI/UIResourceFrame.h"
+
+namespace AtomicEditor
+{
+
+class EditorTabLayout: public TBLayout
+{
+public:
+
+    ResourceEditor* editor_;
+    TBButton* button_;
+    TBButton* close_;
+    TBTabContainer* container_;
+
+    void SetValue(int value)
+    {
+        button_->SetValue(value);
+    }
+
+    bool OnEvent(const TBWidgetEvent &ev)
+    {
+        if (ev.type == EVENT_TYPE_CLICK || ev.type == EVENT_TYPE_POINTER_DOWN)
+        {
+            if (ev.target->GetID() == TBIDC("tabclose"))
+            {
+                container_->OnEvent(ev);
+                editor_->Close();
+                return true;
+            }
+            else
+            {
+                TBWidgetEvent nevent = ev;
+                nevent.target = this;
+                container_->OnEvent(nevent);
+            }
+        }
+
+        return false;
+    }
+};
+
+ResourceEditor::ResourceEditor(Context* context, const String& fullpath, UITabContainer *container):
+    Object(context), fullpath_(fullpath), container_(container),
+    editorTabLayout_(0), rootContentWidget_(0), button_(0)
+{
+
+    String filename = GetFileNameAndExtension(fullpath_);
+
+    editorTabLayout_ = new EditorTabLayout();
+    editorTabLayout_->SetID(TBIDC("tab"));
+
+    button_ = new UIButton(context_);
+    button_->SetText(filename.CString());
+    button_->SetSqueezable(true);
+    button_->SetSkinBg("TBButton.flat");
+    button_->SetValue(1);
+    editorTabLayout_->AddChild(button_->GetInternalWidget());
+
+    TBButton* closebutton = new TBButton();
+    editorTabLayout_->AddChild(closebutton);
+    closebutton->SetSkinBg(TBIDC("TBWindow.close"));
+    closebutton->SetIsFocusable(false);
+    closebutton->SetID(TBIDC("tabclose"));
+
+    editorTabLayout_->editor_ = this;
+    editorTabLayout_->button_ = (TBButton*) button_->GetInternalWidget();
+    editorTabLayout_->close_ = closebutton;
+    editorTabLayout_->container_ = (TBTabContainer*) container->GetInternalWidget();
+
+    ((TBTabContainer*)container_->GetInternalWidget())->GetTabLayout()->AddChild(editorTabLayout_);
+
+    rootContentWidget_ = new UIWidget(context_);
+    rootContentWidget_->SetGravity(WIDGET_GRAVITY_ALL);
+    container_->GetContentRoot()->AddChild(rootContentWidget_);
+
+    SubscribeToEvent(E_FILECHANGED, HANDLER(ResourceEditor, HandleFileChanged));
+}
+
+ResourceEditor::~ResourceEditor()
+{
+
+}
+
+void ResourceEditor::HandleFileChanged(StringHash eventType, VariantMap& eventData)
+{
+    using namespace FileChanged;
+    const String& fileName = eventData[P_FILENAME].GetString();
+    const String& resourceName = eventData[P_RESOURCENAME].GetString();
+
+    if (fullpath_ == fileName)
+    {
+        FileSystem* fs = GetSubsystem<FileSystem>();
+        if (!fs->FileExists(fullpath_))
+            Close();
+    }
+}
+
+void ResourceEditor::Close(bool navigateToAvailableResource)
+{
+    // keep us alive through the close
+    SharedPtr<ResourceEditor> keepalive(this);
+
+    ((TBTabContainer*)container_->GetInternalWidget())->GetTabLayout()->RemoveChild(editorTabLayout_);
+
+    VariantMap data;
+    data["Editor"] = this;
+    data["NavigateToAvailableResource"] = navigateToAvailableResource;
+    SendEvent("CloseResourceEditor", data);
+
+    //MainFrame* frame = GetSubsystem<MainFrame>();
+    //ResourceFrame* rframe = frame->GetResourceFrame();
+    //rframe->CloseResourceEditor(this, navigateToAvailabeResource);
+}
+
+}

+ 64 - 0
Source/AtomicEditorWork/Editors/ResourceEditor.h

@@ -0,0 +1,64 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+#include <Atomic/UI/UIButton.h>
+#include <Atomic/UI/UITabContainer.h>
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class EditorTabLayout;
+
+class ResourceEditor: public Object
+{
+    OBJECT(ResourceEditor);
+
+public:
+
+    ResourceEditor(Context* context, const String& fullpath, UITabContainer* container);
+
+    virtual ~ResourceEditor();
+
+    UIButton* GetButton() { return button_; }
+
+    virtual bool HasUnsavedModifications() { return false; }
+
+    virtual void SetFocus() {}
+    virtual void Close(bool navigateToAvailableResource = true);
+
+    virtual bool OnEvent(const TBWidgetEvent &ev) { return false; }
+
+    virtual bool FindText(const String& text, unsigned flags) { return false; }
+    virtual void FindTextClose() { }
+
+    virtual bool RequiresInspector() { return false; }
+
+    const String& GetFullPath() { return fullpath_; }
+
+    UIWidget* GetRootContentWidget() { return rootContentWidget_; }
+
+protected:
+
+    String fullpath_;
+
+    EditorTabLayout* editorTabLayout_;
+
+    SharedPtr<UITabContainer> container_;
+    SharedPtr<UIWidget> rootContentWidget_;
+    SharedPtr<UIButton> button_;
+
+private:
+
+    void HandleFileChanged(StringHash eventType, VariantMap& eventData);
+
+};
+
+}

+ 437 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/Gizmo3D.cpp

@@ -0,0 +1,437 @@
+// Portions Copyright (c) 2008-2015 the Urho3D project.
+
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include <Atomic/Atomic3D/Model.h>
+#include <Atomic/Graphics/Material.h>
+#include <Atomic/Graphics/Octree.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include <Atomic/Input/Input.h>
+
+#include "Gizmo3D.h"
+
+namespace AtomicEditor
+{
+
+
+Gizmo3D::Gizmo3D(Context* context) : Object(context)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    gizmoNode_ = new Node(context_);
+    gizmo_ = gizmoNode_->CreateComponent<StaticModel>();
+    gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
+
+    gizmo_->SetEnabled(false);
+    gizmo_->SetViewMask(0x80000000); // Editor raycasts use viewmask 0x7fffffff
+    gizmo_->SetOccludee(false);
+
+    axisMode_ = AXIS_LOCAL;
+
+    gizmoAxisX_.lastSelected_ = false;
+    gizmoAxisY_.lastSelected_ = false;
+    gizmoAxisZ_.lastSelected_ = false;
+
+    editMode_ = EDIT_MOVE;
+    lastEditMode_ = EDIT_SELECT;
+}
+
+Gizmo3D::~Gizmo3D()
+{
+
+}
+
+void Gizmo3D::SetView(SceneView3D* view3D)
+{
+    view3D_ = view3D;
+    scene_ = view3D->GetScene();
+    camera_ = view3D->GetCameraNode()->GetComponent<Camera>();
+    assert(camera_.NotNull());
+}
+
+void Gizmo3D::Position()
+{
+    Vector3 center(0, 0, 0);
+    bool containsScene = false;
+
+    for (unsigned i = 0; i < editNodes_->Size(); ++i)
+    {
+        // Scene's transform should not be edited, so hide gizmo if it is included
+        if (editNodes_->At(i) == scene_)
+        {
+            containsScene = true;
+            break;
+        }
+        center += editNodes_->At(i)->GetWorldPosition();
+    }
+
+    if (editNodes_->Empty() || containsScene)
+    {
+        Hide();
+        return;
+    }
+
+    center /= editNodes_->Size();
+    gizmoNode_->SetPosition(center);
+
+    if (axisMode_ == AXIS_WORLD || editNodes_->Size() > 1)
+        gizmoNode_->SetRotation(Quaternion());
+    else
+        gizmoNode_->SetRotation(editNodes_->At(0)->GetWorldRotation());
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    if (editMode_ != lastEditMode_)
+    {
+        switch (editMode_)
+        {
+        case EDIT_MOVE:
+            gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/Axes.mdl"));
+            break;
+
+        case EDIT_ROTATE:
+            gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/RotateAxes.mdl"));
+            break;
+
+        case EDIT_SCALE:
+            gizmo_->SetModel(cache->GetResource<Model>("AtomicEditor/Models/ScaleAxes.mdl"));
+            break;
+
+        default:
+            break;
+        }
+
+        lastEditMode_ = editMode_;
+    }
+
+    bool orbiting = false;
+    if ((editMode_ != EDIT_SELECT && !orbiting) && !gizmo_->IsEnabled())
+        Show();
+    else if ((editMode_ == EDIT_SELECT || orbiting) && gizmo_->IsEnabled())
+        Hide();
+
+    if (gizmo_->IsEnabled())
+    {
+        float scale = 0.1f / camera_->GetZoom();
+
+        if (camera_->IsOrthographic())
+            scale *= camera_->GetOrthoSize();
+        else
+            scale *= (camera_->GetView() * gizmoNode_->GetPosition()).z_;
+
+        gizmoNode_->SetScale(Vector3(scale, scale, scale));
+
+    }
+}
+
+void Gizmo3D::Update(Vector<Node *> &editNodes)
+{
+    editNodes_ = &editNodes;
+
+    Use();
+    Position();
+
+
+}
+
+void Gizmo3D::CalculateGizmoAxes()
+{
+    gizmoAxisX_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(1, 0, 0));
+    gizmoAxisY_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 1, 0));
+    gizmoAxisZ_.axisRay_ = Ray(gizmoNode_->GetPosition(), gizmoNode_->GetRotation() * Vector3(0, 0, 1));
+}
+
+void Gizmo3D::Use()
+{
+    if (gizmo_.Null() || !gizmo_->IsEnabled() || editMode_ == EDIT_SELECT)
+    {
+        //StoreGizmoEditActions();
+        //previousGizmoDrag = false;
+        return;
+    }
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    Input* input = GetSubsystem<Input>();
+
+    Ray cameraRay = view3D_->GetCameraRay();
+    float scale = gizmoNode_->GetScale().x_;
+
+    // Recalculate axes only when not left-dragging
+    bool drag = input->GetMouseButtonDown(MOUSEB_LEFT);// && (Abs(input->GetMouseMoveX()) > 3 || Abs(input->GetMouseMoveY()) > 3);
+    if (!drag)
+        CalculateGizmoAxes();
+
+    gizmoAxisX_.Update(cameraRay, scale, drag, camera_->GetNode());
+    gizmoAxisY_.Update(cameraRay, scale, drag, camera_->GetNode());
+    gizmoAxisZ_.Update(cameraRay, scale, drag, camera_->GetNode());
+
+    if (!editNodes_->Size() || editNodes_->At(0) == scene_)
+    {
+        gizmoAxisX_.selected_ = gizmoAxisY_.selected_ = gizmoAxisZ_.selected_ = false;
+        // this just forces an update
+        gizmoAxisX_.lastSelected_ = gizmoAxisY_.lastSelected_ = gizmoAxisZ_.lastSelected_ = true;
+    }
+
+
+    if (gizmoAxisX_.selected_ != gizmoAxisX_.lastSelected_)
+    {
+        gizmo_->SetMaterial(0, cache->GetResource<Material>(
+                                gizmoAxisX_.selected_ ?
+                                    "AtomicEditor/Materials/BrightRedUnlit.xml" : "AtomicEditor/Materials/RedUnlit.xml"));
+
+        gizmoAxisX_.lastSelected_ = gizmoAxisX_.selected_;
+    }
+
+    if (gizmoAxisY_.selected_ != gizmoAxisY_.lastSelected_)
+    {
+        gizmo_->SetMaterial(1, cache->GetResource<Material>(
+                                gizmoAxisY_.selected_ ?
+                                    "AtomicEditor/Materials/BrightGreenUnlit.xml" : "AtomicEditor/Materials/GreenUnlit.xml"));
+
+        gizmoAxisY_.lastSelected_ = gizmoAxisY_.selected_;
+    }
+    if (gizmoAxisZ_.selected_ != gizmoAxisZ_.lastSelected_)
+    {
+        gizmo_->SetMaterial(2, cache->GetResource<Material>(
+                                gizmoAxisZ_.selected_ ?
+                                    "AtomicEditor/Materials/BrightBlueUnlit.xml" : "AtomicEditor/Materials/BlueUnlit.xml"));
+
+        gizmoAxisZ_.lastSelected_ = gizmoAxisZ_.selected_;
+    }
+
+    if (drag)
+        Drag();
+
+}
+
+bool Gizmo3D::MoveEditNodes(Vector3 adjust)
+{
+    bool moved = false;
+
+    if (adjust.Length() > M_EPSILON)
+    {
+        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        {
+            /*
+            if (moveSnap)
+            {
+                float moveStepScaled = moveStep * snapScale;
+                adjust.x = Floor(adjust.x / moveStepScaled + 0.5) * moveStepScaled;
+                adjust.y = Floor(adjust.y / moveStepScaled + 0.5) * moveStepScaled;
+                adjust.z = Floor(adjust.z / moveStepScaled + 0.5) * moveStepScaled;
+            }
+            */
+
+            Node* node = editNodes_->At(i);
+            Vector3 nodeAdjust = adjust;
+            if (axisMode_ == AXIS_LOCAL && editNodes_->Size() == 1)
+                nodeAdjust = node->GetWorldRotation() * nodeAdjust;
+
+            Vector3 worldPos = node->GetWorldPosition();
+            Vector3 oldPos = node->GetPosition();
+
+            worldPos += nodeAdjust;
+
+            if (!node->GetParent())
+                node->SetPosition(worldPos);
+            else
+                node->SetPosition(node->GetParent()->WorldToLocal(worldPos));
+
+            if (node->GetPosition() != oldPos)
+                moved = true;
+        }
+    }
+
+    return moved;
+
+
+}
+
+bool Gizmo3D::RotateEditNodes(Vector3 adjust)
+{
+    bool moved = false;
+
+    /*
+    if (rotateSnap)
+    {
+        float rotateStepScaled = rotateStep * snapScale;
+        adjust.x = Floor(adjust.x / rotateStepScaled + 0.5) * rotateStepScaled;
+        adjust.y = Floor(adjust.y / rotateStepScaled + 0.5) * rotateStepScaled;
+        adjust.z = Floor(adjust.z / rotateStepScaled + 0.5) * rotateStepScaled;
+    }
+    */
+
+    if (adjust.Length() > M_EPSILON)
+    {
+        moved = true;
+
+        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        {
+            Node* node = editNodes_->At(i);
+            Quaternion rotQuat(adjust.x_, adjust.y_, adjust.z_);
+            if (axisMode_ == AXIS_LOCAL && editNodes_->Size() == 1)
+                node->SetRotation(node->GetRotation() * rotQuat);
+            else
+            {
+                Vector3 offset = node->GetWorldPosition() - gizmoAxisX_.axisRay_.origin_;
+                if (node->GetParent() && node->GetParent()->GetWorldRotation() != Quaternion(1, 0, 0, 0))
+                    rotQuat = node->GetParent()->GetWorldRotation().Inverse() * rotQuat * node->GetParent()->GetWorldRotation();
+                node->SetRotation(rotQuat * node->GetRotation());
+                Vector3 newPosition = gizmoAxisX_.axisRay_.origin_ + rotQuat * offset;
+                if (node->GetParent())
+                    newPosition = node->GetParent()->WorldToLocal(newPosition);
+                node->SetPosition(newPosition);
+            }
+        }
+    }
+
+    return moved;
+}
+
+bool Gizmo3D::ScaleEditNodes(Vector3 adjust)
+{
+    bool moved = false;
+
+    if (adjust.Length() > M_EPSILON)
+    {
+        for (unsigned i = 0; i < editNodes_->Size(); ++i)
+        {
+            Node* node = editNodes_->At(i);
+
+            Vector3 scale = node->GetScale();
+            Vector3 oldScale = scale;
+
+            if (true)//!scaleSnap)
+                scale += adjust;
+            else
+            {
+                /*
+                float scaleStepScaled = scaleStep * snapScale;
+                if (adjust.x != 0)
+                {
+                    scale.x += adjust.x * scaleStepScaled;
+                    scale.x = Floor(scale.x / scaleStepScaled + 0.5) * scaleStepScaled;
+                }
+                if (adjust.y != 0)
+                {
+                    scale.y += adjust.y * scaleStepScaled;
+                    scale.y = Floor(scale.y / scaleStepScaled + 0.5) * scaleStepScaled;
+                }
+                if (adjust.z != 0)
+                {
+                    scale.z += adjust.z * scaleStepScaled;
+                    scale.z = Floor(scale.z / scaleStepScaled + 0.5) * scaleStepScaled;
+                }
+                */
+            }
+
+            if (scale != oldScale)
+                moved = true;
+
+            node->SetScale(scale);
+        }
+    }
+
+    return moved;
+}
+
+void Gizmo3D::Moved()
+{
+    gizmoAxisX_.Moved();
+    gizmoAxisY_.Moved();
+    gizmoAxisZ_.Moved();
+}
+
+
+void Gizmo3D::Drag()
+{
+    bool moved = false;
+
+    float scale = gizmoNode_->GetScale().x_;
+
+    if (editMode_ == EDIT_MOVE)
+    {
+        Vector3 adjust(0, 0, 0);
+        if (gizmoAxisX_.selected_)
+            adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
+        if (gizmoAxisY_.selected_)
+            adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
+        if (gizmoAxisZ_.selected_)
+            adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
+
+        moved = MoveEditNodes(adjust);
+    }
+    else if (editMode_ == EDIT_ROTATE)
+    {
+        const float rotSensitivity = 50.0;
+
+        Vector3 adjust(0, 0, 0);
+        if (gizmoAxisX_.selected_)
+            adjust.x_ = (gizmoAxisX_.d_ - gizmoAxisX_.lastD_) * rotSensitivity / scale;
+        if (gizmoAxisY_.selected_)
+            adjust.y_ = -(gizmoAxisY_.d_ - gizmoAxisY_.lastD_) * rotSensitivity / scale;
+        if (gizmoAxisZ_.selected_)
+            adjust.z_ = (gizmoAxisZ_.d_ - gizmoAxisZ_.lastD_) * rotSensitivity / scale;
+
+        moved = RotateEditNodes(adjust);
+    }
+    else if (editMode_ == EDIT_SCALE)
+    {
+        Vector3 adjust(0, 0, 0);
+        if (gizmoAxisX_.selected_)
+            adjust += Vector3(1, 0, 0) * (gizmoAxisX_.t_ - gizmoAxisX_.lastT_);
+        if (gizmoAxisY_.selected_)
+            adjust += Vector3(0, 1, 0) * (gizmoAxisY_.t_ - gizmoAxisY_.lastT_);
+        if (gizmoAxisZ_.selected_)
+            adjust += Vector3(0, 0, 1) * (gizmoAxisZ_.t_ - gizmoAxisZ_.lastT_);
+
+        // Special handling for uniform scale: use the unmodified X-axis movement only
+        if (editMode_ == EDIT_SCALE && gizmoAxisX_.selected_ && gizmoAxisY_.selected_ && gizmoAxisZ_.selected_)
+        {
+            float x = gizmoAxisX_.t_ - gizmoAxisX_.lastT_;
+            adjust = Vector3(x, x, x);
+        }
+
+        moved = ScaleEditNodes(adjust);
+    }
+
+    if (moved)
+    {
+        Moved();
+        //UpdateNodeAttributes();
+        //needGizmoUndo = true;
+    }
+
+}
+
+void Gizmo3D::SetEditMode(EditMode mode)
+{
+    editMode_ = mode;
+}
+
+void Gizmo3D::Hide()
+{
+    gizmo_->SetEnabled(false);
+}
+
+void Gizmo3D::Show()
+{
+    if (scene_.Null())
+        return;
+
+    gizmo_->SetEnabled(true);
+
+    Octree* octree = scene_->GetComponent<Octree>();
+    if (!octree)
+        return;
+
+    octree->AddManualDrawable(gizmo_);
+
+}
+
+}

+ 155 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/Gizmo3D.h

@@ -0,0 +1,155 @@
+
+// Portions Copyright (c) 2008-2015 the Urho3D project.
+
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+#include <Atomic/Math/MathDefs.h>
+#include <Atomic/Math/Ray.h>
+#include <Atomic/Scene/Scene.h>
+
+#include <Atomic/Atomic3D/StaticModel.h>
+#include <Atomic/Graphics/Camera.h>
+
+
+#include "SceneView3D.h"
+
+using namespace Atomic;
+
+namespace Atomic
+{
+    class Node;
+}
+
+namespace AtomicEditor
+{
+
+class Gizmo3DAxis
+{
+public:
+
+    Ray axisRay_;
+    bool selected_;
+    bool lastSelected_;
+    float t_;
+    float d_;
+    float lastT_;
+    float lastD_;
+
+    Gizmo3DAxis()
+    {
+        selected_ = false;
+        lastSelected_ = false;
+        t_ = 0.0;
+        d_ = 0.0;
+        lastT_ = 0.0;
+        lastD_ = 0.0;
+    }
+
+    void Update(Ray cameraRay, float scale, bool drag, Node* cameraNode)
+    {
+        const float axisMaxD = 0.1f;
+        const float axisMaxT = 1.0f;
+
+        Vector3 closest = cameraRay.ClosestPoint(axisRay_);
+        Vector3 projected = axisRay_.Project(closest);
+        d_ = axisRay_.Distance(closest);
+        t_ = (projected - axisRay_.origin_).DotProduct(axisRay_.direction_);
+
+        // Determine the sign of d from a plane that goes through the camera position to the axis
+        Plane axisPlane(cameraNode->GetPosition(), axisRay_.origin_, axisRay_.origin_ + axisRay_.direction_);
+        if (axisPlane.Distance(closest) < 0.0)
+            d_ = -d_;
+
+        // Update selected status only when not dragging
+        if (!drag)
+        {
+            selected_ = Abs(d_) < axisMaxD * scale && t_ >= -axisMaxD * scale && t_ <= axisMaxT * scale;
+            lastT_ = t_;
+            lastD_ = d_;
+        }
+    }
+
+    void Moved()
+    {
+        lastT_ = t_;
+        lastD_ = d_;
+    }
+};
+
+enum EditMode
+{
+    EDIT_SELECT,
+    EDIT_MOVE,
+    EDIT_ROTATE,
+    EDIT_SCALE
+};
+
+enum AxisMode
+{
+    AXIS_WORLD = 0,
+    AXIS_LOCAL
+};
+
+class Gizmo3D: public Object
+{
+    OBJECT(Gizmo3D);
+
+public:
+
+    Gizmo3D(Context* context);
+    virtual ~Gizmo3D();
+
+    void SetView(SceneView3D* view3D);
+
+    void SetEditMode(EditMode);
+
+    bool Selected()
+    {
+        return gizmoAxisX_.selected_ || gizmoAxisY_.selected_ || gizmoAxisZ_.selected_;
+    }
+
+    void Show();
+    void Hide();
+    void Update(Vector<Node*>& editNodes);
+
+    Node* GetGizmoNode() { return gizmoNode_; }
+
+private:
+
+    void Position();
+    void Use();
+    void Drag();
+    void Moved();
+    void CalculateGizmoAxes();
+
+    bool MoveEditNodes(Vector3 adjust);
+    bool RotateEditNodes(Vector3 adjust);
+    bool ScaleEditNodes(Vector3 adjust);
+
+    SharedPtr<Node> gizmoNode_;
+
+    WeakPtr<SceneView3D> view3D_;
+    WeakPtr<Scene> scene_;
+    WeakPtr<Camera> camera_;
+    WeakPtr<StaticModel> gizmo_;
+
+    Gizmo3DAxis gizmoAxisX_;
+    Gizmo3DAxis gizmoAxisY_;
+    Gizmo3DAxis gizmoAxisZ_;
+
+    EditMode editMode_;
+    EditMode lastEditMode_;
+
+    AxisMode axisMode_;
+
+    Vector<Node *> *editNodes_;
+
+};
+
+}
+

+ 183 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.cpp

@@ -0,0 +1,183 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/Log.h>
+#include <Atomic/Core/CoreEvents.h>
+#include <Atomic/Scene/Scene.h>
+#include <Atomic/Graphics/Camera.h>
+
+#include <Atomic/Graphics/DebugRenderer.h>
+#include <Atomic/Graphics/Viewport.h>
+#include <Atomic/Graphics/Octree.h>
+
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include <Atomic/Physics/PhysicsWorld.h>
+
+#include "AEEditor.h"
+#include "AEEvents.h"
+
+#include <Atomic/Input/Input.h>
+#include <Atomic/UI/UI.h>
+
+#include "SceneEditor3D.h"
+
+namespace AtomicEditor
+{
+
+SceneEditor3D ::SceneEditor3D(Context* context, const String &fullpath, UITabContainer *container) :
+    ResourceEditor(context, fullpath, container)
+{
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    scene_ = new Scene(context_);
+    SharedPtr<File> xmlFile = cache->GetFile(fullpath);
+
+    if (GetExtension(fullpath) == ".scene")
+        scene_->LoadXML(*xmlFile);
+    else
+        scene_->Load(*xmlFile);
+
+    scene_->SetUpdateEnabled(false);
+
+    sceneView_ = new SceneView3D(context_, this);
+
+    // EARLY ACCESS
+    if (fullpath.Find(String("ToonTown")) != String::NPOS)
+    {
+          sceneView_->GetCameraNode()->SetWorldPosition(Vector3(-119.073, 76.1121, 16.47763));
+          Quaternion q(0.55, 0.14,  0.8, -0.2);
+          sceneView_->SetYaw(q.YawAngle());
+          sceneView_->SetPitch(q.PitchAngle());
+          sceneView_->GetCameraNode()->SetWorldRotation(q);
+    }
+    else
+    {
+        Node* playerSpawn = scene_->GetChild("PlayerInfoStart", true);
+        if (playerSpawn)
+        {
+            sceneView_->GetCameraNode()->SetPosition(playerSpawn->GetPosition());
+            sceneView_->SetYaw(playerSpawn->GetRotation().EulerAngles().y_);
+        }
+
+    }
+
+    sceneView_->SetGravity(WIDGET_GRAVITY_ALL);
+
+    rootContentWidget_->AddChild(sceneView_);
+
+    gizmo3D_ = new Gizmo3D(context_);
+    gizmo3D_->SetView(sceneView_);
+    gizmo3D_->Show();
+
+    SubscribeToEvent(E_UPDATE, HANDLER(SceneEditor3D, HandleUpdate));
+    SubscribeToEvent(E_EDITORACTIVENODECHANGE, HANDLER(SceneEditor3D, HandleEditorActiveNodeChange));
+
+    // FIXME: Set the size at the end of setup, so all children are updated accordingly
+    // future size changes will be handled automatically
+    IntRect rect = container_->GetContentRoot()->GetRect();
+    rootContentWidget_->SetSize(rect.Width(), rect.Height());
+
+    // TODO: generate this event properly
+    VariantMap eventData;
+    eventData[EditorActiveSceneChange::P_SCENE] = scene_;
+    SendEvent(E_EDITORACTIVESCENECHANGE, eventData);
+
+    SubscribeToEvent(E_EDITORPLAYSTARTED, HANDLER(SceneEditor3D, HandlePlayStarted));
+    SubscribeToEvent(E_EDITORPLAYSTOPPED, HANDLER(SceneEditor3D, HandlePlayStopped));
+
+}
+
+SceneEditor3D::~SceneEditor3D()
+{
+
+}
+
+bool SceneEditor3D::OnEvent(const TBWidgetEvent &ev)
+{
+
+    if (ev.type == EVENT_TYPE_SHORTCUT)
+    {
+        if (ev.ref_id == TBIDC("save"))
+        {
+            File file(context_);
+            if (file.Open(fullpath_, FILE_WRITE))
+            {
+                scene_->SaveXML(file);
+                file.Close();
+            }
+
+            return true;
+
+        }
+    }
+
+    if (ev.type == EVENT_TYPE_CLICK)
+    {
+        SetFocus();
+
+        if (ev.target)
+        {
+            if (ev.target->GetID() == TBIDC("3d_translate"))
+            {
+                gizmo3D_->SetEditMode(EDIT_MOVE);
+                return false;
+            }
+            else if (ev.target->GetID() == TBIDC("3d_rotate"))
+            {
+                gizmo3D_->SetEditMode(EDIT_ROTATE);
+                return false;
+            }
+            else if (ev.target->GetID() == TBIDC("3d_scale"))
+            {
+                gizmo3D_->SetEditMode(EDIT_SCALE);
+                return false;
+            }
+        }
+    }
+
+    return false;
+}
+
+void SceneEditor3D::SetFocus()
+{
+    sceneView_->SetFocus();
+}
+
+void SceneEditor3D::SelectNode(Node* node)
+{
+    selectedNode_ = node;
+}
+
+void SceneEditor3D::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{    
+    Vector<Node*> editNodes;
+    if (selectedNode_.NotNull())
+        editNodes.Push(selectedNode_);
+    gizmo3D_->Update(editNodes);
+}
+
+void SceneEditor3D::HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData)
+{
+    Node* node = (Node*) (eventData[EditorActiveNodeChange::P_NODE].GetPtr());
+    SelectNode(node);
+}
+
+void SceneEditor3D::HandlePlayStarted(StringHash eventType, VariantMap& eventData)
+{
+    sceneView_->Disable();
+
+}
+
+void SceneEditor3D::HandlePlayStopped(StringHash eventType, VariantMap& eventData)
+{
+    sceneView_->Enable();
+}
+
+
+
+
+}

+ 70 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneEditor3D.h

@@ -0,0 +1,70 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <TurboBadger/tb_widgets_common.h>
+
+#include "../ResourceEditor.h"
+#include "SceneView3D.h"
+#include "Gizmo3D.h"
+
+using namespace Atomic;
+using namespace tb;
+
+namespace Atomic
+{
+class Scene;
+class Node;
+class View3D;
+class Camera;
+class DebugRenderer;
+class Octree;
+
+}
+
+namespace AtomicEditor
+{
+
+class SceneEditor3D: public ResourceEditor
+{
+    OBJECT(SceneEditor3D);
+
+public:
+
+    SceneEditor3D(Context* context, const String& fullpath, UITabContainer* container);
+
+    virtual ~SceneEditor3D();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    void SelectNode(Node* node);
+
+    Scene* GetScene() { return scene_; }
+    Gizmo3D* GetGizmo() { return gizmo3D_; }
+
+    void SetFocus();
+
+    virtual bool RequiresInspector() { return true; }
+
+
+private:
+
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
+    void HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData);
+    void HandlePlayStarted(StringHash eventType, VariantMap& eventData);
+    void HandlePlayStopped(StringHash eventType, VariantMap& eventData);
+
+    SharedPtr<Scene> scene_;
+
+    // TODO: multiple views
+    SharedPtr<SceneView3D> sceneView_;
+
+    SharedPtr<Gizmo3D> gizmo3D_;
+
+    WeakPtr<Node> selectedNode_;
+
+};
+
+}

+ 348 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneView3D.cpp

@@ -0,0 +1,348 @@
+// Portions Copyright (c) 2008-2015 the Urho3D project.
+
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/Log.h>
+#include <Atomic/Core/CoreEvents.h>
+#include <Atomic/Scene/Scene.h>
+#include <Atomic/Graphics/Camera.h>
+
+#include <Atomic/Graphics/Graphics.h>
+#include <Atomic/Graphics/DebugRenderer.h>
+#include <Atomic/Graphics/Viewport.h>
+#include <Atomic/Graphics/Octree.h>
+#include <Atomic/Atomic3D/Terrain.h>
+
+#include <Atomic/Input/Input.h>
+
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include <Atomic/Physics/PhysicsWorld.h>
+
+#include "AEEditor.h"
+#include "AEEvents.h"
+
+#include "SceneView3D.h"
+#include "SceneEditor3D.h"
+#include <Atomic/UI/UI.h>
+
+namespace AtomicEditor
+{
+
+SceneView3D ::SceneView3D(Context* context, SceneEditor3D *sceneEditor) :
+    UISceneView(context),
+    yaw_(0.0f),
+    pitch_(0.0f),
+    mouseLeftDown_(false),
+    mouseMoved_(false),
+    enabled_(true)
+{
+
+    sceneEditor_ = sceneEditor;
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    scene_ = sceneEditor->GetScene();
+
+    debugRenderer_ = scene_->GetComponent<DebugRenderer>();
+
+    if (debugRenderer_.Null())
+    {
+        debugRenderer_ = scene_->CreateComponent<DebugRenderer>();
+    }
+
+    octree_ = scene_->GetComponent<Octree>();
+
+    if (octree_.Null())
+    {
+        LOGWARNING("Scene without an octree loaded");
+        octree_ = scene_->CreateComponent<Octree>();
+    }
+
+    cameraNode_ = scene_->CreateChild("Camera");
+    cameraNode_->SetTemporary(true);
+    camera_ = cameraNode_->CreateComponent<Camera>();
+
+    debugRenderer_ = scene_->GetComponent<DebugRenderer>();
+    assert(debugRenderer_.NotNull());
+    octree_ = scene_->GetComponent<Octree>();
+    assert(octree_.NotNull());
+
+    cameraNode_->SetPosition(Vector3(0, 0, -10));
+
+    SetView(scene_, camera_);
+    SetAutoUpdate(false);
+
+    SubscribeToEvent(E_UPDATE, HANDLER(SceneView3D, HandleUpdate));
+    SubscribeToEvent(E_EDITORACTIVENODECHANGE, HANDLER(SceneView3D, HandleEditorActiveNodeChange));
+    SubscribeToEvent(E_POSTRENDERUPDATE, HANDLER(SceneView3D, HandlePostRenderUpdate));
+
+    // TODO: generate this event properly
+    VariantMap eventData;
+    eventData[EditorActiveSceneChange::P_SCENE] = scene_;
+    SendEvent(E_EDITORACTIVESCENECHANGE, eventData);
+
+    SetIsFocusable(true);
+
+
+}
+
+SceneView3D::~SceneView3D()
+{
+
+}
+
+void SceneView3D::Enable()
+{
+    if (enabled_)
+        return;
+
+    enabled_ = true;
+
+    SetVisibility(WIDGET_VISIBILITY_VISIBLE);
+}
+
+void SceneView3D::Disable()
+{
+    if (!enabled_)
+        return;
+
+    enabled_ = false;
+
+    SetVisibility(WIDGET_VISIBILITY_INVISIBLE);
+
+}
+
+void SceneView3D::MoveCamera(float timeStep)
+{
+    if (!enabled_)
+        return;
+
+    Input* input = GetSubsystem<Input>();
+
+    // Movement speed as world units per second
+    float MOVE_SPEED = 20.0f;
+    // Mouse sensitivity as degrees per pixel
+    const float MOUSE_SENSITIVITY = 0.2f;
+
+    if (input->GetKeyDown(KEY_LSHIFT) || input->GetKeyDown(KEY_RSHIFT))
+        MOVE_SPEED *= 3.0f;
+
+    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
+    if (input->GetMouseButtonDown(MOUSEB_RIGHT))
+    {
+        IntVector2 mouseMove = input->GetMouseMove();
+        yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
+        pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
+        pitch_ = Clamp(pitch_, -90.0f, 90.0f);
+        // Not working on OSX
+        //input->SetMouseMode(MM_RELATIVE);
+    }
+    else
+    {
+        // Not working on OSX
+        /*
+        if (input->GetMouseMode() != MM_ABSOLUTE)
+            input->SetMouseMode(MM_ABSOLUTE);
+        */
+    }
+
+
+    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+    cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
+
+    //Vector3 pos = cameraNode_->GetWorldPosition();
+    //Quaternion q = cameraNode_->GetWorldRotation();
+    //LOGINFOF("%f %f %f : %f %f %f %f", pos.x_, pos.y_, pos.z_, q.x_, q.y_, q.z_, q.w_ );
+
+    // Read WASD keys and move the camera scene node to the corresponding direction if they are pressed
+    // Use the Translate() function (default local space) to move relative to the node's orientation.
+    if (input->GetKeyDown('W'))
+        cameraNode_->Translate(Vector3::FORWARD * MOVE_SPEED * timeStep);
+    if (input->GetKeyDown('S'))
+        cameraNode_->Translate(Vector3::BACK * MOVE_SPEED * timeStep);
+    if (input->GetKeyDown('A'))
+        cameraNode_->Translate(Vector3::LEFT * MOVE_SPEED * timeStep);
+    if (input->GetKeyDown('D'))
+        cameraNode_->Translate(Vector3::RIGHT * MOVE_SPEED * timeStep);
+}
+
+Ray SceneView3D::GetCameraRay()
+{
+    Ray camRay;
+
+    Input* input = GetSubsystem<Input>();
+    IntVector2 cpos = input->GetMousePosition();
+
+    IntRect rect = GetRect();
+
+    if (!rect.Width() || !rect.Height())
+        return camRay;
+
+    int x = rect.left_;
+    int y = rect.top_;
+    GetInternalWidget()->ConvertToRoot(x, y);
+
+    return  camera_->GetScreenRay(float(cpos.x_ - x) / rect.Width(),
+                                       float(cpos.y_ - y) / rect.Height());
+}
+
+void SceneView3D::DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode)
+{
+    if (drawNode)
+        debug->AddNode(node, 1.0, false);
+
+    // Exception for the scene to avoid bringing the editor to its knees: drawing either the whole hierarchy or the subsystem-
+    // components can have a large performance hit. Also do not draw terrain child nodes due to their large amount
+    // (TerrainPatch component itself draws nothing as debug geometry)
+    if (node != scene_ && !node->GetComponent<Terrain>())
+    {
+        const Vector<SharedPtr<Component> >& components = node->GetComponents();
+
+        for (unsigned j = 0; j < components.Size(); ++j)
+            components[j]->DrawDebugGeometry(debug, false);
+
+        // To avoid cluttering the view, do not draw the node axes for child nodes
+        for (unsigned k = 0; k < node->GetNumChildren(); ++k)
+            DrawNodeDebug(node->GetChild(k), debug, false);
+    }
+}
+
+bool SceneView3D::MouseInView()
+{
+    Input* input = GetSubsystem<Input>();
+    IntVector2 pos = input->GetMousePosition();
+
+    IntRect rect = GetRect();
+
+    GetInternalWidget()->ConvertToRoot(rect.left_, rect.top_);
+    GetInternalWidget()->ConvertToRoot(rect.right_, rect.bottom_);
+
+    return rect.IsInside(pos);
+
+}
+
+
+void SceneView3D::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData)
+{
+
+    // Visualize the currently selected nodes
+    if (selectedNode_.NotNull())
+    {
+        DrawNodeDebug(selectedNode_, debugRenderer_);
+
+    }
+
+    if (!MouseInView())
+        return;
+
+    Input* input = GetSubsystem<Input>();
+
+    mouseLeftDown_ = false;
+
+    if (input->GetMouseButtonPress(MOUSEB_LEFT))
+    {
+        if (!mouseMoved_ && !sceneEditor_->GetGizmo()->Selected())
+        {
+            Ray camRay  = GetCameraRay();
+            PODVector<RayQueryResult> result;
+
+            RayOctreeQuery query(result, camRay, RAY_TRIANGLE, camera_->GetFarClip(), DRAWABLE_GEOMETRY, 0x7fffffff);
+            octree_->RaycastSingle(query);
+
+            if (query.result_.Size())
+            {
+                const RayQueryResult& r = result[0];
+
+                if (r.drawable_)
+                {
+
+                    VariantMap neventData;
+                    neventData[EditorActiveNodeChange::P_NODE] = r.drawable_->GetNode();
+                    SendEvent(E_EDITORACTIVENODECHANGE, neventData);
+
+                }
+            }
+        }
+
+        mouseMoved_ = false;
+
+    }
+    else if (!input->GetMouseButtonDown(MOUSEB_LEFT))
+    {
+
+        Ray camRay  = GetCameraRay();
+        PODVector<RayQueryResult> result;
+
+        mouseMoved_ = false;
+
+        /*
+        Array<int> pickModeDrawableFlags = {
+            DRAWABLE_GEOMETRY,
+            DRAWABLE_LIGHT,
+            DRAWABLE_ZONE
+        };
+        */
+
+        RayOctreeQuery query(result, camRay, RAY_TRIANGLE, camera_->GetFarClip(), DRAWABLE_GEOMETRY, 0x7fffffff);
+        octree_->RaycastSingle(query);
+
+        if (query.result_.Size())
+        {
+            const RayQueryResult& r = result[0];
+
+            if (r.drawable_)
+            {
+                debugRenderer_->AddNode(r.drawable_->GetNode(), 1.0, false);
+                r.drawable_->DrawDebugGeometry(debugRenderer_, false);
+            }
+
+        }
+    }
+    else
+    {
+        mouseLeftDown_ = true;
+        if (Abs(input->GetMouseMoveX() > 3 || input->GetMouseMoveY() >  3))
+        {
+            mouseMoved_ = true;
+        }
+    }
+
+}
+
+void SceneView3D::SelectNode(Node* node)
+{
+    selectedNode_ = node;
+}
+
+bool SceneView3D::OnEvent(const TBWidgetEvent &ev)
+{
+    return sceneEditor_->OnEvent(ev);
+}
+
+
+void SceneView3D::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+
+    // Timestep parameter is same no matter what event is being listened to
+    float timeStep = eventData[Update::P_TIMESTEP].GetFloat();
+
+    if (MouseInView())
+        MoveCamera(timeStep);
+
+    QueueUpdate();
+}
+
+void SceneView3D::HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData)
+{
+    Node* node = (Node*) (eventData[EditorActiveNodeChange::P_NODE].GetPtr());
+    SelectNode(node);
+}
+
+
+
+}

+ 79 - 0
Source/AtomicEditorWork/Editors/SceneEditor3D/SceneView3D.h

@@ -0,0 +1,79 @@
+// Portions Copyright (c) 2008-2015 the Urho3D project.
+
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+#include <Atomic/UI/UISceneView.h>
+
+using namespace Atomic;
+
+namespace Atomic
+{
+class Scene;
+class Node;
+class Camera;
+class DebugRenderer;
+class Octree;
+}
+
+namespace AtomicEditor
+{
+
+class SceneEditor3D;
+
+class SceneView3D: public UISceneView
+{
+    OBJECT(SceneView3D);
+
+public:
+
+    SceneView3D(Context* context, SceneEditor3D* sceneEditor);
+    virtual ~SceneView3D();
+    void SelectNode(Node* node);
+
+    Ray GetCameraRay();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    void SetPitch(float pitch) { pitch_ = pitch; }
+    void SetYaw(float yaw) { yaw_ = yaw; }
+
+    void Enable();
+    void Disable();
+    bool IsEnabled() { return enabled_; }
+
+private:
+
+    bool MouseInView();
+
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
+    void HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData);
+    void HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData);
+
+    void DrawNodeDebug(Node* node, DebugRenderer* debug, bool drawNode = true);
+
+    void MoveCamera(float timeStep);
+
+    WeakPtr<SceneEditor3D> sceneEditor_;
+
+    float yaw_;
+    float pitch_;
+
+    bool mouseLeftDown_;
+    bool mouseMoved_;
+
+    bool enabled_;
+
+    SharedPtr<Camera> camera_;
+    SharedPtr<DebugRenderer> debugRenderer_;
+    SharedPtr<Octree> octree_;
+    SharedPtr<Node> selectedNode_;
+
+};
+
+}

+ 364 - 0
Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.cpp

@@ -0,0 +1,364 @@
+
+#include <TurboBadger/tb_widgets_common.h>
+#include <TurboBadger/tb_menu_window.h>
+#include <TurboBadger/tb_inline_select.h>
+
+#include "AtomicEditor.h"
+
+#include <Atomic/Core/StringUtils.h>
+
+#include "UIInspectorDataBinding.h"
+
+namespace AtomicEditor
+{
+
+void InspectorDataBinding::SetObjectValueFromWidget(TBWidget *srcWidget)
+{
+    if (objectLocked_)
+        return;
+
+    objectLocked_ = true;
+
+    VariantType type = attrInfo_->type_;
+
+    if (type == VAR_BOOL)
+    {
+        TBCheckBox* box = (TBCheckBox *) widget_;
+        Variant v(box->GetValue() ? true : false);
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_INT)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(ToInt(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_FLOAT)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(ToFloat(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_STRING)
+    {
+        TBEditField* field = (TBEditField*) widget_;
+        Variant v(String(field->GetText().CStr()));
+        object_->SetAttribute(attrInfo_->name_, v);
+    }
+    else if (type == VAR_COLOR && srcWidget)
+    {
+        Color value = object_->GetAttribute(attrInfo_->name_).GetColor();
+
+        TBInlineSelect* select = NULL;
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.r_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.g_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.b_ = (float ) select->GetValueDouble();
+        }
+        else if (srcWidget->GetID() == TBID(unsigned(4)))
+        {
+            select = (TBInlineSelect*) srcWidget;
+            value.a_ = (float ) select->GetValueDouble();
+        }
+
+        object_->SetAttribute(attrInfo_->name_, value);
+    }
+    else if (type == VAR_VECTOR3 && srcWidget)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetVector3();
+
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+            value.x_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+            value.y_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+            value.z_ = srcWidget->GetValueDouble();
+
+        object_->SetAttribute(attrInfo_->name_, value);
+    }
+    else if (type == VAR_QUATERNION && srcWidget)
+    {
+        Quaternion q = object_->GetAttribute(attrInfo_->name_).GetQuaternion();
+
+        Vector3 value = q.EulerAngles();
+
+        if (srcWidget->GetID() == TBID(unsigned(1)))
+            value.x_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(2)))
+            value.y_ = srcWidget->GetValueDouble();
+        else if (srcWidget->GetID() == TBID(unsigned(3)))
+            value.z_ = srcWidget->GetValueDouble();
+
+        q.FromEulerAngles(value.x_, value.y_, value.z_);
+
+        object_->SetAttribute(attrInfo_->name_, q);
+    }
+
+    // refresh widget
+    //SetWidgetValueFromObject();
+
+    objectLocked_ = false;
+
+}
+
+void InspectorDataBinding::SetWidgetValueFromObject()
+{
+    if (widgetLocked_)
+        return;
+
+    widgetLocked_ = true;
+
+    if (attrInfo_->type_ == VAR_BOOL)
+    {
+        bool value = object_->GetAttribute(attrInfo_->name_).GetBool();
+        widget_->SetValue(value ? 1 : 0);
+            }
+    else if (attrInfo_->type_ == VAR_VECTOR3)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetVector3();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.x_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.y_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValueDouble(value.z_);
+    }
+    else if (attrInfo_->type_ == VAR_QUATERNION)
+    {
+        Vector3 value = object_->GetAttribute(attrInfo_->name_).GetQuaternion().EulerAngles();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.x_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.y_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValueDouble(value.z_);
+    }
+    else if (attrInfo_->type_ == VAR_STRING)
+    {
+        String value = object_->GetAttribute(attrInfo_->name_).GetString();
+        widget_->SetText(value.CString());
+    }
+    else if (attrInfo_->type_ == VAR_FLOAT)
+    {
+        float value = object_->GetAttribute(attrInfo_->name_).GetFloat();
+        widget_->SetText(ToString("%f", value).CString());
+    }
+    else if (attrInfo_->type_ == VAR_INT)
+    {
+        int value = object_->GetAttribute(attrInfo_->name_).GetInt();
+
+        if (attrInfo_->enumNames_)
+        {
+            widget_->SetText(attrInfo_->enumNames_[value]);
+        }
+        else
+        {
+            widget_->SetText(ToString("%i", value).CString());
+        }
+
+    }
+    else if (attrInfo_->type_ == VAR_COLOR)
+    {
+        Color value = object_->GetAttribute(attrInfo_->name_).GetColor();
+
+        TBInlineSelect* select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(1)));
+        if (select)
+            select->SetValueDouble(value.r_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(2)));
+        if (select)
+            select->SetValueDouble(value.g_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(3)));
+        if (select)
+            select->SetValue(value.b_);
+        select = widget_->GetWidgetByIDAndType<TBInlineSelect>(TBID(unsigned(4)));
+        if (select)
+            select->SetValueDouble(value.a_);
+
+    }
+
+
+    widgetLocked_ = false;
+
+}
+
+bool InspectorDataBinding::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_CLICK)
+    {
+        if (objectLocked_)
+            return false;
+
+        String id = attrInfo_->name_;
+        id += " enum popup";
+
+        if (ev.target->GetID() == TBIDC(id.CString()))
+        {
+            Variant value(int(ev.ref_id-1));
+            object_->SetAttribute(attrInfo_->name_, value);
+            SetWidgetValueFromObject();
+        }
+        else if (widget_ == ev.target && attrInfo_->enumNames_)
+        {
+            TBMenuWindow *menu = new TBMenuWindow(ev.target, TBIDC(id.CString()));
+            TBGenericStringItemSource* source = menu->GetList()->GetDefaultSource();
+            const char** enumPtr = attrInfo_->enumNames_;
+            unsigned v = 1;
+            while (*enumPtr)
+            {
+                source->AddItem(new TBGenericStringItem(*enumPtr, TBID(v++)));
+                enumPtr++;
+            }
+            menu->Show(source, TBPopupAlignment());
+
+        }
+        else if (widget_ == ev.target || widget_->IsAncestorOf(ev.target))
+        {
+            SetObjectValueFromWidget(ev.target);
+        }
+    }
+
+    if (ev.type == EVENT_TYPE_CHANGED)
+    {
+        if (objectLocked_)
+            return false;
+
+        if (widget_ == ev.target || widget_->IsAncestorOf(ev.target))
+        {
+            SetObjectValueFromWidget(ev.target);
+        }
+    }
+
+    return false;
+}
+
+InspectorDataBinding* InspectorDataBinding::Create(Serializable* object, const AttributeInfo* attrInfo)
+{
+
+    if (attrInfo->mode_ & AM_NOEDIT)
+        return NULL;
+
+    TBWidget* widget = NULL;
+    InspectorDataBinding* binding = NULL;
+
+    TBFontDescription fd;
+    fd.SetID(TBIDC("Vera"));
+    fd.SetSize(11);
+
+    if (attrInfo->type_ == VAR_BOOL)
+    {
+        TBCheckBox* box = new TBCheckBox();
+        box->SetSkinBg(TBIDC("TBGreyCheckBox"));
+        widget = box;
+    }
+    else if (attrInfo->type_ == VAR_STRING)
+    {
+        TBEditField* field = new TBEditField();
+        field->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        field->SetSkinBg(TBIDC("TBAttrEditorField"));
+        field->SetFontDescription(fd);
+        LayoutParams lp;
+        lp.SetWidth(140);
+        field->SetLayoutParams(lp);
+
+        widget = field;
+    }
+    else if (attrInfo->type_ == VAR_FLOAT)
+    {
+        TBEditField* field = new TBEditField();
+        field->SetTextAlign(TB_TEXT_ALIGN_CENTER);
+        field->SetSkinBg(TBIDC("TBAttrEditorField"));
+        field->SetFontDescription(fd);
+        widget = field;
+    }
+    else if (attrInfo->type_ == VAR_COLOR)
+    {
+        TBLayout* layout = new TBLayout();
+        widget = layout;
+        layout->SetSpacing(0);
+
+        LayoutParams lp;
+        lp.SetWidth(70);
+
+        for (unsigned i = 0; i < 4; i++)
+        {
+            TBInlineSelect* select = new TBInlineSelect();
+            select->SetID(TBID(i + 1));
+            select->SetFontDescription(fd);
+            select->SetLimits(-10000000, 10000000);
+            select->SetLayoutParams(lp);
+            layout->AddChild(select);
+        }
+
+    }
+    else if (attrInfo->type_ == VAR_INT)
+    {
+        if (attrInfo->enumNames_)
+        {
+            TBButton* field = new TBButton();
+            field->SetFontDescription(fd);
+            widget = field;
+        }
+        else
+        {
+            TBEditField* field = new TBEditField();
+            field->SetTextAlign(TB_TEXT_ALIGN_CENTER);
+            field->SetFontDescription(fd);
+            field->SetSkinBg(TBIDC("TBAttrEditorField"));
+            widget = field;
+        }
+    }
+    else if (attrInfo->type_ == VAR_VECTOR3 || attrInfo->type_ == VAR_QUATERNION)
+    {
+        TBLayout* layout = new TBLayout();
+        widget = layout;
+        layout->SetSpacing(0);
+
+        LayoutParams lp;
+        lp.SetWidth(100);
+
+        for (unsigned i = 0; i < 3; i++)
+        {
+            TBInlineSelect* select = new TBInlineSelect();
+            select->SetID(TBID(i + 1));
+            select->SetFontDescription(fd);
+            select->SetSkinBg(TBIDC("InspectorTextAttrName"));
+            select->SetLimits(-10000000, 10000000);
+            LayoutParams editlp;
+            editlp.min_w = 60;
+            select->SetEditFieldLayoutParams(editlp);
+            select->SetLayoutParams(lp);
+            layout->AddChild(select);
+        }
+
+    }
+
+    if (widget)
+    {
+        binding = new InspectorDataBinding();
+        binding->object_ = object;
+        binding->widget_ = widget;
+        binding->attrInfo_ = attrInfo;
+    }
+
+    return binding;
+}
+
+}

+ 47 - 0
Source/AtomicEditorWork/Inspector/UIInspectorDataBinding.h

@@ -0,0 +1,47 @@
+
+#pragma once
+
+#include <TurboBadger/tb_widgets.h>
+#include <Atomic/Scene/Scene.h>
+
+namespace tb
+{
+    class TBWidget;
+    class TBMenuWindow;
+    class TBInlineSelect;
+}
+
+using namespace Atomic;
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+class InspectorDataBinding
+{
+
+public:
+
+    static InspectorDataBinding* Create(Serializable* object, const AttributeInfo* attrInfo);
+
+    void SetWidgetValueFromObject();
+    void SetObjectValueFromWidget(TBWidget* srcWidget = NULL);
+
+    TBWidget* GetWidget() { return widget_; }
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+private:
+
+    InspectorDataBinding() : widget_(0), object_(0), attrInfo_(0), objectLocked_(false), widgetLocked_(false) {}
+
+    TBWidget* widget_;
+    Serializable* object_;
+    const AttributeInfo* attrInfo_;
+
+    bool objectLocked_;
+    bool widgetLocked_;
+
+};
+
+}

+ 319 - 0
Source/AtomicEditorWork/Inspector/UIInspectorFrame.cpp

@@ -0,0 +1,319 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include <TurboBadger/tb_editfield.h>
+#include <Atomic/Core/Context.h>
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+#include <Atomic/Resource/ResourceEvents.h>
+
+#include <Atomic/Scene/Component.h>
+
+#include <Atomic/UI/UI.h>
+
+#include "AEEditor.h"
+#include "AEEvents.h"
+
+#include "UIInspectorDataBinding.h"
+#include "UIInspectorFrame.h"
+
+
+#include "UI/Modal/UIModalOps.h"
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+InspectorFrame::InspectorFrame(Context* context) :
+    UIWidget(context, true)
+{
+    SetGravity(WIDGET_GRAVITY_TOP_BOTTOM);
+
+    InitializeSources();
+
+    SubscribeToEvent(E_EDITORACTIVENODECHANGE, HANDLER(InspectorFrame, HandleEditorActiveNodeChange));
+
+}
+
+InspectorFrame::~InspectorFrame()
+{
+
+}
+
+void InspectorFrame::InitializeSources()
+{
+    /*
+    componentCreateSource.AddItem(new MenubarItem("Audio", &audioCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Geometry", &geometryCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Logic", &logicCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Navigation", &navigationCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Network", &networkCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Physics", &physicsCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Scene", &sceneCreateSource));
+    componentCreateSource.AddItem(new MenubarItem("Subsystem", &subsystemCreateSource));
+
+    audioCreateSource.AddItem(new MenubarItem("SoundListener", TBIDC("create component")));
+    audioCreateSource.AddItem(new MenubarItem("SoundSource",  TBIDC("create component")));
+    audioCreateSource.AddItem(new MenubarItem("SoundSource3D", TBIDC("create component")));
+
+    geometryCreateSource.AddItem(new MenubarItem("AnimatedModel", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("BillboardSet",  TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("CustomGeometry", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("ParticleEmitter", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Skybox", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("StaticModel", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("StaticModelGroup", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Terrain", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Text3D", TBIDC("create component")));
+    geometryCreateSource.AddItem(new MenubarItem("Water", TBIDC("create component")));
+
+    logicCreateSource.AddItem(new MenubarItem("AnimationController", TBIDC("create component")));
+    logicCreateSource.AddItem(new MenubarItem("Javascript Component", TBIDC("create component")));
+    logicCreateSource.AddItem(new MenubarItem("SplinePath", TBIDC("create component")));
+
+    navigationCreateSource.AddItem(new MenubarItem("Navigable", TBIDC("create component")));
+    navigationCreateSource.AddItem(new MenubarItem("NavigationMesh", TBIDC("create component")));
+    navigationCreateSource.AddItem(new MenubarItem("OffMeshConnection", TBIDC("create component")));
+
+    networkCreateSource.AddItem(new MenubarItem("Network Priority", TBIDC("create component")));
+
+    physicsCreateSource.AddItem(new MenubarItem("CollisionShape", TBIDC("create component")));
+    physicsCreateSource.AddItem(new MenubarItem("Constraint", TBIDC("create component")));
+    physicsCreateSource.AddItem(new MenubarItem("RigidBody", TBIDC("create component")));
+
+    sceneCreateSource.AddItem(new MenubarItem("Camera", TBIDC("create component")));
+    sceneCreateSource.AddItem(new MenubarItem("Light", TBIDC("create component")));
+    sceneCreateSource.AddItem(new MenubarItem("Zone", TBIDC("create component")));
+
+    subsystemCreateSource.AddItem(new MenubarItem("DebugRenderer", TBIDC("create component")));
+    subsystemCreateSource.AddItem(new MenubarItem("Octree", TBIDC("create component")));
+    subsystemCreateSource.AddItem(new MenubarItem("PhysicsWorld", TBIDC("create component")));
+    */
+
+}
+
+bool InspectorFrame::OnEvent(const TBWidgetEvent &ev)
+{
+    for (unsigned i = 0; i < dataBindings_.Size(); i++)
+        dataBindings_[i]->OnEvent(ev);
+
+    return false;
+}
+
+
+void InspectorFrame::InspectNode(Node* node)
+{
+    if (node_ == node)
+        return;
+
+    node_ = node;
+
+    TBLayout* inspectorContainer = GetInternalWidget()->GetWidgetByIDAndType<TBLayout>(TBIDC("inspectorcontainer"));
+    assert(inspectorContainer);
+
+
+    inspectorContainer->DeleteAllChildren();
+
+    for (unsigned i = 0; i < dataBindings_.Size(); i++)
+        delete dataBindings_[i];
+
+    dataBindings_.Clear();
+
+    if (!node_)
+    {
+        return;
+    }
+    else
+    {
+        TBFontDescription fd;
+        fd.SetID(TBIDC("Vera"));
+        fd.SetSize(11);
+
+        LayoutParams nlp;
+        nlp.SetWidth(304);
+        TBLayout* nodeLayout = new TBLayout(AXIS_Y);
+        nodeLayout->SetSpacing(4);
+
+        nodeLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+        nodeLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+        nodeLayout->SetLayoutParams(nlp);
+
+        TBContainer* nodeContainer = new TBContainer();
+        nodeContainer->SetGravity(WIDGET_GRAVITY_ALL);
+
+        nodeContainer->SetSkinBg("InspectorTopLayout");
+
+        TBLayout* attrsVerticalLayout = new TBLayout(AXIS_Y);
+        attrsVerticalLayout->SetGravity(WIDGET_GRAVITY_ALL);
+        attrsVerticalLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+        nodeContainer->AddChild(attrsVerticalLayout);
+
+        TBTextField* nodeLabel = new TBTextField();
+        nodeLabel->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        nodeLabel->SetText("Node");
+        nodeLabel->SetSkinBg(TBIDC("InspectorTextLabel"));
+        attrsVerticalLayout->AddChild(nodeLabel);
+
+        const Vector<AttributeInfo>* attrs = node->GetAttributes();
+
+        for (unsigned i = 0; i < attrs->Size(); i++)
+        {
+            const AttributeInfo* attr = &attrs->At(i);
+
+            InspectorDataBinding*  binding = InspectorDataBinding::Create(node, attr);
+
+            if (binding)
+            {
+                dataBindings_.Push(binding);
+
+                TBLayout* attrLayout = new TBLayout();
+
+                if (attr->type_ == VAR_VECTOR3 || attr->type_ == VAR_COLOR || attr->type_ == VAR_QUATERNION)
+                {
+                    attrLayout->SetAxis(AXIS_Y);
+                    attrLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+                    attrLayout->SetSkinBg("InspectorVectorAttrLayout");
+                }
+
+                attrLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+                TBTextField* name = new TBTextField();
+                name->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+                name->SetSkinBg(TBIDC("InspectorTextAttrName"));
+
+                String bname = attr->name_;
+                if (bname == "Is Enabled")
+                    bname = "Enabled";
+
+                name->SetText(bname.CString());
+                name->SetFontDescription(fd);
+
+                attrLayout->AddChild(name);
+                TBWidget* bwidget = binding->GetWidget();
+                attrLayout->AddChild(bwidget);
+
+                attrsVerticalLayout->AddChild(attrLayout);
+            }
+
+        }
+
+        nodeLayout->AddChild(nodeContainer);
+
+        TBLayout* componentsLayout = new TBLayout();
+
+        TBTextField* componentsLabel = new TBTextField();
+        componentsLabel->SetText("Components");
+        componentsLabel->SetSkinBg(TBIDC("InspectorTextLabel"));
+        componentsLabel->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+        componentsLayout->AddChild(componentsLabel);
+
+        TBButton* create = new TBButton();
+        LayoutParams createLP;
+        createLP.SetHeight(20);
+        create->SetLayoutParams(createLP);
+        create->SetFontDescription(fd);
+        create->SetText("Create");
+        create->SetID(TBIDC("create button"));
+        componentsLayout->AddChild(create);
+
+        nodeLayout->AddChild(componentsLayout);
+
+        const Vector<SharedPtr<Component> > components = node->GetComponents();
+        for (unsigned i = 0; i < components.Size(); i++)
+        {
+            Component* c = components[i];
+
+            TBContainer* componentContainer = new TBContainer();
+
+            componentContainer->SetSkinBg("InspectorTopLayout");
+
+            componentContainer->SetGravity(WIDGET_GRAVITY_ALL);
+
+            TBLayout* attrsVerticalLayout = new TBLayout(AXIS_Y);
+            attrsVerticalLayout->SetGravity(WIDGET_GRAVITY_ALL);
+            attrsVerticalLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+            attrsVerticalLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+
+            TBTextField* cnameField = new TBTextField();
+            cnameField->SetText(c->GetTypeName().CString());
+            cnameField->SetSkinBg(TBIDC("InspectorTextLabel"));
+            cnameField->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+            cnameField->SetFontDescription(fd);
+            attrsVerticalLayout->AddChild(cnameField);
+
+            componentContainer->AddChild(attrsVerticalLayout);
+
+            const Vector<AttributeInfo>* attrs = c->GetAttributes();
+
+            if (attrs)
+                for (unsigned i = 0; i < attrs->Size(); i++)
+                {
+                    const AttributeInfo* attr = &attrs->At(i);
+
+                    InspectorDataBinding*  binding = InspectorDataBinding::Create(c, attr);
+
+                    if (binding)
+                    {
+                        dataBindings_.Push(binding);
+
+                        TBLayout* attrLayout = new TBLayout();
+
+                        attrLayout->SetLayoutDistribution(LAYOUT_DISTRIBUTION_GRAVITY);
+
+                        if (attr->type_ == VAR_VECTOR3 || attr->type_ == VAR_COLOR || attr->type_ == VAR_QUATERNION)
+                        {
+                            attrLayout->SetAxis(AXIS_Y);
+                            attrLayout->SetLayoutPosition(LAYOUT_POSITION_LEFT_TOP);
+                            attrLayout->SetSkinBg("InspectorVectorAttrLayout");
+                        }
+
+                        TBTextField* name = new TBTextField();
+                        String bname = attr->name_;
+                        if (bname == "Is Enabled")
+                            bname = "Enabled";
+
+                        name->SetTextAlign(TB_TEXT_ALIGN_LEFT);
+                        name->SetText(bname.CString());
+                        name->SetFontDescription(fd);
+
+                        name->SetSkinBg(TBIDC("InspectorTextAttrName"));
+
+                        LayoutParams lp;
+                        lp.SetWidth(140);
+                        name->SetLayoutParams(lp);
+
+                        attrLayout->AddChild(name);
+
+                        TBWidget* bwidget = binding->GetWidget();
+
+                        attrLayout->AddChild(bwidget);
+
+                        attrsVerticalLayout->AddChild(attrLayout);
+                    }
+
+                }
+
+            nodeLayout->AddChild(componentContainer);
+
+        }
+
+        inspectorContainer->AddChild(nodeLayout);
+
+        for (unsigned i = 0; i < dataBindings_.Size(); i++)
+            dataBindings_[i]->SetWidgetValueFromObject();
+    }
+
+}
+
+void InspectorFrame::HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData)
+{
+    Node* node = (Node*) (eventData[EditorActiveNodeChange::P_NODE].GetPtr());
+
+    InspectNode(node);
+}
+
+}

+ 67 - 0
Source/AtomicEditorWork/Inspector/UIInspectorFrame.h

@@ -0,0 +1,67 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/UI/UIWidget.h>
+#include <Atomic/UI/UIMenubar.h>
+
+#include <Atomic/Scene/Scene.h>
+
+using namespace Atomic;
+
+namespace tb
+{
+    class TBLayout;
+    class TBMenuWindow;
+    class TBInlineSelect;
+}
+
+namespace AtomicEditor
+{
+
+class ListView;
+class ListViewItem;
+class InspectorDataBinding;
+
+class InspectorFrame : public UIWidget
+{
+
+    OBJECT(InspectorFrame);
+
+public:
+    /// Construct.
+    InspectorFrame(Context* context);
+    /// Destruct.
+    virtual ~InspectorFrame();
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+private:
+
+    void InitializeSources();
+
+    void InspectNode(Node* node);
+
+    void HandleEditorActiveNodeChange(StringHash eventType, VariantMap& eventData);
+
+    Vector<InspectorDataBinding*> dataBindings_;
+
+    SharedPtr<Node> node_;
+
+    /*
+    UIMenuItemSource componentCreateSource;
+    UIMenuItemSource audioCreateSource;
+    UIMenuItemSource geometryCreateSource;
+    UIMenuItemSource logicCreateSource;
+    UIMenuItemSource navigationCreateSource;
+    UIMenuItemSource networkCreateSource;
+    UIMenuItemSource physicsCreateSource;
+    UIMenuItemSource sceneCreateSource;
+    UIMenuItemSource subsystemCreateSource;
+    */
+
+};
+
+}

+ 18 - 0
Source/AtomicEditorWork/JSTest/MyJSClass.cpp

@@ -0,0 +1,18 @@
+
+#include "MyJSClass.h"
+
+namespace AtomicEditor
+{
+
+MyJSClass::MyJSClass(Context* context) : Object(context),
+  aha_("aha!")
+{
+
+}
+
+MyJSClass::~MyJSClass()
+{
+
+}
+
+}

+ 29 - 0
Source/AtomicEditorWork/JSTest/MyJSClass.h

@@ -0,0 +1,29 @@
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace AtomicEditor
+{
+
+class MyJSClass : public Object
+{
+    OBJECT(MyJSClass);
+
+public:
+    /// Construct.
+    MyJSClass(Context* context);
+    virtual ~MyJSClass();
+
+    const String& GetAha() { return aha_; }
+
+private:
+
+    String aha_;
+
+
+};
+
+}

+ 26 - 0
Source/AtomicEditorWork/Javascript/AEEditorJS.cpp

@@ -0,0 +1,26 @@
+
+#include <AtomicJS/Javascript/JSVM.h>
+
+using namespace Atomic;
+
+namespace Atomic
+{
+    extern void jsb_package_editor_init(JSVM* vm);
+}
+
+namespace AtomicEditor
+{
+
+void jsapi_init_editor(JSVM* vm)
+{
+    duk_context* ctx = vm->GetJSContext();
+
+    duk_push_object(ctx);
+    duk_put_global_string(ctx, "Editor");
+
+    jsb_package_editor_init(vm);
+}
+
+}
+
+

+ 0 - 0
Source/AtomicEditorWork/Javascript/AEEditorJS.h


+ 961 - 0
Source/AtomicEditorWork/Javascript/JSAST.cpp

@@ -0,0 +1,961 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+
+#include <rapidjson/document.h>
+#include <rapidjson/stringbuffer.h>
+#include <rapidjson/prettywriter.h>
+
+#include <Atomic/IO/Log.h>
+#include "JSAST.h"
+#include "JSASTVisitor.h"
+
+using namespace rapidjson;
+
+namespace AtomicEditor
+{
+
+JSASTNode::JSASTNode(JSASTType type) : type_(type), rangeStart_(0), rangeEnd_(0)
+{
+
+}
+
+bool JSASTNode::Parse(const rapidjson::Value& value)
+{
+    assert(value.IsObject());
+
+    for (Value::ConstMemberIterator itr = value.MemberBegin();
+         itr != value.MemberEnd(); ++itr)
+    {
+        String name = itr->name.GetString();
+
+        if (name == "loc")
+        {
+            ParseLoc(itr->value);
+        }
+        else if (name == "type")
+        {
+            // TODO: Verify type
+        }
+        else if (name == "range")
+        {
+            ParseRange(itr->value);
+        }
+
+    }
+
+    return true;
+
+}
+
+bool JSASTNode::ParseLoc(const rapidjson::Value& value)
+{
+    // SpiderMonkey can have this for loc :/
+    if (value.IsNull())
+        return true;
+
+    assert(value.IsObject());
+
+    const Value::Member* mstart = value.FindMember("start");
+    assert(mstart);
+
+    const Value::Member* mend = value.FindMember("end");
+    assert(mend);
+
+    loc_.startLine_ = mstart->value["line"].GetInt();
+    loc_.startColumn_ = mstart->value["column"].GetInt();
+    loc_.endLine_ = mend->value["line"].GetInt();
+    loc_.endColumn_ = mend->value["column"].GetInt();
+
+    return true;
+}
+
+bool JSASTNode::ParseRange(const rapidjson::Value& value)
+{
+    assert(value.IsArray() && value.Size() == 2);
+
+    rangeStart_ = value[SizeType(0)].GetInt();
+    rangeEnd_ = value[SizeType(1)].GetInt();
+
+    return true;
+
+}
+
+JSASTStatement* JSASTNode::ParseStatement(const rapidjson::Value &value)
+{
+    assert(value.IsObject());
+
+    JSASTStatement* statement = NULL;
+
+    const Value::Member* mtype = value.FindMember("type");
+    assert(mtype);
+
+    String type = mtype->value.GetString();
+
+    if (type == "ExpressionStatement")
+    {
+        statement = new JSASTExpressionStatement();
+    }
+    else if (type == "VariableDeclaration")
+    {
+        statement = new JSASTVariableDeclaration();
+    }
+    else if (type == "ReturnStatement")
+    {
+        statement = new JSASTReturnStatement();
+    }
+    else if (type == "FunctionDeclaration")
+    {
+        statement = new JSASTFunctionDeclaration();
+    }
+    else if (type == "IfStatement")
+    {
+        statement = new JSASTIfStatement();
+    }
+    else if (type == "BlockStatement")
+    {
+        statement = new JSASTBlockStatement();
+    }
+    else if (type == "EmptyStatement")
+    {
+        statement = new JSASTBlockStatement();
+    }
+    else if (type == "ForStatement")
+    {
+        statement = new JSASTForStatement();
+    }
+    else if (type == "LabeledStatement")
+    {
+        statement = new JSASTLabeledStatement();
+    }
+
+    if (!statement)
+    {
+        LOGWARNINGF("Unknown JSASTStatement: %s", type.CString());
+    }
+    else
+    {
+        statement->Parse(value);
+    }
+
+    return statement;
+
+}
+
+JSASTExpression* JSASTNode::ParseExpression(const rapidjson::Value &value, JSASTType astType)
+{
+    if (!value.IsObject())
+        return NULL;
+
+    JSASTExpression* expr = NULL;
+
+    const Value::Member* mtype = value.FindMember("type");
+    assert(mtype);
+
+    String type = mtype->value.GetString();
+    if (type == "Identifier" && (astType == JSAST_UNDEFINED || astType == JSAST_IDENTIFIER))
+    {
+        expr = new JSASTIdentifier();
+    }
+    else if (type == "Literal" && (astType == JSAST_UNDEFINED || astType == JSAST_LITERAL))
+    {
+        expr = new JSASTLiteral();
+    }
+    else if (type == "UnaryExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_UNARYEXPRESSION))
+    {
+        expr = new JSASTUnaryExpression();
+    }
+    else if (type == "UpdateExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_UPDATEEXPRESSION))
+    {
+        expr = new JSASTUpdateExpression();
+    }
+    else if (type == "NewExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_NEWEXPRESSION))
+    {
+        expr = new JSASTNewExpression();
+    }
+    else if (type == "FunctionExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_FUNCTIONEXPRESSION))
+    {
+        expr = new JSASTFunctionExpression();
+    }
+    else if (type == "BinaryExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_BINARYEXPRESSION))
+    {
+        expr = new JSASTBinaryExpression();
+    }
+    else if (type == "AssignmentExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_ASSIGNMENTEXPRESSION))
+    {
+        expr = new JSASTAssignmentExpression();
+    }
+    else if (type == "LogicalExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_LOGICALEXPRESSION))
+    {
+        expr = new JSASTAssignmentExpression();
+    }
+    else if (type == "CallExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_CALLEXPRESSION))
+    {
+        expr = new JSASTCallExpression();
+    }
+    else if (type == "VariableDeclarator" && (astType == JSAST_UNDEFINED || astType == JSAST_VARIABLEDECLARATOR))
+    {
+        expr = new JSASTVariableDeclarator();
+    }
+    else if (type == "MemberExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_MEMBEREXPRESSION))
+    {
+        expr = new JSASTMemberExpression();
+    }
+    else if (type == "ArrayExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_ARRAYEXPRESSION))
+    {
+        expr = new JSASTArrayExpression();
+    }
+    else if (type == "ObjectExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_OBJECTEXPRESSION))
+    {
+        expr = new JSASTObjectExpression();
+    }
+    else if (type == "ConditionalExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_CONDITIONALEXPRESSION))
+    {
+        expr = new JSASTConditionalExpression();
+    }
+    else if (type == "ThisExpression" && (astType == JSAST_UNDEFINED || astType == JSAST_THISEXPRESSION))
+    {
+        expr = new JSASTThisExpression();
+    }
+
+    if (!expr)
+    {
+        LOGWARNINGF("Unknown JSASTExpression: %s", type.CString());
+    }
+    else
+    {
+        expr->Parse(value);
+    }
+
+    return expr;
+
+}
+
+bool JSASTNode::ParseExpressionArray(const rapidjson::Value &value, Vector<JSASTExpression *> &expressions)
+{
+
+    assert(value.IsArray());
+
+    for (Value::ConstValueIterator itr = value.Begin(); itr != value.End(); itr++)
+    {
+        JSASTExpression* expr = ParseExpression(*itr);
+        //assert(expr);
+        expressions.Push(expr);
+
+    }
+
+    return true;
+}
+
+
+bool JSASTNode::ParseStatementArray(const rapidjson::Value &value, Vector<JSASTStatement *> &statements)
+{
+
+    assert(value.IsArray());
+
+    for (Value::ConstValueIterator itr = value.Begin(); itr != value.End(); itr++)
+    {
+        JSASTStatement* stmt = ParseStatement(*itr);
+        //assert(stmt);
+        statements.Push(stmt);
+
+    }
+
+    return true;
+}
+
+bool JSASTThisExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    return true;
+}
+
+void JSASTThisExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTIdentifier::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    if (value.FindMember("name"))
+        name_ = value["name"].GetString();
+    return true;
+}
+
+void JSASTIdentifier::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTAssignmentExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTAssignmentExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTLogicalExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTLogicalExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTBinaryExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    operator_ = value["operator"].GetString();
+
+    leftExpression_ = ParseExpression(value["left"]);
+    rightExpression_ = ParseExpression(value["right"]);
+
+    return true;
+}
+
+void JSASTBinaryExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTLiteral::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    if (!value.FindMember("value") || !value.FindMember("raw"))
+    {
+        literalType_ = LITERAL_UNKNOWN;
+        return false;
+    }
+
+    const rapidjson::Value& v = value["value"];
+
+    raw_ = value["raw"].GetString();;
+
+    if (v.IsNull())
+        literalType_ = LITERAL_NULL;
+    else if (v.IsString())
+        literalType_ = LITERAL_STRING;
+    else if (v.IsNumber())
+        literalType_ = LITERAL_NUMBER;
+    else if (v.IsBool())
+        literalType_ = LITERAL_BOOLEAN;
+
+
+
+    return true;
+}
+
+void JSASTLiteral::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTArrayExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    ParseExpressionArray(value["elements"], elements_);
+
+    return true;
+}
+
+void JSASTArrayExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTProperty::Parse(const rapidjson::Value& value)
+{
+    key_ = ParseExpression(value["key"]);
+    assert(key_);
+
+    value_ = ParseExpression(value["value"]);
+    assert(value_);
+
+    return true;
+}
+
+void JSASTProperty::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTObjectExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    const rapidjson::Value& jprops = value["properties"];
+
+    assert(jprops.IsArray());
+
+    for (Value::ConstValueIterator itr = jprops.Begin(); itr != jprops.End(); itr++)
+    {
+
+        JSASTProperty* property = new JSASTProperty();
+        property->Parse(*itr);
+        properties_.Push(property);
+
+    }
+
+
+    return true;
+}
+
+void JSASTObjectExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTMemberExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    computed_ = value["computed"].GetBool();
+    object_ = ParseExpression(value["object"]);
+    property_ = ParseExpression(value["property"]);
+
+    return true;
+}
+
+void JSASTMemberExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTCallExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    callee_ = ParseExpression(value["callee"]);
+
+    ParseExpressionArray(value["arguments"], arguments_);
+
+    return true;
+
+}
+
+void JSASTCallExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTNewExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    callee_ = ParseExpression(value["callee"]);
+
+    ParseExpressionArray(value["arguments"], arguments_);
+
+    return true;
+
+}
+
+void JSASTNewExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTVariableDeclarator::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    id_ = ParseExpression(value["id"]);
+
+    const Value::Member* init = value.FindMember("init");
+    if (init && init->value.IsObject())
+    {
+        init_ = ParseExpression(init->value);
+    }
+
+    return true;
+
+}
+
+void JSASTVariableDeclarator::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTUnaryExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    argument_ = ParseExpression(value["argument"]);
+
+    prefix_ = value["prefix"].IsTrue();
+
+    operator_ = value["operator"].GetString();
+
+    return true;
+
+}
+
+void JSASTUnaryExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTUpdateExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    argument_ = ParseExpression(value["argument"]);
+
+    prefix_ = value["prefix"].IsTrue();
+
+    operator_ = value["operator"].GetString();
+
+    return true;
+
+}
+
+void JSASTUpdateExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTConditionalExpression::Parse(const rapidjson::Value& value)
+{
+    JSASTExpression::Parse(value);
+
+    test_ = ParseExpression(value["test"]);
+    consequent_ = ParseExpression(value["consequent"]);
+    alternate_ = ParseExpression(value["alternate"]);
+
+    return true;
+}
+
+void JSASTConditionalExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTFunctionExpression::Parse(const rapidjson::Value& value)
+{
+
+    JSASTExpression::Parse(value);
+
+    if (!value["id"].IsNull())
+    {
+        id_ = (JSASTIdentifier*) ParseExpression(value["id"],  JSAST_IDENTIFIER);
+    }
+
+    ParseExpressionArray(value["params"], params_);
+
+    const rapidjson::Value& body = value["body"];
+
+    String type = body["type"].GetString();
+
+    if (type == "BlockStatement")
+    {
+        body_ = (JSASTBlockStatement*) ParseStatement(body);
+    }
+    else
+    {
+        return true;
+    }
+
+
+    return true;
+}
+
+void JSASTFunctionExpression::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTFunctionExpression::~JSASTFunctionExpression()
+{
+    if (id_)
+        delete id_;
+    if (rest_)
+        delete rest_;
+    if (body_)
+        delete body_;
+
+
+
+    for (unsigned i = 0; i < params_.Size(); i++)
+        delete params_[i];
+    for (unsigned i = 0; i < defaults_.Size(); i++)
+        delete defaults_[i];
+
+    params_.Clear();
+    defaults_.Clear();
+}
+
+// STATEMENTS
+
+bool JSASTExpressionStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    const Value::Member* expr = value.FindMember("expression");
+    assert(expr);
+
+    expression_ = ParseExpression(expr->value);
+
+    return expression_ != NULL;
+
+}
+
+void JSASTLabeledStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTLabeledStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    const Value::Member* body = value.FindMember("body");
+    assert(body);
+
+    const Value::Member* label = value.FindMember("label");
+    assert(label);
+
+
+    body_ = ParseStatement(body->value);
+    label_ = (JSASTIdentifier*) ParseExpression(label->value,  JSAST_IDENTIFIER);
+
+    return body_ && label_;
+
+}
+
+void JSASTExpressionStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTReturnStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    if (!value["argument"].IsNull())
+    {
+        argument_ = ParseExpression(value["argument"]);
+    }
+
+
+    return true;
+}
+
+void JSASTReturnStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+bool JSASTVariableDeclaration::Parse(const rapidjson::Value& value)
+{
+    JSASTDeclaration::Parse(value);
+
+    kind_ = value["kind"].GetString();
+
+    ParseExpressionArray(value["declarations"], declarations_);
+
+    return true;
+
+}
+
+void JSASTVariableDeclaration::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTComment::JSASTComment() : JSASTNode(JSAST_COMMENT) , isBlock_(false)
+{
+
+}
+
+JSASTComment::~JSASTComment()
+{
+
+}
+
+bool JSASTComment::Parse(const rapidjson::Value& value)
+{
+    JSASTNode::Parse(value);
+
+    // Block or Line
+    if (value.FindMember("type"))
+    {
+        String type = value["type"].GetString();
+        isBlock_ = type == "Block";
+    }
+
+    if (value.FindMember("value"))
+    {
+        value_ = value["value"].GetString();
+    }
+
+    return true;
+}
+
+void JSASTComment::Accept(JSASTVisitor *visitor)
+{
+    visitor->visit(this);
+}
+
+
+
+JSASTProgram::JSASTProgram(const String &path, const String &json) : JSASTNode(JSAST_PROGRAM)
+  , document_(new Document())
+{
+
+    if (document_->Parse<0>(json.CString()).HasParseError())
+    {
+        LOGERRORF("Could not parse JSON data from %s", path.CString());
+        return;
+    }
+    else
+    {
+        //StringBuffer buffer;
+        //PrettyWriter<StringBuffer> writer(buffer, &(document_->GetAllocator()));
+        //writer.SetIndent(' ', 3);
+        //document_->Accept(writer);
+        //LOGINFOF("%s", buffer.GetString());
+    }
+
+    Parse(*document_);
+
+}
+
+
+
+bool JSASTProgram::Parse(const rapidjson::Value& value)
+{
+    JSASTNode::Parse(value);
+
+    for (Value::ConstMemberIterator itr = value.MemberBegin();
+         itr != value.MemberEnd(); ++itr)
+    {
+        String name = itr->name.GetString();
+
+        if (name == "body")
+        {
+            ParseStatementArray(itr->value, body_);
+        }
+
+        if (name == "comments")
+        {
+            if (itr->value.IsArray())
+            {
+                for (Value::ConstValueIterator citr = itr->value.Begin();
+                     citr != itr->value.End();
+                     citr++)
+                {
+                    JSASTComment* comment = new JSASTComment();
+                    assert(citr->IsObject());
+                    comment->Parse(*citr);
+                    comments_.Push(comment);
+                }
+            }
+        }
+
+    }
+
+    return true;
+
+}
+
+JSASTProgram::~JSASTProgram()
+{
+    for (unsigned i = 0; i < comments_.Size(); i++)
+        delete comments_[i];
+
+    comments_.Clear();
+
+    delete document_;
+    document_ = 0;
+}
+
+void JSASTProgram::Accept(JSASTVisitor *visitor)
+{
+    visitor->visit(this);
+}
+
+
+JSASTProgram* JSASTProgram::ParseFromJSON(const String& path, const String& json)
+{
+    JSASTProgram* cunit = new JSASTProgram(path, json);
+
+    return cunit;
+}
+
+bool JSASTIfStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    test_ = ParseExpression(value["test"]);
+    consequent_ = ParseStatement(value["consequent"]);
+
+    if (!value["alternate"].IsNull())
+        alternate_ = ParseStatement(value["alternate"]);
+
+    return true;
+}
+
+void JSASTIfStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+bool JSASTFunctionDeclaration::Parse(const rapidjson::Value& value)
+{
+    JSASTDeclaration::Parse(value);
+
+    if (!value["id"].IsNull())
+    {
+        id_ = (JSASTIdentifier*) ParseExpression(value["id"],  JSAST_IDENTIFIER);
+    }
+
+    ParseExpressionArray(value["params"], params_);
+
+    const rapidjson::Value& body = value["body"];
+
+    String type = body["type"].GetString();
+
+    if (type == "BlockStatement")
+    {
+        body_ = (JSASTBlockStatement*) ParseStatement(body);
+    }
+    else
+    {
+
+    }
+
+
+    return true;
+}
+
+void JSASTFunctionDeclaration::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+JSASTFunctionDeclaration::~JSASTFunctionDeclaration()
+{
+    if (id_)
+        delete id_;
+    if (rest_)
+        delete rest_;
+    if (body_)
+        delete body_;
+
+    for (unsigned i = 0; i < params_.Size(); i++)
+        delete params_[i];
+    for (unsigned i = 0; i < defaults_.Size(); i++)
+        delete defaults_[i];
+
+    params_.Clear();
+    defaults_.Clear();
+}
+
+bool JSASTBlockStatement::Parse(const rapidjson::Value& value)
+{
+    if (value.FindMember("body"))
+    {
+        ParseStatementArray(value["body"], body_);
+
+    }
+
+    return true;
+
+}
+
+void JSASTBlockStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+bool JSASTForStatement::Parse(const rapidjson::Value& value)
+{
+    JSASTStatement::Parse(value);
+
+    if (value.FindMember("init") && !value.FindMember("init")->value.IsNull())
+    {
+        const Value::Member* member = value.FindMember("init");
+
+        String type = member->value.FindMember("type")->value.GetString();
+        if (type == "VariableDeclaration")
+        {
+            initVariable_ = (JSASTVariableDeclaration*) ParseStatement(member->value);
+        }
+        else
+        {
+            initExpression_ = ParseExpression(member->value);
+        }
+    }
+
+    if (value.FindMember("test") && !value.FindMember("test")->value.IsNull())
+    {
+        test_ = ParseExpression(value["test"]);
+    }
+
+    if (value.FindMember("update") && !value.FindMember("update")->value.IsNull())
+    {
+        update_ = ParseExpression(value["update"]);
+    }
+
+    if (value.FindMember("body"))
+    {
+        body_ = ParseStatement(value["body"]);
+    }
+
+    return true;
+}
+
+void JSASTForStatement::Accept(JSASTVisitor* visitor)
+{
+    visitor->visit(this);
+}
+
+
+}

+ 967 - 0
Source/AtomicEditorWork/Javascript/JSAST.h

@@ -0,0 +1,967 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+// see https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Container/Vector.h>
+
+using namespace Atomic;
+
+namespace rapidjson
+{
+template<typename CharType> struct UTF8;
+class CrtAllocator;
+template <typename BaseAllocator> class MemoryPoolAllocator;
+template <typename Encoding, typename Allocator> class GenericValue;
+typedef GenericValue<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Value;
+template <typename Encoding, typename Allocator> class GenericDocument;
+typedef GenericDocument<UTF8<char>, MemoryPoolAllocator<CrtAllocator> > Document;
+}
+
+namespace AtomicEditor
+{
+
+enum JSASTType
+{
+    JSAST_UNDEFINED,
+    JSAST_COMMENT,
+    JSAST_PROGRAM,
+    JSAST_PROPERTY,
+    // Expression
+    JSAST_ASSIGNMENTEXPRESSION,
+    JSAST_LOGICALEXPRESSION,
+    JSAST_IDENTIFIER,
+    JSAST_CALLEXPRESSION,
+    JSAST_VARIABLEDECLARATOR,
+    JSAST_BINARYEXPRESSION,
+    JSAST_MEMBEREXPRESSION,
+    JSAST_LITERAL,
+    JSAST_ARRAYEXPRESSION,
+    JSAST_FUNCTIONEXPRESSION,
+    JSAST_NEWEXPRESSION,
+    JSAST_UNARYEXPRESSION,
+    JSAST_UPDATEEXPRESSION,
+    JSAST_CONDITIONALEXPRESSION,
+    JSAST_OBJECTEXPRESSION,
+    JSAST_THISEXPRESSION,
+    // Statements
+    JSAST_RETURNSTATEMENT,
+    JSAST_EMPTYSTATEMENT,
+    JSAST_EXPRESSIONSTATEMENT,
+    JSAST_LABELEDSTATEMENT,
+    JSAST_VARIABLEDECLARATION,
+    JSAST_FUNCTIONDECLARATION,
+    JSAST_IFSTATEMENT,
+    JSAST_BLOCKSTATEMENT
+};
+
+
+class JSASTProgram;
+class JSASTStatement;
+class JSASTEmptyStatement;
+class JSASTExpressionStatement;
+class JSASTExpression;
+class JSASTVisitor;
+class JSASTBlockStatement;
+
+class JSASTNode
+{    
+public:
+
+    struct Loc
+    {
+        int startLine_;
+        int startColumn_;
+        int endLine_;
+        int endColumn_;
+
+        Loc()
+        {
+            startLine_ = startColumn_ = -1;
+            endLine_ = endColumn_ = -1;
+        }
+
+        bool Valid() const { return startLine_ != -1 && startColumn_ != -1 &&
+                    endLine_ != -1 && endColumn_ != -1; }
+
+    };
+
+    JSASTNode(JSASTType type);
+    virtual bool Parse(const rapidjson::Value& value);
+
+    JSASTType GetType() { return type_; }
+    const Loc& GetLoc() { return loc_; }
+
+    virtual void Accept(JSASTVisitor* visitor) = 0;
+
+protected:
+
+    bool ParseLoc(const rapidjson::Value &value);
+    bool ParseRange(const rapidjson::Value &value);
+
+    JSASTStatement* ParseStatement(const rapidjson::Value &value);
+    JSASTExpression* ParseExpression(const rapidjson::Value &value, JSASTType astType = JSAST_UNDEFINED);
+
+    bool ParseExpressionArray(const rapidjson::Value &value, Vector<JSASTExpression *> &expressions);
+    bool ParseStatementArray(const rapidjson::Value &value, Vector<JSASTStatement *> &statements);
+
+private:
+
+    Loc loc_;
+    JSASTType type_;
+    // character range
+    int rangeStart_;
+    int rangeEnd_;
+
+
+};
+
+class JSASTComment: public JSASTNode
+{
+public:
+
+    JSASTComment();
+    virtual ~JSASTComment();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetValue() { return value_; }
+    bool IsBlock() { return isBlock_; }
+
+private:
+
+    bool isBlock_;
+    String value_;
+
+};
+
+class JSASTProgram : public JSASTNode
+{
+public:
+
+    JSASTProgram(const String& path, const String& json);
+    virtual ~JSASTProgram();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    static JSASTProgram* ParseFromJSON(const String& path, const String& json);
+
+    unsigned GetStatementCount() { return body_.Size(); }
+    JSASTStatement* GetStatement(unsigned idx) { return body_[idx]; }
+
+    unsigned GetCommentCount() { return comments_.Size(); }
+    JSASTComment* GetComment(unsigned idx) { return comments_[idx]; }
+
+
+protected:
+
+private:
+
+    Vector<JSASTStatement*> body_;
+    rapidjson::Document* document_;
+
+    Vector<JSASTComment*> comments_;
+
+};
+
+class JSASTExpression : public JSASTNode
+{
+public:
+
+    virtual ~JSASTExpression() {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTNode::Parse(value);
+    }
+
+protected:
+    JSASTExpression(JSASTType type) :  JSASTNode(type) {}
+
+};
+
+
+class JSASTIdentifier : public JSASTExpression
+{
+
+public:
+    JSASTIdentifier() : JSASTExpression(JSAST_IDENTIFIER) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+    String name_;
+};
+
+class JSASTThisExpression : public JSASTExpression
+{
+
+public:
+    JSASTThisExpression() : JSASTExpression(JSAST_THISEXPRESSION) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+};
+
+
+class JSASTLiteral : public JSASTExpression
+{
+
+public:
+
+    enum LiteralType
+    {
+        LITERAL_UNKNOWN,
+        LITERAL_NULL,
+        LITERAL_STRING,
+        LITERAL_NUMBER,
+        LITERAL_BOOLEAN,
+        LITERAL_REGEX
+    };
+
+    JSASTLiteral() : JSASTExpression(JSAST_LITERAL), literalType_(LITERAL_UNKNOWN) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    LiteralType GetLiteralType() { return literalType_; }
+
+private:
+
+    //value: string | boolean | null | number | RegExp;
+    LiteralType literalType_;
+    String raw_;
+};
+
+class JSASTArrayExpression : public JSASTExpression
+{
+
+public:
+    JSASTArrayExpression() : JSASTExpression(JSAST_ARRAYEXPRESSION) {}
+
+    virtual ~JSASTArrayExpression()
+    {
+        for (unsigned i = 0; i < elements_.Size(); i++)
+            delete elements_[i];
+
+        elements_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    unsigned GetElementCount() { return elements_.Size(); }
+    JSASTExpression* GetElement(unsigned idx) { return elements_[idx]; }
+
+private:
+
+    Vector<JSASTExpression*> elements_;
+
+};
+
+class JSASTProperty: public JSASTNode
+{
+
+public:
+
+    JSASTProperty() : JSASTNode(JSAST_PROPERTY), key_(0), value_(0) {}
+    virtual ~JSASTProperty()
+    {
+        if (key_)
+            delete key_;
+
+        if (value_)
+            delete value_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetKey() { return key_; }
+    JSASTExpression* GetValue() { return value_; }
+
+
+private:
+    JSASTExpression* key_;
+    JSASTExpression* value_;
+
+};
+
+
+class JSASTObjectExpression : public JSASTExpression
+{
+
+public:
+    JSASTObjectExpression() : JSASTExpression(JSAST_OBJECTEXPRESSION) {}
+
+    virtual ~JSASTObjectExpression()
+    {
+        for (unsigned i = 0; i < properties_.Size(); i++)
+            delete properties_[i];
+
+        properties_.Clear();
+    }
+
+    unsigned GetPropertyCount() { return properties_.Size(); }
+    JSASTProperty* GetProperty(unsigned idx) { return properties_[idx]; }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+private:
+
+    Vector<JSASTProperty*> properties_;
+};
+
+
+
+class JSASTAssignmentExpression : public JSASTExpression
+{
+
+public:
+    JSASTAssignmentExpression() : JSASTExpression(JSAST_ASSIGNMENTEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTAssignmentExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_; }
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+class JSASTLogicalExpression : public JSASTExpression
+{
+
+public:
+    JSASTLogicalExpression() : JSASTExpression(JSAST_LOGICALEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTLogicalExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_; }
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+
+class JSASTUnaryExpression : public JSASTExpression
+{
+
+public:
+    JSASTUnaryExpression() : JSASTExpression(JSAST_UNARYEXPRESSION),
+        argument_(0), prefix_(false) {}
+
+    virtual ~JSASTUnaryExpression()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_;}
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+private:
+
+    String operator_;
+    bool prefix_;
+    JSASTExpression* argument_;
+
+};
+
+class JSASTUpdateExpression : public JSASTExpression
+{
+
+public:
+    JSASTUpdateExpression() : JSASTExpression(JSAST_UPDATEEXPRESSION),
+        argument_(0), prefix_(false) {}
+
+    virtual ~JSASTUpdateExpression()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetOperator() { return operator_;}
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+private:
+
+    String operator_;
+    bool prefix_;
+    JSASTExpression* argument_;
+
+};
+
+class JSASTFunctionExpression : public JSASTExpression
+{
+
+public:
+    JSASTFunctionExpression() : JSASTExpression(JSAST_FUNCTIONEXPRESSION),
+        id_(0), rest_(0), body_(0), generator_(false), expression_(false) {}
+
+    virtual ~JSASTFunctionExpression();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTIdentifier* GetID() { return id_; }
+    JSASTBlockStatement* GetBodyStatement() { return body_; }
+
+    unsigned GetParamsCount() { return params_.Size(); }
+    JSASTExpression* GetParam(unsigned idx) { return params_[idx]; }
+
+private:
+
+    JSASTIdentifier* id_;
+    Vector<JSASTExpression*> params_;
+    Vector<JSASTExpression*> defaults_;
+    JSASTIdentifier* rest_;
+
+    JSASTBlockStatement* body_;
+
+    bool generator_;
+    bool expression_;
+
+};
+
+
+class JSASTBinaryExpression : public JSASTExpression
+{
+
+public:
+    JSASTBinaryExpression() : JSASTExpression(JSAST_BINARYEXPRESSION),
+        leftExpression_(0), rightExpression_(0) {}
+
+    virtual ~JSASTBinaryExpression()
+    {
+        if (leftExpression_)
+            delete leftExpression_;
+        if (rightExpression_)
+            delete rightExpression_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetLeft() { return leftExpression_; }
+    JSASTExpression* GetRight() { return rightExpression_; }
+
+    const String& GetOperator() { return operator_;}
+
+private:
+
+    String operator_;
+    JSASTExpression* leftExpression_;
+    JSASTExpression* rightExpression_;
+
+};
+
+class JSASTMemberExpression : public JSASTExpression
+{
+
+public:
+    JSASTMemberExpression() : JSASTExpression(JSAST_MEMBEREXPRESSION),
+        object_(0), property_(0), computed_(false) {}
+
+    virtual ~JSASTMemberExpression()
+    {
+        if (object_)
+            delete object_;
+        if (property_)
+            delete property_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetObject() { return object_;}
+    JSASTExpression* GetProperty() { return property_; }
+
+
+private:
+
+    JSASTExpression* object_;
+    JSASTExpression* property_;
+    bool computed_;
+};
+
+class JSASTConditionalExpression : public JSASTExpression
+{
+public:
+
+    JSASTConditionalExpression() : JSASTExpression(JSAST_CONDITIONALEXPRESSION), test_(0), consequent_(0), alternate_(0) {}
+
+    virtual ~JSASTConditionalExpression()
+    {
+        if (test_)
+            delete test_;
+        if (consequent_)
+            delete consequent_;
+        if (alternate_)
+            delete alternate_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTExpression* GetConsequent() { return consequent_; }
+    JSASTExpression* GetAlternate() { return alternate_; }
+
+private:
+
+    JSASTExpression* test_;
+    JSASTExpression* consequent_;
+    JSASTExpression* alternate_;
+
+};
+
+class JSASTCallExpression : public JSASTExpression
+{
+
+public:
+    JSASTCallExpression() : JSASTExpression(JSAST_CALLEXPRESSION),
+        callee_(0){}
+
+    virtual ~JSASTCallExpression()
+    {
+        if (callee_)
+            delete callee_;
+
+        for (unsigned i = 0; i < arguments_.Size(); i++)
+        {
+            delete arguments_[i];
+        }
+
+        arguments_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetCallee() { return callee_; }
+    unsigned GetArgumentCount() { return arguments_.Size(); }
+    JSASTExpression* GetArgument(unsigned idx) { return arguments_[idx]; }
+
+private:
+
+    JSASTExpression* callee_;
+    Vector<JSASTExpression*> arguments_;
+
+};
+
+class JSASTNewExpression : public JSASTExpression
+{
+
+public:
+    JSASTNewExpression() : JSASTExpression(JSAST_NEWEXPRESSION),
+        callee_(0){}
+
+    virtual ~JSASTNewExpression()
+    {
+        if (callee_)
+            delete callee_;
+
+        for (unsigned i = 0; i < arguments_.Size(); i++)
+        {
+            delete arguments_[i];
+        }
+
+        arguments_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetCallee() { return callee_; }
+    unsigned GetArgumentCount() { return arguments_.Size(); }
+    JSASTExpression* GetArgument(unsigned idx) { return arguments_[idx]; }
+
+
+private:
+
+    JSASTExpression* callee_;
+    Vector<JSASTExpression*> arguments_;
+
+};
+
+class JSASTVariableDeclarator : public JSASTExpression
+{
+public:
+
+    JSASTVariableDeclarator() : JSASTExpression(JSAST_VARIABLEDECLARATOR), id_(0), init_(0) {}
+
+    virtual ~JSASTVariableDeclarator()
+    {
+        if (id_)
+            delete id_;
+        if (init_)
+            delete init_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetID() { return id_; }
+    JSASTExpression* GetInit() { return init_; }
+
+
+private:
+
+    JSASTExpression* id_;
+    JSASTExpression* init_;
+
+};
+
+class JSASTStatement : public JSASTNode
+{
+
+public:
+
+    virtual ~JSASTStatement() {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTNode::Parse(value);
+    }
+
+protected:
+    JSASTStatement(JSASTType type) :  JSASTNode(type) {}
+
+};
+
+class JSASTEmptyStatement : public JSASTStatement
+{
+
+public:
+    JSASTEmptyStatement() : JSASTStatement(JSAST_EMPTYSTATEMENT) {}
+
+    virtual bool Parse(const rapidjson::Value& value)
+    {
+        return JSASTStatement::Parse(value);
+    }
+    virtual void Accept(JSASTVisitor* visitor);
+
+};
+
+class JSASTExpressionStatement : public JSASTStatement
+{
+
+public:
+    JSASTExpressionStatement(JSASTType type = JSAST_EXPRESSIONSTATEMENT) : JSASTStatement(type),
+        expression_(0) {}
+
+    virtual ~JSASTExpressionStatement()
+    {
+        if (expression_)
+            delete expression_;
+    }
+
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetExpression() { return expression_; }
+
+protected:
+
+    JSASTExpression* expression_;
+
+};
+
+class JSASTLabeledStatement : public JSASTStatement
+{
+
+public:
+    JSASTLabeledStatement(JSASTType type = JSAST_LABELEDSTATEMENT) : JSASTStatement(type),
+        label_(0), body_(0) {}
+
+    virtual ~JSASTLabeledStatement()
+    {
+        if (label_)
+            delete label_;
+
+        if (body_)
+            delete body_;
+
+    }
+
+    JSASTIdentifier* GetLabel() { return label_; }
+    JSASTStatement* GetBody() { return body_; }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+protected:
+
+    JSASTIdentifier* label_;
+    JSASTStatement* body_;
+
+};
+
+
+class JSASTReturnStatement : public JSASTStatement
+{
+
+public:
+    JSASTReturnStatement(JSASTType type = JSAST_RETURNSTATEMENT) : JSASTStatement(type),
+        argument_(0) {}
+
+    virtual ~JSASTReturnStatement()
+    {
+        if (argument_)
+            delete argument_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetArgument() { return argument_; }
+
+protected:
+
+    JSASTExpression* argument_;
+
+};
+
+
+class JSASTDeclaration : public JSASTStatement
+{
+
+public:
+
+    virtual bool Parse(const rapidjson::Value& value) { return JSASTStatement::Parse(value); }
+
+protected:
+
+    JSASTDeclaration(JSASTType type) : JSASTStatement(type) {}
+
+};
+
+class JSASTBlockStatement : public JSASTStatement
+{
+public:
+    JSASTBlockStatement() : JSASTStatement(JSAST_BLOCKSTATEMENT) {}
+
+    virtual ~JSASTBlockStatement()
+    {
+        for (unsigned i = 0; i < body_.Size(); i++)
+            delete body_[i];
+
+        body_.Clear();
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    unsigned GetStatementCount() { return body_.Size(); }
+    JSASTStatement* GetStatement(unsigned idx) { return body_[idx]; }
+
+
+private:
+
+    Vector<JSASTStatement*> body_;
+
+};
+
+class JSASTIfStatement : public JSASTStatement
+{
+public:
+
+    JSASTIfStatement() : JSASTStatement(JSAST_IFSTATEMENT), test_(0), consequent_(0), alternate_(0) {}
+
+    virtual ~JSASTIfStatement()
+    {
+        if (test_)
+            delete test_;
+        if (consequent_)
+            delete consequent_;
+        if (alternate_)
+            delete alternate_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTStatement* GetConsequent() { return consequent_; }
+    JSASTStatement* GetAlternate() { return alternate_; }
+
+private:
+
+    JSASTExpression* test_;
+    JSASTStatement* consequent_;
+    JSASTStatement* alternate_;
+
+};
+
+class JSASTVariableDeclaration : public JSASTDeclaration
+{
+
+public:
+
+    JSASTVariableDeclaration() : JSASTDeclaration(JSAST_VARIABLEDECLARATION) {}
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    const String& GetKind()  { return kind_; }
+
+    unsigned GetDeclarationsCount() { return declarations_.Size(); }
+    JSASTExpression* GetDeclaration(unsigned idx) { return declarations_[idx]; }
+
+protected:
+
+    // "var" | "let" | "const";
+    String kind_;
+
+    // Array of JSASTVariableDeclarator
+    Vector<JSASTExpression*> declarations_;
+
+};
+
+
+class JSASTFunctionDeclaration : public JSASTDeclaration
+{
+
+public:
+    JSASTFunctionDeclaration() : JSASTDeclaration(JSAST_FUNCTIONDECLARATION),
+        id_(0), rest_(0), body_(0), generator_(false), expression_(false) {}
+
+    virtual ~JSASTFunctionDeclaration();
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTIdentifier* GetID() { return id_; }
+    JSASTBlockStatement* GetBodyStatement() { return body_; }
+
+    unsigned GetParamsCount() { return params_.Size(); }
+    JSASTExpression* GetParam(unsigned idx) { return params_[idx]; }
+
+private:
+
+    JSASTIdentifier* id_;
+    Vector<JSASTExpression*> params_;
+    Vector<JSASTExpression*> defaults_;
+    JSASTIdentifier* rest_;
+
+    JSASTBlockStatement* body_;
+
+    bool generator_;
+    bool expression_;
+
+};
+
+/*
+interface ForStatement <: Statement {
+    type: "ForStatement";
+    init: VariableDeclaration | Expression | null;
+    test: Expression | null;
+    update: Expression | null;
+    body: Statement;
+}
+*/
+
+
+class JSASTForStatement : public JSASTStatement
+{
+public:
+
+    JSASTForStatement() : JSASTStatement(JSAST_IFSTATEMENT),
+        initVariable_(0), initExpression_(0), test_(0), update_(0), body_(0)
+    {
+
+    }
+
+    virtual ~JSASTForStatement()
+    {
+        if (initVariable_)
+            delete initVariable_;
+
+        if (initExpression_)
+            delete initExpression_;
+
+        if (test_)
+            delete(test_);
+
+        if(update_)
+            delete update_;
+
+        if (body_)
+            delete body_;
+    }
+
+    virtual bool Parse(const rapidjson::Value& value);
+    virtual void Accept(JSASTVisitor* visitor);
+
+    JSASTExpression* GetTest() { return test_; }
+    JSASTVariableDeclaration* GetInitVariable() { return initVariable_; }
+    JSASTExpression* GetInitExpression() { return initExpression_; }
+    JSASTExpression* GetUpdate() { return update_; }
+    JSASTStatement* GetBody() { return body_; }
+
+private:
+    JSASTVariableDeclaration* initVariable_;
+    JSASTExpression* initExpression_;
+    JSASTExpression* test_;
+    JSASTExpression* update_;
+    JSASTStatement* body_;
+
+};
+
+
+}
+
+

+ 2 - 2
Source/AtomicJS/JSBind/JSBHeaderVisitor.cpp → Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.cpp

@@ -2,6 +2,6 @@
 // Please see LICENSE.md in repository root for license information
 // Please see LICENSE.md in repository root for license information
 // https://github.com/AtomicGameEngine/AtomicGameEngine
 // https://github.com/AtomicGameEngine/AtomicGameEngine
 
 
-#include <Atomic/Atomic.h>
-#include "JSBHeaderVisitor.h"
+#include "AtomicEditor.h"
 
 
+#include "JSASTSyntaxColorVisitor.h"

+ 360 - 0
Source/AtomicEditorWork/Javascript/JSASTSyntaxColorVisitor.h

@@ -0,0 +1,360 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+
+#include "JSASTVisitor.h"
+#include "JSTheme.h"
+
+#include <TurboBadger/tb_style_edit.h>
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+// TBBlock::InsertText, handle text input, split/layout
+
+class JSASTSyntaxColorVisitor : public JSASTTraversalVisitor
+{
+public:
+
+    JSASTSyntaxColorVisitor(TBStyleEdit* styleEdit) : styleEdit_(styleEdit) {}
+
+    using JSASTTraversalVisitor::visit;
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr)
+    {
+        //SetLocColor(expr->GetLoc(), 6);
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTLiteral* expr)
+    {
+        JSThemeColor color = JSTHEME_NORMAL;
+        switch (expr->GetLiteralType())
+        {
+        case JSASTLiteral::LITERAL_BOOLEAN:
+            color = JSTHEME_LITERAL_BOOLEAN;
+            break;
+        case JSASTLiteral::LITERAL_STRING:
+            color = JSTHEME_LITERAL_STRING;
+            break;
+        case JSASTLiteral::LITERAL_NULL:
+            color = JSTHEME_LITERAL_NULL;
+            break;
+        case JSASTLiteral::LITERAL_NUMBER:
+            color = JSTHEME_LITERAL_NUMBER;
+            break;
+        case JSASTLiteral::LITERAL_REGEX:
+            color = JSTHEME_LITERAL_REGEX;
+            break;
+
+        default:
+            break;
+        }
+
+        SetLocColor(expr->GetLoc(), color);
+        return expr;
+    }
+
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration* decl)
+    {
+        SetLocColor(decl->GetLoc(), JSTHEME_FUNCTION, true);
+
+        JSASTNode* id = decl->GetID();
+        if (id)
+            SetLocColor(id->GetLoc(), JSTHEME_CODE, true);
+
+        for (unsigned i = 0; i < decl->GetParamsCount(); i++)
+        {
+            JSASTExpression* p = decl->GetParam(i);
+            SetLocColor(p->GetLoc(), JSTHEME_FUNCTIONDECLARG, true);
+        }
+
+
+        return JSASTTraversalVisitor::visit(decl);
+
+    }
+
+    virtual JSASTStatement* visit(JSASTVariableDeclaration* decl)
+    {
+        SetLocColor(decl->GetLoc(), JSTHEME_VAR, true);
+
+        SetLocColor(decl->GetLoc(), "=", JSTHEME_KEYWORD, true);
+
+        for (unsigned i = 0; i < decl->GetDeclarationsCount(); i++)
+        {
+            JSASTExpression* expr = decl->GetDeclaration(i);
+
+            if (expr->GetType() == JSAST_VARIABLEDECLARATOR)
+            {
+                JSASTVariableDeclarator* v = (JSASTVariableDeclarator*) expr;
+
+                if (JSASTExpression* init = v->GetInit())
+                {
+                    if (init->GetType() == JSAST_FUNCTIONEXPRESSION)
+                    {
+                        if (v->GetID())
+                            SetLocColor(v->GetID()->GetLoc(), JSTHEME_CODE, true);
+                    }
+                }
+            }
+
+        }
+
+        return JSASTTraversalVisitor::visit(decl);
+    }
+
+    virtual JSASTComment* visit(JSASTComment* comment)
+    {
+        SetLocColor(comment->GetLoc(), JSTHEME_COMMENT, false);
+        return comment;
+    }
+
+    virtual JSASTStatement* visit(JSASTIfStatement* stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+    virtual JSASTExpression* visit(JSASTConditionalExpression* expr)
+    {
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTStatement* visit(JSASTForStatement* stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTCallExpression* expr)
+    {
+
+        JSASTNode* locNode = expr->GetCallee();
+        if (locNode->GetType() == JSAST_MEMBEREXPRESSION)
+        {
+            locNode = ((JSASTMemberExpression*) locNode)->GetProperty();
+        }
+
+        //SetLocColor(locNode->GetLoc(), 2);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt)
+    {
+        SetLocColor(stmt->GetLoc(), JSTHEME_KEYWORD, true);
+
+        return JSASTTraversalVisitor::visit(stmt);
+    }
+
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr)
+    {
+        const String& op = expr->GetOperator();
+
+        SetLocColor(expr->GetLoc(), op, JSTHEME_KEYWORD, false);
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+    virtual JSASTExpression* visit(JSASTNewExpression* expr)
+    {
+        SetLocColor(expr->GetLoc(), JSTHEME_KEYWORD, true);
+
+        JSASTNode* locNode = expr->GetCallee();
+
+        if (locNode->GetType() == JSAST_MEMBEREXPRESSION)
+        {
+            locNode = ((JSASTMemberExpression*) locNode)->GetProperty();
+        }
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr)
+    {
+        SetLocColor(expr->GetLoc(), "function", JSTHEME_FUNCTION, true);
+
+        for (unsigned i = 0; i < expr->GetParamsCount(); i++)
+        {
+            JSASTExpression* p = expr->GetParam(i);
+            SetLocColor(p->GetLoc(), JSTHEME_FUNCTIONDECLARG, true);
+        }
+
+        return JSASTTraversalVisitor::visit(expr);
+    }
+
+
+private:
+
+    void SetLocColor(const JSASTNode::Loc& loc, unsigned color, bool single = false)
+    {
+        if (!loc.Valid())
+            return;
+
+        PODVector<TBTextFragment*> fragments;
+        GetTextFragments(loc, fragments);
+        for (unsigned i = 0; i < fragments.Size(); i++)
+        {
+            fragments.At(i)->themeColor = color;
+            if (single)
+                break;
+        }
+    }
+
+    void SetLocColor(const JSASTNode::Loc& loc, const String& value, unsigned color, bool single = false)
+    {
+        if (!loc.Valid())
+            return;
+
+        String substring = value;
+
+        PODVector<TBTextFragment*> fragments;
+        GetTextFragments(loc, fragments);
+        for (unsigned i = 0; i < fragments.Size(); i++)
+        {
+            TBTextFragment* fragment = fragments[i];
+
+            if (!strncmp(substring.CString(), fragment->Str(), substring.Length()))
+            {
+                fragments.At(i)->themeColor = color;
+
+                substring = substring.Substring(fragments.At(i)->len);
+
+                if (single || !substring.Length())
+                    break;
+            }
+        }
+    }
+
+
+    TBTextFragment* GetFragmentAt( const JSASTNode::Loc& loc)
+    {
+        if (!loc.Valid())
+            return NULL;
+
+        int line = loc.startLine_ - 1;
+
+        TBBlock *block = styleEdit_->blocks.GetFirst();
+        int count = 0;
+        while (block && count < line)
+        {
+            block = block->GetNext();
+            count++;
+        }
+
+        if (!block)
+            return NULL;
+
+        for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+        {
+            if (frag->ofs >= loc.startColumn_)
+                return frag;
+        }
+
+        return NULL;
+
+    }
+
+
+    void GetTextFragments( const JSASTNode::Loc& loc,  PODVector<TBTextFragment*>& fragments)
+    {
+        if (!loc.Valid())
+            return;
+
+        for (int lcounter = loc.startLine_; lcounter <= loc.endLine_; lcounter++)
+        {
+            int line = lcounter - 1;
+
+            TBBlock *block = styleEdit_->blocks.GetFirst();
+            int count = 0;
+            while (block && count < line)
+            {
+                block = block->GetNext();
+                count++;
+            }
+
+            if (!block)
+                return;
+
+            for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+            {
+                if (loc.startLine_ == loc.endLine_)
+                {
+                    if (frag->ofs >= loc.startColumn_ && frag->ofs < loc.endColumn_)
+                        fragments.Push(frag);
+                }
+                else
+                {
+                    if (lcounter == loc.startLine_)
+                    {
+                        if (frag->ofs >= loc.startColumn_)
+                            fragments.Push(frag);
+
+                    }
+                    else if (lcounter == loc.endLine_)
+                    {
+                        if (frag->ofs < loc.endColumn_)
+                            fragments.Push(frag);
+
+                    }
+                    else
+                    {
+                        fragments.Push(frag);
+                    }
+                }
+            }
+        }
+
+    }
+
+    TBStyleEdit* styleEdit_;
+
+};
+
+
+}

+ 3 - 2
Source/AtomicJS/JSBind/JSBSymbol.cpp → Source/AtomicEditorWork/Javascript/JSASTVisitor.cpp

@@ -2,5 +2,6 @@
 // Please see LICENSE.md in repository root for license information
 // Please see LICENSE.md in repository root for license information
 // https://github.com/AtomicGameEngine/AtomicGameEngine
 // https://github.com/AtomicGameEngine/AtomicGameEngine
 
 
-#include <Atomic/Atomic.h>
-#include "JSBSymbol.h"
+#include "AtomicEditor.h"
+
+#include "JSASTVisitor.h"

+ 382 - 0
Source/AtomicEditorWork/Javascript/JSASTVisitor.h

@@ -0,0 +1,382 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include "JSAST.h"
+
+namespace AtomicEditor
+{
+
+class JSASTVisitor
+{
+public:
+
+    virtual JSASTProgram* visit(JSASTProgram *program) = 0;
+
+    virtual JSASTStatement* visit(JSASTBlockStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTExpressionStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTIfStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTVariableDeclaration *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTEmptyStatement *stmt) = 0;
+    virtual JSASTStatement* visit(JSASTForStatement *stmt) = 0;
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTConditionalExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTThisExpression* expr) = 0;
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr) = 0;
+    virtual JSASTExpression* visit(JSASTCallExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTMemberExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTLiteral* expr) = 0;
+    virtual JSASTExpression* visit(JSASTArrayExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTObjectExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTNewExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr) = 0;
+    virtual JSASTExpression* visit(JSASTVariableDeclarator* expr) = 0;    
+
+    virtual JSASTProperty* visit(JSASTProperty* property) = 0;
+    virtual JSASTStatement*  visit(JSASTLabeledStatement* property) = 0;
+
+    virtual JSASTComment* visit(JSASTComment* expr) = 0;
+
+};
+
+class JSASTTraversalVisitor : public JSASTVisitor
+{
+public:
+
+    virtual JSASTProgram* visit(JSASTProgram *program)
+    {
+        // this would cause recursion
+        // program->Accept(this);
+
+        unsigned count = program->GetCommentCount();
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTComment* cmt = program->GetComment(i);
+            if (cmt)
+                cmt->Accept(this);
+        }
+
+
+        count = program->GetStatementCount();
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTStatement* stmt = program->GetStatement(i);
+            if (stmt)
+                stmt->Accept(this);
+        }
+
+        return program;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTForStatement *stmt)
+    {
+        if (stmt->GetInitVariable())
+            stmt->GetInitVariable()->Accept(this);
+        if (stmt->GetInitExpression())
+            stmt->GetInitExpression()->Accept(this);
+
+        if (stmt->GetTest())
+            stmt->GetTest()->Accept(this);
+
+        if (stmt->GetUpdate())
+            stmt->GetUpdate()->Accept(this);
+
+        if (stmt->GetBody())
+            stmt->GetBody()->Accept(this);
+
+        return stmt;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTBlockStatement *block)
+    {
+        unsigned count = block->GetStatementCount();
+
+        for (unsigned i = 0; i < count; i++)
+        {
+            JSASTStatement* stmt = block->GetStatement(i);
+            if (stmt)
+                stmt->Accept(this);
+        }
+
+        return block;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTExpressionStatement *stmt)
+    {
+        if (stmt->GetExpression())
+            stmt->GetExpression()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTIfStatement *stmt)
+    {
+        if (stmt->GetTest())
+            stmt->GetTest()->Accept(this);
+        if (stmt->GetConsequent())
+            stmt->GetConsequent()->Accept(this);
+        if (stmt->GetAlternate())
+            stmt->GetAlternate()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTFunctionDeclaration *stmt)
+    {
+        if (stmt->GetID())
+            stmt->GetID()->Accept(this);
+
+        for (unsigned i = 0; i < stmt->GetParamsCount(); i++)
+            stmt->GetParam(i)->Accept(this);
+
+        JSASTBlockStatement* body = stmt->GetBodyStatement();
+
+        if (body)
+            body->Accept(this);
+
+        return stmt;
+
+    }
+
+    virtual JSASTStatement* visit(JSASTVariableDeclaration *stmt)
+    {
+        for (unsigned i = 0; i < stmt->GetDeclarationsCount(); i++)
+            stmt->GetDeclaration(i)->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTReturnStatement *stmt)
+    {
+        if (stmt->GetArgument())
+            stmt->GetArgument()->Accept(this);
+
+        return stmt;
+    }
+
+    virtual JSASTStatement* visit(JSASTEmptyStatement *stmt)
+    {
+        return stmt;
+    }
+
+    virtual JSASTExpression* visit(JSASTAssignmentExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTConditionalExpression *expr)
+    {
+        if (expr->GetTest())
+            expr->GetTest()->Accept(this);
+        if (expr->GetConsequent())
+            expr->GetConsequent()->Accept(this);
+        if (expr->GetAlternate())
+            expr->GetAlternate()->Accept(this);
+
+        return expr;
+    }
+
+
+    virtual JSASTExpression* visit(JSASTLogicalExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTIdentifier* expr)
+    {
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTCallExpression* expr)
+    {
+        if (expr->GetCallee())
+            expr->GetCallee()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetArgumentCount(); i++)
+        {   if (expr->GetArgument(i))
+                expr->GetArgument(i)->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTBinaryExpression* expr)
+    {
+        if (expr->GetLeft())
+            expr->GetLeft()->Accept(this);
+
+        if (expr->GetRight())
+            expr->GetRight()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTMemberExpression* expr)
+    {
+        if (expr->GetObject())
+            expr->GetObject()->Accept(this);
+
+        if (expr->GetProperty())
+            expr->GetProperty()->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTLiteral* expr)
+    {
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTArrayExpression* expr)
+    {
+        for (unsigned i = 0; i < expr->GetElementCount(); i++)
+        {
+            JSASTExpression* arrayElement = expr->GetElement(i);
+            if (arrayElement)
+                arrayElement->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTObjectExpression* expr)
+    {
+        for (unsigned i = 0; i < expr->GetPropertyCount(); i++)
+        {
+            JSASTProperty* property = expr->GetProperty(i);
+
+            if (property)
+                property->Accept(this);
+        }
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTFunctionExpression* expr)
+    {
+        if (expr->GetID())
+            expr->GetID()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetParamsCount(); i++)
+            expr->GetParam(i)->Accept(this);
+
+        JSASTBlockStatement* body = expr->GetBodyStatement();
+
+        if (body)
+            body->Accept(this);
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTNewExpression* expr)
+    {
+        if (expr->GetCallee())
+            expr->GetCallee()->Accept(this);
+
+        for (unsigned i = 0; i < expr->GetArgumentCount(); i++)
+        {
+            if (expr->GetArgument(i))
+                expr->GetArgument(i)->Accept(this);
+        }
+
+        return expr;
+
+    }
+
+    virtual JSASTExpression* visit(JSASTUnaryExpression* expr)
+    {
+        if (expr->GetArgument())
+            expr->GetArgument()->Accept(this);
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTUpdateExpression* expr)
+    {
+        if (expr->GetArgument())
+            expr->GetArgument()->Accept(this);
+
+        return expr;
+    }
+
+    virtual JSASTExpression* visit(JSASTVariableDeclarator* expr)
+    {
+        if (expr->GetID())
+            expr->GetID()->Accept(this);
+
+        if (expr->GetInit())
+            expr->GetInit()->Accept(this);
+
+
+        return expr;
+    }
+
+    virtual JSASTComment* visit(JSASTComment* comment)
+    {
+        return comment;
+    }
+
+    virtual JSASTProperty* visit(JSASTProperty* property)
+    {
+        if (property->GetKey())
+            property->GetKey()->Accept(this);
+        if (property->GetValue())
+            property->GetValue()->Accept(this);
+
+        return property;
+    }
+
+    virtual JSASTStatement* visit(JSASTLabeledStatement* labeledStatement)
+    {
+        if (labeledStatement->GetLabel())
+            labeledStatement->GetLabel()->Accept(this);
+        if (labeledStatement->GetBody())
+            labeledStatement->GetBody()->Accept(this);
+
+        return labeledStatement;
+    }
+
+
+    virtual JSASTExpression* visit(JSASTThisExpression* expr)
+    {
+        return expr;
+    }
+
+
+};
+
+
+
+}
+
+

+ 190 - 0
Source/AtomicEditorWork/Javascript/JSAutocomplete.cpp

@@ -0,0 +1,190 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/IO/Log.h>
+#include "JSAutocomplete.h"
+
+
+namespace AtomicEditor
+{
+
+JSAutocomplete::JSAutocomplete(TBEditField* editField) :
+    editField_(editField),
+    styleEdit_(editField->GetStyleEdit()),
+    autoList_(0)
+{
+    autoList_ = new TBSelectList();
+    autoList_->SetSource(&autoSource_);
+
+    autoList_->SetSize(150, 100);
+    autoList_->SetIgnoreInput(true);
+
+    editField->AddChild(autoList_);
+
+    Hide();
+}
+
+bool JSAutocomplete::UpdateCompletions(const String& value)
+{
+    if (value == currentValue_)
+        return autoSource_.GetNumItems() > 0;
+
+    currentValue_ = value;
+
+    String lstring(value[0]);
+    if (!locals_.Contains(lstring))
+    {
+        return false;
+    }
+
+    autoSource_.DeleteAllItems();
+
+    const List<String>& tokens = locals_[lstring];
+
+    for (List<String>::ConstIterator i = tokens.Begin(); i != tokens.End(); ++i)
+    {
+        if (*i != value && i->StartsWith(value))
+        {
+            autoSource_.AddItem(new TBGenericStringItem(i->CString()));
+        }
+
+    }
+
+    autoList_->SetValue(0);
+
+    return autoSource_.GetNumItems() > 0;
+
+}
+
+void JSAutocomplete::UpdateLocals()
+{
+    TBBlock *block = styleEdit_->blocks.GetFirst();
+    while (block)
+    {
+        for (TBTextFragment* frag = block->fragments.GetFirst(); frag; frag = frag->GetNext())
+        {
+            if (frag->len > 3)
+            {
+                String lstring(frag->Str()[0]);
+
+                String token(frag->Str(), frag->len);
+
+                if (!locals_[lstring].Contains(token))
+                {
+                    //LOGINFOF("Adding Token %s to locals", token.CString());
+                    locals_[lstring].Push(token);
+                }
+
+            }
+
+        }
+
+        block = block->GetNext();
+    }
+}
+
+void JSAutocomplete::SetPosition(const TBPoint &pos)
+{
+    autoList_->SetPosition(pos);
+}
+
+bool JSAutocomplete::OnEvent(const TBWidgetEvent &ev)
+{
+    if (ev.type == EVENT_TYPE_KEY_DOWN)
+    {
+        if (ev.special_key == TB_KEY_UP)
+        {
+            int v = autoList_->GetValue() - 1;
+            if (v < 0)
+            {
+                styleEdit_->autocomplete_visible = false;
+                autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+                autoList_->SetValue(0);
+                return styleEdit_->KeyDown(ev.key, ev.special_key, ev.modifierkeys);
+
+            }
+            else
+            {
+                autoList_->SetValue(v);
+            }
+        }
+        else if (ev.special_key == TB_KEY_DOWN)
+        {
+            int v = autoList_->GetValue() + 1;
+            if (v >= autoList_->GetSource()->GetNumItems())
+            {
+                styleEdit_->autocomplete_visible = false;
+                autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+                autoList_->SetValue(0);
+                return styleEdit_->KeyDown(ev.key, ev.special_key, ev.modifierkeys);
+            }
+            else
+            {
+                autoList_->SetValue(v);
+            }
+        }
+        else if (ev.special_key == TB_KEY_ENTER || ev.special_key == TB_KEY_TAB)
+        {
+            TBStr str = autoList_->GetSource()->GetItemString(autoList_->GetValue());
+
+            TBTextFragment* fragment = 0;
+            int ofs = styleEdit_->caret.pos.ofs;
+            if (ofs >= 0)
+            {
+                fragment = styleEdit_->caret.pos.block->FindFragment(ofs, true);
+                if (fragment)
+                {
+                    int gofs = fragment->GetGlobalOfs();
+                    styleEdit_->selection.Select(gofs, gofs + fragment->len);
+                    styleEdit_->InsertText(str.CStr(), str.Length());
+                }
+            }
+
+            styleEdit_->autocomplete_visible = false;
+            autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+            autoList_->SetValue(0);
+
+            return true;
+        }
+        else if (ev.special_key == TB_KEY_ESC)
+        {
+            styleEdit_->autocomplete_visible = false;
+            autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+            autoList_->SetValue(0);
+            return true;
+        }
+
+    }
+
+    return false;
+}
+
+JSAutocomplete::~JSAutocomplete()
+{
+    autoList_->SetSource(NULL);
+    autoSource_.DeleteAllItems();
+}
+
+void JSAutocomplete::Show()
+{
+    styleEdit_->autocomplete_visible = true;
+    autoList_->SetVisibilility(WIDGET_VISIBILITY_VISIBLE);
+    autoList_->SetValue(0);
+}
+
+void JSAutocomplete::Hide()
+{
+    styleEdit_->autocomplete_visible = false;
+    autoList_->SetVisibilility(WIDGET_VISIBILITY_INVISIBLE);
+    autoList_->SetValue(0);
+}
+
+bool JSAutocomplete::Visible()
+{
+    return autoList_->GetVisibility() == WIDGET_VISIBILITY_VISIBLE;
+}
+
+
+}

+ 56 - 0
Source/AtomicEditorWork/Javascript/JSAutocomplete.h

@@ -0,0 +1,56 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Atomic/Container/Str.h>
+#include <Atomic/Container/HashMap.h>
+#include <Atomic/Container/List.h>
+#include <Atomic/Math/StringHash.h>
+
+#include <TurboBadger/tb_select.h>
+#include <TurboBadger/tb_editfield.h>
+#include <TurboBadger/tb_style_edit.h>
+
+using namespace Atomic;
+
+using namespace tb;
+
+namespace AtomicEditor
+{
+
+// per document autocompletion
+class JSAutocomplete
+{
+public:
+
+    JSAutocomplete(TBEditField* editField);
+
+    ~JSAutocomplete();
+
+    void Show();
+    void Hide();
+    bool Visible();
+
+    void SetPosition(const TBPoint& pos);
+    bool UpdateCompletions(const String& value);
+
+    bool OnEvent(const TBWidgetEvent &ev);
+
+    void UpdateLocals();
+
+private:
+
+    HashMap<StringHash, List<String> > locals_;
+
+    TBEditField* editField_;
+    TBStyleEdit* styleEdit_;
+    TBSelectList* autoList_;
+    TBGenericStringItemSource autoSource_;
+    String currentValue_;
+
+};
+
+
+}

+ 26 - 0
Source/AtomicEditorWork/Javascript/JSErrorChecker.cpp

@@ -0,0 +1,26 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#include "AtomicEditor.h"
+#include <Atomic/Core/Context.h>
+#include "JSErrorChecker.h"
+
+namespace AtomicEditor
+{
+
+/// Construct.
+JSErrorChecker::JSErrorChecker(Context* context, duk_context *ctx) :
+    Object(context),
+    ctx_(ctx)
+{
+
+}
+
+/// Destruct.
+JSErrorChecker::~JSErrorChecker()
+{
+
+}
+
+}

+ 33 - 0
Source/AtomicEditorWork/Javascript/JSErrorChecker.h

@@ -0,0 +1,33 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#pragma once
+
+#include <Duktape/duktape.h>
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace AtomicEditor
+{
+
+class JSErrorChecker : public Object
+{
+    OBJECT(JSErrorChecker);
+
+public:
+
+    /// Construct.
+    JSErrorChecker(Context* context, duk_context* ctx);
+    /// Destruct.
+    ~JSErrorChecker();
+
+private:
+
+    duk_context* ctx_;
+
+};
+
+}

+ 303 - 0
Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.cpp

@@ -0,0 +1,303 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#ifdef USE_SPIDERMONKEY
+
+#include "AtomicEditor.h"
+#include "/Users/josh/Desktop/SpiderMonkey/include/mozjs-37a1/jsapi.h"
+#include "js/RootingAPI.h"
+#include "JSSpiderMonkeyVM.h"
+
+using namespace JS;
+
+#include "Context.h"
+#include "FileSystem.h"
+#include "ResourceCache.h"
+#include "JSFile.h"
+
+static RootedObject* __global = NULL;
+
+// The class of the global object.
+static JSClass globalClass = {
+    "global",
+    JSCLASS_GLOBAL_FLAGS,
+    JS_PropertyStub,
+    JS_DeletePropertyStub,
+    JS_PropertyStub,
+    JS_StrictPropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr, nullptr, nullptr, nullptr,
+    JS_GlobalObjectTraceHook
+};
+
+// The error reporter callback.
+void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
+    fprintf(stderr, "%s:%u:%s\n",
+            report->filename ? report->filename : "[no filename]",
+            (unsigned int) report->lineno,
+            message);
+}
+
+
+namespace AtomicEditor
+{
+
+
+
+WeakPtr<JSSpiderMonkeyVM> JSSpiderMonkeyVM::instance_;
+
+static bool
+print(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+    for (unsigned i = 0; i < args.length(); i++) {
+        JSString *str = JS::ToString(cx, args[i]);
+        if (!str)
+            return false;
+        char *bytes = JS_EncodeString(cx, str);
+        if (!bytes)
+            return false;
+        printf("%s%s", i ? " " : "", bytes);
+        JS_free(cx, bytes);
+    }
+
+    putchar('\n');
+    fflush(stdout);
+    args.rval().setUndefined();
+    return true;
+}
+
+JSSpiderMonkeyVM::JSSpiderMonkeyVM(Context* context) :
+    Object(context), runtime_(0), jscontext_(0),
+    autorequest_(0), autocompartment_(0)
+{
+    context_->RegisterSubsystem(this);
+    instance_ = this;
+
+    // Initialize the JS engine.
+    if (!JS_Init())
+    {
+        assert(0);
+    }
+
+    // Create a JS runtime.
+    runtime_ = JS_NewRuntime(32L * 1024L * 1024L);
+
+    if (runtime_)
+    {
+        jscontext_ = JS_NewContext(runtime_, 8192);
+        if (jscontext_)
+        {
+            JS_SetErrorReporter(runtime_, reportError);
+        }
+        else
+        {
+            assert(0);
+        }
+
+    }
+    else
+    {
+        assert(0);
+    }
+
+    // Enter a request before running anything in the context.
+    autorequest_ = new JSAutoRequest(jscontext_);
+
+    // Create the global object and a new compartment.
+    __global = new RootedObject(jscontext_);
+    *__global = JS_NewGlobalObject(jscontext_, &globalClass, nullptr,
+                                   JS::DontFireOnNewGlobalHook);
+    // Enter the new global object's compartment.
+    autocompartment_ = new JSAutoCompartment(jscontext_, *__global);
+
+    // Populate the global object with the standard globals, like Object and
+    // Array.
+    if (!JS_InitStandardClasses(jscontext_, *__global))
+    {
+        assert(0);
+    }
+
+    if (!JS_InitReflect(jscontext_, *__global))
+    {
+        assert(0);
+    }
+
+    JS_DefineFunction(jscontext_, *__global, "print", (JSNative) print, 0, 0);
+
+    ExecuteFile("AtomicEditor/javascript/modules/acorn_spidermonkey.js");
+
+}
+
+JSSpiderMonkeyVM::~JSSpiderMonkeyVM()
+{
+    instance_ = NULL;
+
+    if (autocompartment_)
+        delete autocompartment_;
+
+    if (__global)
+    {
+        delete __global;
+        __global = NULL;
+    }
+
+    if (autorequest_)
+        delete autorequest_;
+
+    // Shut everything down.
+    if (context_)
+        JS_DestroyContext(jscontext_);
+    if (runtime_)
+        JS_DestroyRuntime(runtime_);
+
+    JS_ShutDown();
+
+}
+
+bool JSSpiderMonkeyVM::ExecuteFile(const String& path)
+{
+    SharedPtr<JSFile> jsfile;
+    jsfile = GetSubsystem<ResourceCache>()->GetResource<JSFile>(path);
+
+    JS::RootedValue returnValue(jscontext_);
+
+    OwningCompileOptions options(jscontext_);
+
+    options.setFileAndLine(jscontext_, path.CString(), 0);
+    options.setCompileAndGo(true);
+    bool ok = JS::Evaluate(jscontext_, *__global, options,
+                           jsfile->GetSource(), strlen(jsfile->GetSource()), &returnValue);
+
+    return ok;
+
+}
+
+bool JSSpiderMonkeyVM::ParseJavascriptToJSON(const char* source, String& json)
+{
+    json.Clear();
+
+    bool ok = true;
+
+    JS::Rooted<JS::Value> v(jscontext_);
+    JS::RootedValue returnValue(jscontext_);
+    JS::RootedValue jsource(jscontext_, JS::StringValue(JS_NewStringCopyZ(jscontext_, source)));
+    JS_CallFunctionName(jscontext_, *__global, "__atomic_parse_to_json", HandleValueArray(jsource), &returnValue);
+
+    JS::RootedString hm(jscontext_);
+    hm = JS::ToString(jscontext_, returnValue);
+
+    const char* strjson = JS_EncodeStringToUTF8(jscontext_, hm);
+
+    json = strjson;
+
+    //printf("%s\n", strjson);
+
+    JS_free(jscontext_, (void *) strjson);
+
+    return ok;
+
+}
+
+
+bool JSSpiderMonkeyVM::ParseJavascriptToJSONWithSpiderMonkeyReflectAPI(const char* source, String& json)
+{
+    json.Clear();
+
+    bool ok = true;
+
+    JS::Rooted<JS::Value> v(jscontext_);
+    JS::RootedValue returnValue(jscontext_);
+    JS::RootedValue jsource(jscontext_, JS::StringValue(JS_NewStringCopyZ(jscontext_, source)));
+
+    // get the Reflect object
+    if (!JS_GetProperty(jscontext_, *__global, "Reflect", &v))
+        return false;
+
+    JS::RootedObject reflectObject(jscontext_);
+    JS_ValueToObject(jscontext_, v, &reflectObject);
+
+    JS_CallFunctionName(jscontext_, reflectObject, "parse", HandleValueArray(jsource), &returnValue);
+
+    JS::RootedObject jsonObject(jscontext_);
+    if (!JS_GetProperty(jscontext_, *__global, "JSON", &v))
+        return false;
+
+    JS_ValueToObject(jscontext_, v, &jsonObject);
+
+    // pretty printed, can remove for speed
+    JS::AutoValueArray<3> args(jscontext_);
+    args[0].set(returnValue);
+    args[1].set(JS::NullValue());
+    args[2].setNumber(3.0);
+
+    JS::RootedValue jjson(jscontext_);
+    JS_CallFunctionName(jscontext_, jsonObject, "stringify", HandleValueArray(args), &jjson);
+
+    JS::RootedString hm(jscontext_);
+    hm = JS::ToString(jscontext_, jjson);
+
+    const char* strjson = JS_EncodeStringToUTF8(jscontext_, hm);
+
+    json = strjson;
+
+    //printf("%s\n", strjson);
+
+    JS_free(jscontext_, (void *) strjson);
+
+    return ok;
+
+}
+
+bool JSSpiderMonkeyVM::ReadZeroTerminatedSourceFile(const String& path, String& source)
+{
+    File file(instance_->GetContext());
+    file.Open(path);
+
+    unsigned size = file.GetSize();
+
+    SharedArrayPtr<char> data;
+    data = new char[size + 1];
+    data[size] = '\0';
+    file.Read(data, size);
+
+    source = data;
+
+    return true;
+
+}
+
+
+}
+
+int run(JSContext *cx) {
+    // Enter a request before running anything in the context.
+    JSAutoRequest ar(cx);
+
+    // Create the global object and a new compartment.
+    RootedObject global(cx);
+    global = JS_NewGlobalObject(cx, &globalClass, nullptr,
+                                JS::DontFireOnNewGlobalHook);
+    if (!global)
+        return 1;
+
+    // Enter the new global object's compartment.
+    JSAutoCompartment ac(cx, global);
+
+    // Populate the global object with the standard globals, like Object and
+    // Array.
+    if (!JS_InitStandardClasses(cx, global))
+        return 1;
+
+    // Your application code here. This may include JSAPI calls to create your
+    // own custom JS objects and run scripts.
+
+    return 0;
+}
+
+#endif
+

+ 56 - 0
Source/AtomicEditorWork/Javascript/JSSpiderMonkeyVM.h

@@ -0,0 +1,56 @@
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+// Please see LICENSE.md in repository root for license information
+// https://github.com/AtomicGameEngine/AtomicGameEngine
+
+#ifdef USE_SPIDERMONKEY
+#pragma once
+
+#include "Object.h"
+
+using namespace Atomic;
+
+struct JSRuntime;
+struct JSContext;
+class JSAutoRequest;
+class JSAutoCompartment;
+
+namespace AtomicEditor
+{
+
+// Notes: SpiderMonkey Reflect API is giving some bad loc's, which seems to be an issue with the
+// C++ generator not passing in tokens for some nodes (MemberExpression.property for example)
+// probably should use http://esprima.org/
+class JSSpiderMonkeyVM : public Object
+{
+    OBJECT(JSSpiderMonkeyVM);
+
+public:
+    /// Construct.
+    JSSpiderMonkeyVM(Context* context);
+    /// Destruct.
+    ~JSSpiderMonkeyVM();
+
+    bool ExecuteFile(const String& path);
+
+    bool ParseJavascriptToJSON(const char* source, String& json);
+
+    bool ParseJavascriptToJSONWithSpiderMonkeyReflectAPI(const char* source, String& json);
+
+private:
+
+    bool ReadZeroTerminatedSourceFile(const String& path, String& source);
+
+    // for access within duktape callbacks
+    static WeakPtr<JSSpiderMonkeyVM> instance_;
+
+    JSRuntime* runtime_;
+    JSContext* jscontext_;
+    JSAutoRequest* autorequest_;
+    JSAutoCompartment* autocompartment_;
+
+};
+
+
+}
+
+#endif

Some files were not shown because too many files changed in this diff