2
0
Эх сурвалжийг харах

[filebrowser] Refactored FileEntries to be handled by FileManager

Clément Espeute 2 сар өмнө
parent
commit
fddd42d922

+ 25 - 0
hide/tools/Delayer.hx

@@ -0,0 +1,25 @@
+package hide.tools;
+
+class Delayer<Item> {
+	var queuedItems : Map<{}, Bool> = [];
+	var callback : (Item) -> Void;
+
+	public function new(callback: (Item) -> Void) {
+		this.callback = callback;
+	}
+
+	public function queue(item: Item) {
+		var empty = !queuedItems.iterator().hasNext();
+		queuedItems.set(cast item, true);
+		if (empty) {
+			js.Browser.window.requestAnimationFrame((_) -> processQueue());
+		}
+	}
+
+	function processQueue() {
+		for (item => _ in queuedItems) {
+			callback((cast item: Item));
+		}
+		queuedItems.clear();
+	}
+}

+ 145 - 4
hide/tools/FileManager.hx

@@ -16,9 +16,116 @@ typedef GenToManagerSuccessMessage = {
 	var thumbnailPath : String;
 }
 
-typedef FileData = {
-	name: String,
-	parent: FileData,
+enum FileKind {
+	Dir;
+	File;
+}
+
+@:access(hide.tools.FileManager)
+@:allow(hide.tools.FileManager)
+class FileEntry {
+	public var name: String;
+	public var children: Array<FileEntry>;
+	public var kind: FileKind;
+	public var parent: FileEntry;
+	public var iconPath: String;
+
+	var registeredWatcher : hide.tools.FileWatcher.FileWatchEvent = null;
+
+	public function new(name: String, parent: FileEntry, kind: FileKind) {
+		this.name = name;
+		this.parent = parent;
+		this.kind = kind;
+
+		watch();
+	}
+
+	public function dispose() {
+		if (children != null) {
+			for (child in children) {
+				child.dispose();
+			}
+		}
+		children = null;
+		if (registeredWatcher != null) {
+			hide.Ide.inst.fileWatcher.unregister(this.getPath(), registeredWatcher.fun);
+			registeredWatcher = null;
+		}
+	}
+
+	function refreshChildren(rec: Bool) {
+		if (kind != Dir)
+			return;
+		var fullPath = getPath();
+
+		var oldChildren : Map<String, FileEntry> = [for (file in (children ?? [])) file.name => file];
+
+		if (children == null)
+			children = [];
+		else
+			children.resize(0);
+
+		if (js.node.Fs.existsSync(fullPath)) {
+			var paths = js.node.Fs.readdirSync(fullPath);
+
+			for (path in paths) {
+				if (StringTools.startsWith(path, "."))
+					continue;
+				var prev = oldChildren.get(path);
+				if (prev != null) {
+					children.push(prev);
+					oldChildren.remove(path);
+				} else {
+					var info = js.node.Fs.statSync(fullPath + "/" + path);
+					children.push(
+						new FileEntry(path, this, info.isDirectory() ? Dir : File)
+					);
+				}
+			}
+		}
+
+		for (child in oldChildren) {
+			child.dispose();
+		}
+
+		children.sort(compareFile);
+
+		if (rec) {
+			for (child in children) {
+				child.refreshChildren(rec);
+			}
+		}
+	}
+
+	function watch() {
+		if (registeredWatcher != null)
+			throw "already watching";
+
+		var rel = this.getRelPath();
+		registeredWatcher = hide.Ide.inst.fileWatcher.register(rel, FileManager.inst.fileChangeInternal.bind(this), true);
+	}
+
+	public function getPath() {
+		if (this.parent == null) return hide.Ide.inst.resourceDir;
+		return this.parent.getPath() + "/" + this.name;
+	}
+
+	public function getRelPath() {
+		if (this.parent == null) return "";
+		if (this.parent.parent == null) return this.name;
+		return this.parent.getRelPath() + "/" + this.name;
+	}
+
+	// sort directories before files, and then dirs and files alphabetically
+	static public 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);
+	}
 }
 
 typedef MiniatureReadyCallback = (miniaturePath: String) -> Void;
@@ -28,10 +135,13 @@ typedef MiniatureReadyCallback = (miniaturePath: String) -> Void;
 **/
 class FileManager {
 
+	public var fileRoot: FileEntry;
+
 	public static final thumbnailGeneratorPort = 9669;
 	public static final thumbnailGeneratorUrl = "localhost";
 
 	public static var inst(get, default) : FileManager;
+	public var onFileChangeHandlers: Array<(entry: FileEntry) -> Void> = [];
 
 	var windowManager : RenderWindowManager = null;
 
@@ -41,16 +151,23 @@ class FileManager {
 	var generatorSocket : hxd.net.Socket = null;
 	var pendingMessages : Array<String> = [];
 
+	var fileEntryRefreshDelay : Delayer<FileEntry>;
+
 	var retries = 0;
 	static final maxRetries = 5;
 
 	static function get_inst() {
 		if (inst == null) {
 			inst = new FileManager();
+			inst.init();
 		}
 		return inst;
 	}
 
+	function new() {
+
+	}
+
 	public static function onBeforeReload() {
 		if (inst != null) {
 			inst.cleanupGenerator();
@@ -138,14 +255,38 @@ class FileManager {
 		});
 	}
 
-	function new() {
+	function init() {
 		// kill server when page is reloaded
 		js.Browser.window.addEventListener('beforeunload', () -> { cleanupGenerator(); cleanupServer(); });
 
 		setupServer();
 		checkWindowReady();
+		initFileSystem();
 	}
 
+	function initFileSystem() {
+		fileEntryRefreshDelay = new Delayer((entry: FileEntry) -> {
+			entry.refreshChildren(false);
+		});
+
+		fileRoot = new FileEntry("res", null, Dir);
+		fileRoot.refreshChildren(true);
+	}
+
+	function fileChangeInternal(entry: FileEntry) {
+		if (!js.node.Fs.existsSync(entry.getPath()) && entry.parent != null) {
+			fileEntryRefreshDelay.queue(entry.parent);
+			return;
+		}
+		if (entry.kind == Dir) {
+			fileEntryRefreshDelay.queue(entry);
+		}
+		for (handler in onFileChangeHandlers) {
+			handler(entry);
+		}
+	}
+
+
 	function processThumbnailGeneratorMessage(message: String) {
 		try {
 			var message = haxe.Json.parse(message);

+ 11 - 126
hide/view/FileBrowser.hx

@@ -1,129 +1,11 @@
 package hide.view;
 
+import hide.tools.FileManager;
+import hide.tools.FileManager.FileEntry;
 typedef FileBrowserState = {
 
 }
 
-enum FileKind {
-	Dir;
-	File;
-}
-
-class FileEntry {
-	public var name: String;
-	public var children: Array<FileEntry>;
-	public var kind: FileKind;
-	public var parent: FileEntry;
-	public var iconPath: String;
-
-	public var onChange : (file: FileEntry) -> Void;
-
-	var registeredWatcher : hide.tools.FileWatcher.FileWatchEvent = null;
-
-	public function new(name: String, parent: FileEntry, kind: FileKind, onChange: (file: FileEntry) -> Void) {
-		this.name = name;
-		this.parent = parent;
-		this.kind = kind;
-		this.onChange = onChange;
-
-		watch();
-	}
-
-	public function dispose() {
-		if (children != null) {
-			for (child in children) {
-				child.dispose();
-			}
-		}
-		children = null;
-		if (registeredWatcher != null) {
-			hide.Ide.inst.fileWatcher.unregister(this.getPath(), registeredWatcher.fun);
-			registeredWatcher = null;
-		}
-	}
-
-	public function refreshChildren() {
-		var fullPath = getPath();
-
-		if (children == null)
-			children = [];
-		else
-			children.resize(0);
-
-		if (!js.node.Fs.existsSync(fullPath)) {
-			return;
-		}
-
-		var paths = js.node.Fs.readdirSync(fullPath);
-
-		var oldChildren : Map<String, FileEntry> = [for (file in (children ?? [])) file.name => file];
-
-
-
-		for (path in paths) {
-			if (StringTools.startsWith(path, "."))
-				continue;
-			var prev = oldChildren.get(path);
-			if (prev != null) {
-				children.push(prev);
-				oldChildren.remove(path);
-			} else {
-				var info = js.node.Fs.statSync(fullPath + "/" + path);
-				children.push(
-					new FileEntry(path, this, info.isDirectory() ? Dir : File, onChange)
-				);
-			}
-		}
-
-		for (child in oldChildren) {
-			child.dispose();
-		}
-
-		children.sort(compareFile);
-	}
-
-	function watch() {
-		if (registeredWatcher != null)
-			throw "already watching";
-
-		var rel = this.getRelPath();
-		if (this.kind == Dir) {
-			registeredWatcher = hide.Ide.inst.fileWatcher.register(rel, onChangeDirInternal, true);
-		} else if (onChange != null) {
-			registeredWatcher = hide.Ide.inst.fileWatcher.register(rel, onChange.bind(this), true);
-		}
-	}
-
-	function onChangeDirInternal() {
-		refreshChildren();
-
-		if (onChange != null) {
-			onChange(this);
-		}
-	}
-
-	public function getPath() {
-		if (this.parent == null) return hide.Ide.inst.resourceDir;
-		return this.parent.getPath() + "/" + this.name;
-	}
-
-	public function getRelPath() {
-		if (this.parent == null) return "";
-		if (this.parent.parent == null) return this.name;
-		return this.parent.getRelPath() + "/" + this.name;
-	}
-
-	// sort directories before files, and then dirs and files alphabetically
-	static public 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);
-	}
-}
 
 class FileBrowser extends hide.ui.View<FileBrowserState> {
 
@@ -231,7 +113,7 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 				for (file in files) {
 					if (file.kind == Dir && (collapseSubfolders || searchString.length > 0)) {
 						if (file.children == null) {
-							file.refreshChildren();
+							throw "null children";
 						}
 						rec(file.children);
 					}
@@ -283,9 +165,7 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 		keys.register("undo", function() undo.undo());
 		keys.register("redo", function() undo.redo());
 
-		root = new FileEntry("res", null, Dir, onFileChange);
-
-		root.refreshChildren();
+		root = FileManager.inst.fileRoot;
 
 		var layout = new Element('
 			<file-browser>
@@ -347,7 +227,7 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 			if (file.kind == File)
 				return null;
 			if (file.children == null)
-				file.refreshChildren();
+				throw "null children";
 			return file.children.filter((file) -> file.kind == Dir);
 		};
 		//fancyTree.hasChildren = (file: FileEntry) -> return file.kind == Dir;
@@ -515,7 +395,6 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 
 		fancyGallery.onContextMenu = contextMenu.bind(true);
 
-
 		if (Ide.inst.ideConfig.filebrowserDebugShowMenu) {
 			layout.find(".btn-collapse-folders").after(new Element('<fancy-button class="btn-debug"><span class="ico ico-bug"></span></fancy-button>'));
 			var button = layout.find(".btn-debug").get(0);
@@ -594,6 +473,12 @@ class FileBrowser extends hide.ui.View<FileBrowserState> {
 		}
 		syncCollapseSubfolders();
 
+		FileManager.inst.onFileChangeHandlers.push(onFileChange);
+	}
+
+	override function destroy() {
+		super.destroy();
+		FileManager.inst.onFileChangeHandlers.remove(onFileChange);
 	}
 
 	function createNew( directoryFullPath : String, ext : hide.view.FileTree.ExtensionDesc ) {