@@ -112,6 +114,10 @@ class ScenePreview extends Scene {
}
function listRenderProps() : Array<{name: String, value: String}> {
+ return listRenderPropsStatic(config);
+ }
+
+ static public function listRenderPropsStatic(config: hide.Config) : Array<{name: String, value: String}> {
var renderProps = config.getLocal("scene.renderProps");
var ret : Array<{name: String, value: String}> = [];
diff --git a/hide/tools/FileManager.hx b/hide/tools/FileManager.hx
new file mode 100644
index 000000000..4dbf0c4a2
--- /dev/null
+++ b/hide/tools/FileManager.hx
@@ -0,0 +1,212 @@
+package hide.tools;
+
+
+enum abstract GenToManagerCommand(String) {
+ var success;
+}
+
+enum abstract ManagerToGenCommand(String) {
+ var queue;
+ var prio;
+ var clear;
+}
+
+typedef GenToManagerSuccessMessage = {
+ var originalPath : String;
+ var thumbnailPath : String;
+}
+
+typedef FileData = {
+ name: String,
+ parent: FileData,
+}
+
+typedef MiniatureReadyCallback = (miniaturePath: String) -> Void;
+
+/**
+ Class that handle parsing and maintaining the state of the project files, and generate miniatures for them on demand
+**/
+class FileManager {
+
+ public static final thumbnailGeneratorPort = 9669;
+ public static final thumbnailGeneratorUrl = "localhost";
+
+ public static var inst(get, default) : FileManager;
+
+ var generatorWindow : nw.Window;
+
+ var onReadyCallbacks : Map
= [];
+
+ var serverSocket : hxd.net.Socket = null;
+ var generatorSocket : hxd.net.Socket = null;
+
+ static function get_inst() {
+ if (inst == null) {
+ inst = new FileManager();
+ }
+ return inst;
+ }
+
+ public static function onBeforeReload() {
+ if (inst != null) {
+ inst.cleanupGenerator();
+ }
+ }
+
+ var reloadQueued = false;
+
+ function queueReload() {
+ if (reloadQueued == false) {
+ reloadQueued = true;
+ haxe.Timer.delay(setupGenerator, 5000);
+ }
+ }
+
+ function setupGenerator() {
+ reloadQueued = false;
+ serverSocket = new hxd.net.Socket();
+ serverSocket.onError = (msg) -> {
+ hide.Ide.inst.quickError("FileManager socket error : " + msg);
+ cleanupGenerator();
+ queueReload();
+ }
+ serverSocket.bind(thumbnailGeneratorUrl, thumbnailGeneratorPort, (remoteSocket) -> {
+ if (generatorSocket != null) {
+ generatorSocket.close();
+ }
+ generatorSocket = remoteSocket;
+ generatorSocket.onError = (msg) -> {
+ hide.Ide.inst.quickError("Generator socket error : " + msg);
+ cleanupGenerator();
+ queueReload();
+ }
+
+ var handler = new hide.tools.ThumbnailGenerator.MessageHandler(generatorSocket, processThumbnailGeneratorMessage);
+
+ // resend command that weren't completed
+ for (path => _ in onReadyCallbacks) {
+ sendGenerateCommand(path);
+ }
+ });
+
+ nw.Window.open('app.html?thumbnail=true', cast {
+ new_instance: true,
+ show: false,
+ title: "HideThumbnailGenerator"
+ }, (win: nw.Window) -> {
+ generatorWindow = win;
+ win.on("close", () -> {
+
+ cleanupGenerator();
+ });
+ });
+ }
+
+ function cleanupGenerator() {
+ if (generatorSocket != null) {
+ generatorSocket.close();
+ generatorSocket = null;
+ }
+
+ if (serverSocket != null) {
+ serverSocket.close();
+ serverSocket = null;
+ }
+
+ if (generatorWindow != null) {
+ generatorWindow.close(true);
+ generatorWindow = null;
+ }
+ untyped nw.Window.getAll((win:nw.Window) -> {
+ if (win.title == "HideThumbnailGenerator") {
+ win.close(true);
+ }
+ });
+ }
+
+ function new() {
+ setupGenerator();
+ }
+
+ function processThumbnailGeneratorMessage(message: String) {
+ try {
+ var message = haxe.Json.parse(message);
+ switch(message.type) {
+ case success:
+ var message : GenToManagerSuccessMessage = message.data;
+ var cb = onReadyCallbacks.get(message.originalPath);
+ if (cb == null) {
+ return;
+ //throw "Generated a thumbnail for a file not registered";
+ }
+ cb(message.thumbnailPath);
+ onReadyCallbacks.remove(message.originalPath);
+ default:
+ throw "Unknown message type " + message.type;
+ }
+ } catch(e) {
+ hide.Ide.inst.quickError("Thumb Generator invalid message : " + e + "\n" + message);
+ }
+ }
+
+ var queued = false;
+
+ /**
+ Asyncrhonusly generates a miniature.
+ onReady is called back with the path of the loaded miniature, or null if the miniature couldn't be loaded
+ **/
+ public function renderMiniature(path: String, onReady: MiniatureReadyCallback) {
+ var ext = path.split(".").pop();
+ switch(ext) {
+ case "prefab" | "fbx" | "l3d" | "fx" | "shgraph" | "jpg" | "jpeg" | "png":
+ if (!onReadyCallbacks.exists(path)) {
+ onReadyCallbacks.set(path, onReady);
+ sendGenerateCommand(path);
+ }
+ default:
+ onReady(null);
+ }
+ }
+
+ public function clearRenderQueue() {
+ onReadyCallbacks.clear();
+ if (generatorSocket == null) {
+ return;
+ }
+ var message = {
+ type: ManagerToGenCommand.clear,
+ };
+ var cmd = haxe.Json.stringify(message) + "\n";
+ generatorSocket.out.writeString(cmd);
+ }
+
+ public function setPriority(path: String, newPriority: Int) {
+ if (!onReadyCallbacks.exists(path)) {
+ return;
+ }
+ if (generatorSocket == null) {
+ return;
+ }
+ var message = {
+ type: ManagerToGenCommand.prio,
+ path: path,
+ prio: newPriority
+ };
+ var cmd = haxe.Json.stringify(message) + "\n";
+ generatorSocket.out.writeString(cmd);
+ }
+
+ function sendGenerateCommand(path: String) {
+ if (generatorSocket == null) {
+ return;
+ }
+ var message = {
+ type: ManagerToGenCommand.queue,
+ path: path,
+ };
+ var cmd = haxe.Json.stringify(message) + "\n";
+ generatorSocket.out.writeString(cmd);
+ }
+
+
+}
\ No newline at end of file
diff --git a/hide/tools/ThumbnailGenerator.hx b/hide/tools/ThumbnailGenerator.hx
new file mode 100644
index 000000000..e35fc71e6
--- /dev/null
+++ b/hide/tools/ThumbnailGenerator.hx
@@ -0,0 +1,414 @@
+package hide.tools;
+
+typedef RenderInfo = {path: String, cb: hide.tools.FileManager.MiniatureReadyCallback, priority: Int};
+
+/**
+ Handle recieving messages separated by `\n` characters by a socket, correctly buffering the data
+**/
+class MessageHandler {
+ var socket: hxd.net.Socket;
+ var bufferedData : haxe.io.Bytes;
+ var bufferSize = 0;
+ static final maxBufferSize = 16384;
+
+ public function new(socket: hxd.net.Socket, callback: (content: String) -> Void) {
+ this.socket = socket;
+ bufferedData = haxe.io.Bytes.alloc(maxBufferSize);
+ bufferSize = 0;
+
+ socket.onData = () -> {
+ while(socket.input.available > 0) {
+ var read = hxd.Math.imin(maxBufferSize - bufferSize, socket.input.available);
+ if (read == 0) {
+ throw "message too long";
+ }
+
+ socket.input.readFullBytes(bufferedData, bufferSize, read);
+ bufferSize += read;
+
+ var last = 0;
+ var pos = 0;
+
+ // split on newLines
+ while(pos < bufferSize) {
+ if (bufferedData.get(pos) == 10) {
+ var command = bufferedData.getString(last, pos-last);
+ callback(command);
+ last = pos+1;
+ }
+ pos ++;
+ }
+
+ if (last > 0) {
+ var remaining = bufferSize - last;
+ if (remaining > 0) {
+ bufferedData.blit(0, bufferedData, last, remaining);
+ bufferSize = remaining;
+ } else {
+ bufferSize = 0;
+ }
+ } else if (bufferSize == maxBufferSize) {
+ throw "message too long";
+ }
+ }
+ }
+ }
+
+}
+
+@:access(hide.tools.FileManager)
+class ThumbnailGenerator {
+ var miniaturesToRender : Array = [];
+ var prioDirty = false;
+ var renderCanvas : hide.comp.Scene;
+ var renderTexture : h3d.mat.Texture;
+ final renderRes = 512;
+
+ var sceneRoot : h3d.scene.Object;
+
+ var socket : hxd.net.Socket = null;
+ var ready : Bool = false;
+
+ static final debugBypassCache = false;
+ static final debugShowWindow = false;
+
+ function sendSuccess(originalPath: String, finalPath: String) {
+ var message = {
+ type: hide.tools.FileManager.GenToManagerCommand.success,
+ data: ({
+ originalPath: originalPath,
+ thumbnailPath: finalPath,
+ }:hide.tools.FileManager.GenToManagerSuccessMessage)
+ };
+ var serialized = haxe.Json.stringify(message);
+ socket.out.writeString(serialized + "\n");
+ }
+
+ var bufferedData : haxe.io.Bytes;
+ var bufferSize = 0;
+ static final maxBufferSize = 16384;
+
+
+
+ function new() {
+ if (debugShowWindow) {
+ nw.Window.get().show(true);
+ } else {
+ untyped nw.Window.get().hide();
+ }
+ nw.Window.get().resizeTo(128,128);
+
+ bufferedData = haxe.io.Bytes.alloc(maxBufferSize);
+
+ socket = new hxd.net.Socket();
+
+ // Destroy the generator if any error occurs
+ socket.onError = (msg) -> {
+ nw.Window.get().close(true);
+ }
+
+ var handler = new MessageHandler(socket, handleCommand);
+
+ socket.connect(hide.tools.FileManager.thumbnailGeneratorUrl, hide.tools.FileManager.thumbnailGeneratorPort, () -> {
+ });
+
+ var cont = new Element('').appendTo(js.Browser.document.body);
+ renderCanvas = new hide.comp.Scene(hide.Ide.inst.currentConfig, cont, null);
+ renderCanvas.enableNewErrorSystem = true;
+ renderCanvas.errorHandler = (e) -> {
+ // do nothing;
+ return null;
+ }
+ renderCanvas.autoUpdate = false;
+ renderCanvas.onReady = () -> {
+ renderCanvas.engine.setCurrent();
+
+ renderCanvas.s3d.removeChildren();
+ renderCanvas.s2d.removeChildren();
+
+ renderTexture = new h3d.mat.Texture(renderRes,renderRes, [Target]);
+
+ sceneRoot = new h3d.scene.Object(renderCanvas.s3d);
+
+ renderCanvas.errorHandler = (e) -> null;
+
+ var renderPropsList = hide.comp.ScenePreview.listRenderPropsStatic(hide.Ide.inst.config.current);
+ if (renderPropsList.length > 0) {
+ renderCanvas.setRenderProps(renderPropsList[0].value);
+ }
+
+
+ haxe.Timer.delay(() -> {
+ this.ready = true;
+ }, 100);
+ };
+ }
+
+ function handleCommand(command: String) {
+ var message : Dynamic = {};
+ try {
+ message = haxe.Json.parse(command);
+ } catch (e) {
+ return;
+ }
+ switch((message.type:FileManager.ManagerToGenCommand)) {
+ case queue:
+ var thumbPath = getThumbPath(message.path).toString();
+
+ var shouldGenerate = true;
+ if (!debugBypassCache && sys.FileSystem.exists(thumbPath)) {
+ var thumbStat = sys.FileSystem.stat(thumbPath);
+ var fileStat = sys.FileSystem.stat(message.path);
+ if (thumbStat.mtime.getTime() > fileStat.mtime.getTime()) {
+ shouldGenerate = false;
+ sendSuccess(message.path, thumbPath);
+ }
+ }
+
+ if (shouldGenerate) {
+ renderMiniature(message.path, sendSuccess.bind(message.path));
+ }
+ case clear:
+ miniaturesToRender = [];
+ case prio:
+ var toSet = Lambda.find(miniaturesToRender, (m) -> m.path == message.path);
+ if (toSet != null) {
+ toSet.priority = message.prio;
+ prioDirty = true;
+ }
+ }
+ }
+
+ var queued = false;
+
+ /**
+ Asynchronously generates a miniature.
+ onReady is called back with the path of the loaded miniature, or null if the miniature couldn't be loaded
+ **/
+ public function renderMiniature(path: String, onReady: hide.tools.FileManager.MiniatureReadyCallback) {
+ miniaturesToRender.push({path: path, cb: onReady, priority: 0});
+ if (!queued) {
+ haxe.Timer.delay(processMiniature, 1);
+ }
+ }
+
+ public static final thumbRoot = ".tmp/";
+ public static final thumbExt = "thumb.jpg";
+
+ static public function getThumbPath(basePath: String) : haxe.io.Path {
+ basePath = StringTools.replace(basePath, hide.Ide.inst.resourceDir, "");
+ var path = new haxe.io.Path(haxe.io.Path.join([hide.Ide.inst.resourceDir, thumbRoot, basePath]));
+ path.ext = thumbExt;
+ return path;
+ }
+
+ function handleModel(toRender: RenderInfo) {
+ renderCanvas.engine.setCurrent();
+
+ sceneRoot.removeChildren();
+
+ var engine = renderCanvas.engine;
+
+ var ctx = new hide.prefab.ContextShared(null, sceneRoot);
+ ctx.scene = renderCanvas;
+
+ var ext = toRender.path.split(".").pop();
+
+ var abort = false;
+ if (ext == "fbx") {
+ var model = new hrt.prefab.Model(null, null);
+ model.source = toRender.path;
+ model.make(ctx);
+ } else if (ext == "prefab" || ext == "l3d" || ext == "fx") {
+ try {
+ var ref = new hrt.prefab.Reference(null, null);
+ var cut = StringTools.replace(toRender.path, hide.Ide.inst.resourceDir + "/", "");
+ ref.source = cut;
+
+ var prefab = ref.make(ctx);
+
+ if (ext == "fx") {
+ var fx = prefab.find(hrt.prefab.fx.FX, true, false);
+ if (fx != null) {
+ var fxAnim = Std.downcast(fx.local3d, hrt.prefab.fx.FX.FXAnimation);
+ // Forward the animations a little bit to show something more usefull
+ if (fxAnim != null) {
+ var duration = fxAnim.duration;
+ fxAnim.setTime(duration * 0.25);
+ }
+ }
+ }
+
+ } catch (e) {
+ hide.Ide.inst.quickError('miniature render fail for ${toRender.path} : $e');
+ abort = true;
+ }
+ } else if (ext == "shgraph") {
+ try {
+ var spherePrim = new h3d.prim.Sphere(1.0, 32, 32, 1);
+ spherePrim.addNormals();
+ spherePrim.addUVs();
+ spherePrim.addTangents();
+
+ var sphere = new h3d.scene.Mesh(spherePrim, sceneRoot);
+
+ var shgraph = new hrt.prefab.DynamicShader(null, null);
+ var cut = StringTools.replace(toRender.path, hide.Ide.inst.resourceDir + "/", "");
+ shgraph.source = cut;
+ ctx = new hide.prefab.ContextShared(null, sphere);
+ shgraph.makeShader();
+ for (m in sphere.getMaterials()) {
+ @:privateAccess shgraph.applyShader(sphere, m, shgraph.shader);
+ }
+ } catch(e) {
+ hide.Ide.inst.quickError('miniature render fail for ${toRender.path} : $e');
+ abort = true;
+ }
+ }
+ if (!abort) {
+ try {
+
+ renderCanvas.resetCamera(sceneRoot, 0.85, 32.0);
+
+ renderTexture.clear(0,0);
+
+ @:privateAccess renderCanvas.doSync();
+
+ engine.pushTarget(renderTexture);
+ engine.clear();
+ renderCanvas.s3d.render(engine);
+ engine.popTarget();
+
+ sceneRoot.removeChildren();
+
+ var path = convertAndWriteThumbnail(toRender.path, renderTexture);
+ toRender.cb(path);
+ }
+ catch (e) {
+ hide.Ide.inst.quickError('miniature render fail for ${toRender.path} : $e');
+ toRender.cb(null);
+ }
+ } else {
+ toRender.cb(null);
+ }
+ }
+
+ function convertAndWriteThumbnail(basePath: String, texture: h3d.mat.Texture) {
+ var path = getThumbPath(basePath).toString();
+ path = StringTools.replace(path, "\\", "/");
+
+ var dir = path.split("/");
+ dir.pop();
+ var dirPath = dir.join("/") + "/";
+ if(!sys.FileSystem.isDirectory( hide.Ide.inst.getPath(dirPath)))
+ sys.FileSystem.createDirectory( hide.Ide.inst.getPath(dirPath));
+
+ var pixels = texture.capturePixels();
+ pixels.convert(ARGB);
+ //sys.io.File.saveBytes(path, renderTexture.capturePixels().toPNG());
+ var bytes = new haxe.io.BytesOutput();
+ var writer = new format.jpg.Writer(bytes);
+ writer.write({
+ width: texture.width,
+ height: texture.height,
+ pixels: pixels.bytes,
+ quality: 50
+ });
+
+ sys.io.File.saveBytes(path, bytes.getBytes());
+ return path;
+ }
+
+ function handleTexture(toRender: RenderInfo) {
+ renderCanvas.engine.setCurrent();
+
+ var cut = StringTools.replace(toRender.path, hide.Ide.inst.resourceDir + "/", "");
+ var img = hxd.res.Loader.currentInstance.load(cut).toTexture();
+ var width = img.width;
+ var height = img.height;
+
+ final size = 512;
+
+ if (width > height) {
+ height = hxd.Math.floor(height / width * size);
+ width = size;
+ } else if (width < height) {
+ width = hxd.Math.floor(width / height * size);
+ height = size;
+ } else {
+ width = size;
+ height = size;
+ }
+
+ renderCanvas.s2d.removeChildren();
+
+ var bg = new h2d.Bitmap(h2d.Tile.fromColor(0), renderCanvas.s2d);
+ bg.width = size;
+ bg.height = size;
+
+ var bmp = new h2d.Bitmap(h2d.Tile.fromTexture(img), renderCanvas.s2d);
+ bmp.width = width;
+ bmp.height = height;
+ bmp.x = (size - width) / 2;
+ bmp.y = (size - height) / 2;
+
+ bmp.blendMode = None;
+
+ var shader = new hide.view.GraphEditor.PreviewShaderAlpha();
+ bmp.addShader(shader);
+
+
+ var engine = renderCanvas.engine;
+
+ engine.pushTarget(renderTexture);
+ engine.clear();
+ renderCanvas.render(engine);
+ engine.popTarget();
+
+ renderCanvas.s2d.removeChildren();
+
+ var path = convertAndWriteThumbnail(toRender.path, renderTexture);
+
+ // restore renderTexture original size
+ renderTexture.resize(512, 512);
+
+ toRender.cb(path);
+ }
+
+ function processMiniature() {
+ if (!ready || renderCanvas.s3d == null) {
+ haxe.Timer.delay(processMiniature, 1);
+ return;
+ }
+
+ queued = false;
+
+ if (miniaturesToRender.length == 0) {
+ return;
+ }
+
+ if (prioDirty) {
+ miniaturesToRender.sort((a, b) -> Reflect.compare(a.priority, b.priority));
+ prioDirty = false;
+ }
+
+ var toRender = miniaturesToRender.pop();
+
+ var ext = toRender.path.split(".").pop();
+ switch(ext) {
+ case "prefab" | "fbx" | "l3d" | "fx" | "shgraph":
+ handleModel(toRender);
+ case "jpg" | "jpeg" | "png":
+ handleTexture(toRender);
+ default:
+ toRender.cb(null);
+ }
+
+
+ if (miniaturesToRender.length > 0) {
+ haxe.Timer.delay(processMiniature, 1);
+ return;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/hide/ui/Keys.hx b/hide/ui/Keys.hx
index 67fd75b51..ce49c0b6f 100644
--- a/hide/ui/Keys.hx
+++ b/hide/ui/Keys.hx
@@ -77,6 +77,25 @@ class Keys {
return false;
}
+ static public function matchJsEvent(shortcutName : String, event: js.html.KeyboardEvent, config : Config) {
+ var keyCode : String = config.get("key."+shortcutName);
+
+ var split = keyCode.split("-");
+ for (part in split) {
+ switch (part) {
+ case "Shift":
+ if(!event.shiftKey) return false;
+ case "Ctrl":
+ if(!event.ctrlKey) return false;
+ case "Alt":
+ if(!event.altKey) return false;
+ default:
+ if(hxd.Key.getKeyName(event.keyCode) != part) return false;
+ }
+ }
+ return true;
+ }
+
public function triggerKey( e : Element.Event, key : String, config : Config ) {
for( l in listeners )
if( l(e) )
diff --git a/hide/ui/View.hx b/hide/ui/View.hx
index 2d1914d32..55f61333d 100644
--- a/hide/ui/View.hx
+++ b/hide/ui/View.hx
@@ -1,13 +1,14 @@
package hide.ui;
-enum DisplayPosition {
- Left;
- Center;
- Right;
- Bottom;
+enum abstract DisplayPosition(String) from String to String {
+ var Left = "content_left";
+ var Center = "content_center";
+ var Right = "content_right";
+ var Bottom = "content_bottom";
+ var MiddleColumnInternal = "content_middle_internal";
}
-typedef ViewOptions = { ?position : DisplayPosition, ?width : Int }
+typedef ViewOptions = { ?position : DisplayPosition, ?width : Int, ?id: String }
@:keepSub @:allow(hide.Ide)
class View extends hide.comp.Component {
diff --git a/hide/view/FileBrowser.hx b/hide/view/FileBrowser.hx
new file mode 100644
index 000000000..3efbd99b1
--- /dev/null
+++ b/hide/view/FileBrowser.hx
@@ -0,0 +1,313 @@
+package hide.view;
+
+typedef FileBrowserState = {
+
+}
+
+enum FileKind {
+ Dir;
+ File;
+}
+
+typedef FileEntry = {
+ name: String,
+ children: Array,
+ kind: FileKind,
+ parent: FileEntry,
+ iconPath: String,
+}
+
+class FileBrowser extends hide.ui.View {
+
+ var fileTree: Element;
+ var fileIcons: Element;
+
+ var root : FileEntry;
+
+ override function new(state) {
+ super(state);
+ }
+
+ override function onDragDrop(items:Array, isDrop:Bool, event:js.html.DragEvent):Bool {
+ return false;
+ }
+
+ function populateChildren(file: FileEntry) {
+ var fullPath = getFileEntryPath(file);
+ var paths = js.node.Fs.readdirSync(fullPath);
+ file.children = [];
+ for (path in paths) {
+ if (StringTools.startsWith(path, "."))
+ continue;
+ var info = js.node.Fs.statSync(fullPath + "/" + path);
+ file.children.push({
+ name: path,
+ kind: info.isDirectory() ? Dir : File,
+ parent: file,
+ children: null,
+ iconPath: null,
+ });
+ }
+
+ file.children.sort(compareFile);
+ }
+
+ // sort directories before files, and then dirs and files alphabetically
+ function compareFile(a: FileEntry, b: FileEntry) {
+ if (a.kind != b.kind) {
+ if (a.kind == Dir) {
+ return -1;
+ }
+ return 1;
+ }
+ return Reflect.compare(a.name, b.name);
+ }
+
+ function getFileEntryPath(file: FileEntry) {
+ if (file.parent == null) return ide.resourceDir;
+ return getFileEntryPath(file.parent) + "/" + file.name;
+ }
+
+ public static final dragKey = "application/x.filemove";
+
+ var currentFolder : FileEntry;
+ var currentSearch = [];
+ var searchString: String = "";
+ var fancyGallery : hide.comp.FancyGallery;
+ var fancyTree: hide.comp.FancyTree;
+
+
+ function onSearch() {
+ hide.tools.FileManager.inst.clearRenderQueue();
+ currentSearch = [];
+ if (searchString.length == 0) {
+ currentSearch = currentFolder.children;
+ } else {
+ function rec(files: Array) {
+ for (file in files) {
+ if (file.kind == Dir) {
+ rec(file.children);
+ }
+ else {
+ var range = hide.comp.FancySearch.computeSearchRanges(file.name, searchString);
+ if (range != null) {
+ currentSearch.push(file);
+ }
+ }
+ }
+ }
+
+ rec(currentFolder.children);
+ }
+
+ for (i => _ in currentSearch) {
+ var child = currentSearch[currentSearch.length - i - 1];
+ if ((child.iconPath == null || child.iconPath == "loading") && child.kind == File) {
+ child.iconPath = "loading";
+ hide.tools.FileManager.inst.renderMiniature(getFileEntryPath(child), (path: String) -> {child.iconPath = path; fancyGallery.queueRefresh();} );
+ }
+ }
+
+ fancyGallery.queueRefresh(Items);
+ fancyGallery.queueRefresh(RegenHeader);
+ }
+
+ override function onDisplay() {
+ root = {
+ name: "res",
+ kind: Dir,
+ children: null,
+ parent: null,
+ iconPath: null,
+ };
+
+ populateChildren(root);
+
+ var layout = new Element('
+
+
+
+
+
+
+
+ ').appendTo(element);
+
+ var resize = new hide.comp.ResizablePanel(Horizontal, layout.find(".left"), After);
+
+ var search = new hide.comp.FancySearch(null, layout.find(".fb-search"));
+ search.onSearch = (string, _) -> {
+ searchString = string;
+ onSearch();
+ };
+
+ var btnParent = layout.find(".btn-parent");
+ btnParent.get(0).onclick = (e: js.html.MouseEvent) -> {
+ if (currentFolder.parent != null) {
+ currentFolder = currentFolder.parent;
+ onSearch();
+ }
+ }
+
+ fancyTree = new hide.comp.FancyTree(resize.element);
+ fancyTree.saveDisplayKey = "fileBrowserTree";
+ fancyTree.getChildren = (file: FileEntry) -> {
+ if (file == null)
+ return [root];
+ if (file.kind == File)
+ return null;
+ if (file.children == null)
+ populateChildren(file);
+ return file.children.filter((file) -> file.kind == Dir);
+ };
+ //fancyTree.hasChildren = (file: FileEntry) -> return file.kind == Dir;
+ fancyTree.getName = (file: FileEntry) -> return file?.name;
+ fancyTree.getIcon = (file: FileEntry) -> return '';
+
+ fancyTree.onNameChange = (item: FileEntry, newName: String) -> {
+ item.name = newName;
+ }
+
+ fancyTree.dragAndDropInterface =
+ {
+ onDragStart: function(file: FileEntry, dataTransfer: js.html.DataTransfer) : Bool {
+ var selection = fancyTree.getSelectedItems();
+ if (selection.length <= 0)
+ return false;
+ var ser = [];
+ for (item in selection) {
+ ser.push(getFileEntryPath(file));
+ }
+ dataTransfer.setData(dragKey, haxe.Json.stringify(ser));
+ return true;
+ },
+ getItemDropFlags: function(target: FileEntry, dataTransfer: js.html.DataTransfer) : hide.comp.FancyTree.DropFlags {
+ var containsFiles = false;
+ if (dataTransfer.types.contains("Files")) {
+ containsFiles = true;
+ }
+ if (dataTransfer.types.contains(dragKey)) {
+ containsFiles = true;
+ }
+
+ if (!containsFiles) {
+ return hide.comp.FancyTree.DropFlags.ofInt(0);
+ }
+
+ if (target.kind == Dir) {
+ return (Reorder:hide.comp.FancyTree.DropFlags) | Reparent;
+ }
+ return Reorder;
+ },
+ onDrop: function(target: FileEntry, operation: hide.comp.FancyTree.DropOperation, dataTransfer: js.html.DataTransfer) : Bool {
+ var files : Array = [];
+ for (file in dataTransfer.files) {
+ var path : String = untyped file.path; //file.path is an extension from nwjs or node
+ path = StringTools.replace(path, "\\", "/");
+ files.push(path);
+ }
+
+ var fileMoveData = dataTransfer.getData(dragKey);
+ if (fileMoveData.length > 0) {
+ try {
+ var unser = haxe.Json.parse(fileMoveData);
+ for (file in (unser:Array)) {
+ files.push(file);
+ }
+ } catch (e) {
+ trace("Invalid data " + e);
+ }
+ }
+
+ return true;
+ }
+ }
+
+ fancyTree.rebuildTree();
+
+ fancyTree.openItem(root);
+
+ currentFolder = root;
+
+ var right = layout.find(".right");
+ right.get(0).onkeydown = (e: js.html.KeyboardEvent) -> {
+ if (hide.ui.Keys.matchJsEvent("search", e, ide.currentConfig)) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ search.focus();
+ return;
+ }
+ }
+
+ fancyGallery = new hide.comp.FancyGallery(null, layout.find(".right fancy-gallery"));
+ fancyGallery.getItems = () -> {
+ return currentSearch;
+ }
+
+ fancyGallery.getName = (item : FileEntry) -> item.name;
+
+ fancyGallery.getIcon = (item : FileEntry) -> {
+ if (item.kind == Dir) {
+ return '';
+
+ }
+ else if (item.iconPath == "loading") {
+ return '';
+ }
+ else if (item.iconPath != null) {
+ var url = "file://" + item.iconPath;
+ return '';
+ }
+ else {
+ return '';
+ }
+ };
+
+ fancyGallery.onDoubleClick = (item: FileEntry) -> {
+ if (item.kind == File) {
+ ide.openFile(getFileEntryPath(item));
+ } else {
+ openDir(item, true);
+ }
+ }
+
+ fancyGallery.visibilityChanged = (item: FileEntry, visible: Bool) -> {
+ var path = getFileEntryPath(item);
+ hide.tools.FileManager.inst.setPriority(path, visible ? 1 : 0);
+ }
+
+ fancyGallery.dragAndDropInterface = {
+ onDragStart: (item: FileEntry, dataTransfer: js.html.DataTransfer) -> {
+ dataTransfer.setData(dragKey, haxe.Json.stringify([getFileEntryPath(item)]));
+ return true;
+ }
+ }
+
+ fancyGallery.rebuild();
+
+
+ fancyTree.onSelectionChanged = () -> {
+ var selection = fancyTree.getSelectedItems();
+
+ if (selection.length > 0) {
+ openDir(selection[0], false);
+ }
+ }
+
+ onSearch();
+ }
+
+ function openDir(item: FileEntry, syncTree: Bool) {
+ if (item.kind == Dir) {
+ currentFolder = item;
+ onSearch();
+ }
+
+ if (syncTree) {
+ fancyTree.selectItem(item, true);
+ }
+ }
+
+ static var _ = hide.ui.View.register(FileBrowser, { width : 350, position : Bottom });
+}
\ No newline at end of file
diff --git a/hide/view/GenericGraphEditor.hx b/hide/view/GenericGraphEditor.hx
index cfd478e35..6200aeae6 100644
--- a/hide/view/GenericGraphEditor.hx
+++ b/hide/view/GenericGraphEditor.hx
@@ -54,6 +54,7 @@ class GenericGraphEditor extends hide.view.FileView implements IGraphEditor {
// Scene init
scenePreview = new hide.comp.ScenePreview(config, previewContainer, null, saveDisplayKey + "/scenePreview");
+ scenePreview.addToolbar();
scenePreview.element.addClass("scene-preview");
scenePreview.onReady = onScenePreviewReady;
diff --git a/hide/view/Gym.hx b/hide/view/Gym.hx
index f55b63eb6..b444a7496 100644
--- a/hide/view/Gym.hx
+++ b/hide/view/Gym.hx
@@ -141,12 +141,189 @@ class Gym extends hide.ui.View<{}> {
'));
}
+
+ {
+ var toolbar = section(element, "Windows");
+
+ var btn = new Element("Open subwindow 'test'");
+
+ toolbar.append(btn);
+
+ var subwindow : js.html.Window;
+ var scene : hide.comp.Scene;
+
+ btn.on("click", (_) -> {
+ subwindow = js.Browser.window.open("", "test","popup=true");
+
+ var jq = new Element(subwindow.document.body);
+ jq.empty();
+ jq.append(new Element("This is a triumph
"));
+ var container = new Element("");
+ jq.append(container);
+
+ // var paragraphs = subwindow.document.querySelectorAll("p");
+ // for (p in paragraphs) {
+ // p.textContent = "This is the begining of something great";
+ // }
+
+ scene = new hide.comp.Scene(config, container, null);
+
+ scene.onReady = () -> {
+ new h3d.scene.CameraController(scene.s3d);
+ var box = new h3d.scene.Box(scene.s3d);
+ box.material.mainPass.setPassName("overlay");
+
+ var text = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
+ text.text = "Hello world";
+ text.x = 8;
+ text.y = 8;
+ };
+
+ var drag = new Element('Drag Me
').appendTo(jq);
+ drag.get(0).addEventListener("dragstart", (ev: js.html.DragEvent) -> {
+ ev.dataTransfer.setData("text/plain", "foo");
+ ev.dataTransfer.dropEffect = "copy";
+ });
+ });
+
+ var btn = new Element("Spawn cube in subwindow");
+ toolbar.append(btn);
+
+ btn.on("click", (_) -> {
+ var box = new h3d.scene.Box(0xFFFFFFFF, scene.s3d);
+ box.setPosition(hxd.Math.random(10),hxd.Math.random(10),hxd.Math.random(10));
+ box.material.mainPass.setPassName("overlay");
+ box.material.color.r = hxd.Math.random();
+ box.material.color.g = hxd.Math.random();
+ box.material.color.b = hxd.Math.random();
+ });
+
+ var dropZone = new Element("Drop something on me from the other window
").appendTo(toolbar);
+ dropZone.get(0).addEventListener("drop", (ev : js.html.DragEvent) -> {
+ ev.preventDefault();
+ var data = ev.dataTransfer.getData("text/plain");
+ dropZone.text(data);
+ });
+
+ dropZone.get(0).addEventListener("dragover", (ev : js.html.DragEvent) -> {
+ ev.preventDefault();
+ ev.dataTransfer.dropEffect = "copy";
+ });
+
+ var btn2 = new Element("Localhost 5500").appendTo(toolbar);
+ btn2.on("click", (_) -> {
+ subwindow = js.Browser.window.open("http://127.0.0.1:5500/", "test","popup=true");
+ });
+ }
+
+
+ {
+ var toolbar = section(element, "Offscreen Rendering");
+ var btn = new Element("Render test.thumb.png").appendTo(toolbar);
+
+ btn.get(0).onclick = (e) -> {
+ var fm = hide.tools.FileManager.inst;
+ }
+
+ var btn = new Element("Test thumbnail generator").appendTo(toolbar);
+
+ var sub : js.node.child_process.ChildProcess = null;
+
+ var remoteSocket : hxd.net.Socket = null;
+
+ btn.get(0).onclick = (e) -> {
+
+ if (sock != null) {
+ sock.close();
+ }
+
+ sock = new hxd.net.Socket();
+
+ sock.onError = (msg) -> {
+ trace("Socket error " + msg);
+ }
+
+ sock.onData = () -> {
+ trace("sock.onData");
+ while(sock.input.available > 0) {
+ var data = sock.input.readLine().toString();
+
+ trace("recieved data sock.onData", data);
+ }
+ }
+
+ sock.bind("localhost", 9669, (rs: hxd.net.Socket) -> {
+ trace("new connexion");
+ remoteSocket = rs;
+
+ remoteSocket.onError = (msg) -> {
+ trace("Socket error " + msg);
+ }
+
+ remoteSocket.onData = () -> {
+ trace("rawsocket.onData");
+
+ while(remoteSocket.input.available > 0) {
+ var data = remoteSocket.input.readLine().toString();
+
+ trace("recieved data", data);
+ }
+ }
+ });
+
+ nw.Window.open('app.html?thumbnail=true', {new_instance: true}, (win: nw.Window) -> {
+ win.on("close", () -> {
+ sock.close();
+ sock = null;
+ });
+ });
+ }
+
+ var btn = new Element("Send message").appendTo(toolbar);
+ btn.get(0).onclick = (e) -> {
+ remoteSocket.out.writeString("Test message\n");
+ }
+
+ var btn = new Element("Rethrow test").appendTo(toolbar);
+ btn.get(0).onclick = (e) -> {
+ rethrowTest1();
+ }
+ }
+ }
+
+ function rethrowTest1() {
+ try {
+ rethrowTest2();
+ } catch (e) {
+ js.Lib.rethrow();
+ }
}
+ function rethrowTest2() {
+ try {
+ rethrowTest3();
+ } catch(e) {
+ js.Lib.rethrow();
+ }
+ }
+
+ function rethrowTest3() {
+ throw "Error Lol";
+ }
+
+ var subwin: js.html.Window;
+ static var sock: hxd.net.Socket;
+
+
static function section(parent: Element, name: String) : Element {
return new Element('$name
').appendTo(parent);
}
+ static public function onBeforeReload() {
+ sock?.close();
+ sock = null;
+ }
+
static function getContextMenuContent() : Array {
var radioState = 0;
diff --git a/hide/view/Inspector.hx b/hide/view/Inspector.hx
new file mode 100644
index 000000000..60ca897b5
--- /dev/null
+++ b/hide/view/Inspector.hx
@@ -0,0 +1,17 @@
+package hide.view;
+
+typedef InspectorState = {
+
+}
+
+class Inspector extends hide.ui.View {
+
+ override function new(state) {
+ super(state);
+ }
+ override function onDisplay() {
+ element.html("Hello world
");
+ }
+
+ static var _ = hide.ui.View.register(Inspector, { width : 350, position : Right, id: "inspector" });
+}
\ No newline at end of file
diff --git a/hide/view/Prefab.hx b/hide/view/Prefab.hx
index afd3b72f5..ad3b50a79 100644
--- a/hide/view/Prefab.hx
+++ b/hide/view/Prefab.hx
@@ -242,6 +242,13 @@ class Prefab extends hide.view.FileView {
sceneReadyDelayed.empty();
}
+ override function onHide() {
+ super.onHide();
+
+ //ide.closeInspector();
+
+ }
+
override function onDisplay() {
if( sceneEditor != null ) sceneEditor.dispose();
createData();
@@ -391,6 +398,8 @@ class Prefab extends hide.view.FileView {
tools.refreshToggles();
setRenderPropsEditionVisibility(Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false));
+
+ //ide.getOrOpenInspector();
}
public function hideColumns(?_) {
diff --git a/hide/view/RemoteConsoleView.hx b/hide/view/RemoteConsoleView.hx
index 710915484..46be0d4e8 100644
--- a/hide/view/RemoteConsoleView.hx
+++ b/hide/view/RemoteConsoleView.hx
@@ -180,6 +180,8 @@ class RemoteConsoleView extends hide.ui.View<{}> {
haxe.Timer.delay(wait, 10);
return;
}
+ if (Ide.inst.thumbnailMode)
+ return;
var config = Ide.inst.config.project;
var pconfig = config.get("remoteconsole");
if( pconfig != null && pconfig.disableAutoStartServer != true ) {
diff --git a/hide/view/animgraph/BlendSpace2DEditor.hx b/hide/view/animgraph/BlendSpace2DEditor.hx
index 1a2a15992..3d133fe38 100644
--- a/hide/view/animgraph/BlendSpace2DEditor.hx
+++ b/hide/view/animgraph/BlendSpace2DEditor.hx
@@ -300,6 +300,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
panel.onResize = refreshGraph;
scenePreview = new hide.comp.ScenePreview(config, previewContainer, null, saveDisplayKey + "/preview");
+ scenePreview.addToolbar();
scenePreview.listLoadableMeshes = () -> {
var ret : Array<{label: String, path: String}> = [];
var list = AnimGraphEditor.gatherAllPreviewModels(blendSpace2D.animFolder);
diff --git a/hrt/prefab/Model.hx b/hrt/prefab/Model.hx
index 7bcfbcd1a..f4a527159 100644
--- a/hrt/prefab/Model.hx
+++ b/hrt/prefab/Model.hx
@@ -55,8 +55,7 @@ class Model extends Object3D {
return obj;
#if editor
} catch( e : Dynamic ) {
- e.message = "Could not load model " + source + ": " + e.message;
- shared.onError(e);
+ hide.Ide.inst.quickError("Could not load model " + source + ": " + e.message);
}
#end
return new h3d.scene.Object(parent3d);
diff --git a/hrt/prefab/fx/FX.hx b/hrt/prefab/fx/FX.hx
index 0cb63dcca..c879f814e 100644
--- a/hrt/prefab/fx/FX.hx
+++ b/hrt/prefab/fx/FX.hx
@@ -301,7 +301,7 @@ class FXAnimation extends h3d.scene.Object {
var visible = anim.elt.visible;
#if editor
var editor = anim.elt.shared.editor;
- visible = visible && editor.isVisible(anim.elt);
+ visible = visible && (editor?.isVisible(anim.elt) ?? true);
#end
anim.obj.visible = visible && evaluator.getFloat(anim.visibility, time) > 0.5;
}
diff --git a/hrt/prefab/l3d/Trails.hx b/hrt/prefab/l3d/Trails.hx
index dc85f436b..58b726ecb 100644
--- a/hrt/prefab/l3d/Trails.hx
+++ b/hrt/prefab/l3d/Trails.hx
@@ -233,8 +233,11 @@ class TrailObj extends h3d.scene.Mesh {
fxAnim.push(fx);
p = p.parent;
}
- for ( fx in fxAnim )
- fx.trails.remove(this);
+ for ( fx in fxAnim ) {
+ if (fx.trails != null) {
+ fx.trails.remove(this);
+ }
+ }
dprim.dispose();
}
diff --git a/libs/golden/ContentItem.hx b/libs/golden/ContentItem.hx
index 2bb01dbb8..c286980ee 100644
--- a/libs/golden/ContentItem.hx
+++ b/libs/golden/ContentItem.hx
@@ -9,6 +9,7 @@ extern class ContentItem {
var childElementContainer : Container;
var config : Config.ItemConfig;
var header : Header;
+ var id : String;
var __view : Dynamic;
@@ -19,5 +20,6 @@ extern class ContentItem {
public function getItemsByFilter( f : ContentItem -> Bool ) : Array;
public function getActiveContentItem() : ContentItem;
public function setActiveContentItem( item : ContentItem ) : Void;
-
+ public function getItemsById(id : String) : Array;
+ public function remove() : Void ;
}
\ No newline at end of file