瀏覽代碼

review and document prefab api:
- differentiate load/save/makeInstance (immediate) from loadData/saveData/make (recursive)
- rename makeInstanceRec to make
- rename loadRec to loadPrefab
- added find
- get/getOpt/findAll now includes 'this'
- removed dot path handling in get/getOpt (moved to Library.getFromPath)

ncannasse 6 年之前
父節點
當前提交
9ded8c2a34
共有 3 個文件被更改,包括 197 次插入79 次删除
  1. 19 30
      hxd/prefab/Library.hx
  2. 177 48
      hxd/prefab/Prefab.hx
  3. 1 1
      hxd/res/Prefab.hx

+ 19 - 30
hxd/prefab/Library.hx

@@ -2,46 +2,35 @@ package hxd.prefab;
 
 class Library extends Prefab {
 
-	var inRec = false;
-
 	public function new() {
 		super(null);
 		type = "prefab";
 	}
 
-	// hacks to use directly non-recursive api
-
 	override function load( obj : Dynamic ) {
-		if( inRec )
-			return;
-		var children : Array<Dynamic> = obj.children;
-		if( children != null )
-			for( v in children )
-				Prefab.loadRec(v, this);
-	}
-
-	override function reload(v:Dynamic) {
-		inRec = true;
-		super.reload(v);
-		inRec = false;
 	}
 
 	override function save() {
-		if( inRec )
-			return {};
-		inRec = true;
-		var obj = saveRec();
-		inRec = false;
-		return obj;
+		return {};
 	}
 
-	override function makeInstance(ctx:Context):Context {
-		if( inRec )
-			return ctx;
-		inRec = true;
-		makeInstanceRec(ctx);
-		inRec = false;
-		return ctx;
+	/**
+		Returns the prefab within children that matches the given absolute path
+	**/
+	public function getFromPath( path : String ) : Prefab {
+		var parts = path.split(".");
+		var cur : Prefab = this;
+		for( p in parts ) {
+			var found = false;
+			for( c in cur.children )
+				if( c.name == p ) {
+					found = true;
+					cur = c;
+					break;
+				}
+			if( !found ) return null;
+		}
+		return cur;
 	}
 
 	static var registeredElements = new Map<String,{ cl : Class<Prefab> #if editor, inf : hide.prefab.HideProps #end }>();
@@ -67,7 +56,7 @@ class Library extends Prefab {
 		if( extension != null ) registeredExtensions.set(extension, type);
 		return true;
 	}
-	
+
 	public static function create( extension : String ) {
 		var type = getPrefabType(extension);
 		var p : hxd.prefab.Prefab;

+ 177 - 48
hxd/prefab/Prefab.hx

@@ -1,16 +1,50 @@
 package hxd.prefab;
 
+/**
+	Prefab is an data-oriented tree container capable of creating instances of Heaps objects.
+**/
 @:keepSub
 class Prefab {
 
+	/**
+		The type of prefab, allows to identify which class it should be loaded with.
+	**/
 	public var type(default, null) : String;
+
+	/**
+		The name of the prefab in the tree view
+	**/
 	public var name(default, set) : String;
+
+	/**
+		The parent of the prefab in the tree view
+	**/
 	public var parent(default, set) : Prefab;
+
+	/**
+		The associated source file (an image, a 3D model, etc.) if the prefab type needs it.
+	**/
 	public var source(default, set) : String;
+
+	/**
+		The list of children prefab in the tree view
+	**/
 	public var children(default, null) : Array<Prefab>;
+
+	/**
+		Tells if the prefab will create an instance when calling make() or be ignored. Also apply to this prefab children.
+	**/
 	public var enabled : Bool = true;
+
+
+	/**
+		A storage for some extra properties
+	**/
 	public var props : Any;
 
+	/**
+		Creates a new prefab with the given parent.
+	**/
 	public function new(?parent) {
 		this.parent = parent;
 		children = [];
@@ -34,13 +68,23 @@ class Prefab {
 	}
 
 	#if editor
+
+	/**
+		Allows to customize how the prefab object is edited within Hide
+	**/
 	public function edit( ctx : hide.prefab.EditContext ) {
 	}
 
+	/**
+		Allows to customize how the prefab object is displayed / handled within Hide
+	**/
 	public function getHideProps() : hide.prefab.HideProps {
 		return { icon : "question-circle", name : "Unknown" };
 	}
 
+	/**
+		Allows to customize how the prefab instance changes when selected/unselected within Hide
+	**/
 	public function setSelected( ctx : hide.prefab.Context, b : Bool ) {
 		var materials = ctx.shared.getMaterials(this);
 
@@ -62,31 +106,55 @@ class Prefab {
 	}
 	#end
 
+	/**
+		Iterate over children prefab
+	**/
 	public inline function iterator() : Iterator<Prefab> {
 		return children.iterator();
 	}
 
-	public function load( v : Dynamic ) {
+	/**
+		Override to implement your custom prefab data loading
+	**/
+	function load( v : Dynamic ) {
 		throw "Not implemented";
 	}
 
-	public function save() : {} {
+	/**
+		Override to implement your custom prefab data saving
+	**/
+	function save() : {} {
 		throw "Not implemented";
 		return null;
 	}
 
+	/**
+		Creates an instance for this prefab only (and not its children).
+		Use make(ctx) to creates the whole instances tree;
+	**/
 	public function makeInstance( ctx : Context ) : Context {
 		return ctx;
 	}
 
+	/**
+		Allows to customize how an instance gets updated when a property name changes.
+		You can also call updateInstance(ctx) in order to force whole instance synchronization against current prefab data.
+	**/
 	public function updateInstance( ctx : Context, ?propName : String ) {
 	}
 
+	/**
+		Removes the created instance for this prefab only (not is children).
+		If false is returned, the instance could not be removed and the whole context scene needs to be rebuilt
+	**/
 	public function removeInstance( ctx : Context ) : Bool {
 		return false;
 	}
 
-	public function saveRec() : {} {
+	/**
+		Save the whole prefab data and its children.
+	**/
+	@:final public function saveData() : {} {
 		var obj : Dynamic = save();
 		obj.type = type;
 		if( !enabled )
@@ -96,13 +164,38 @@ class Prefab {
 		if( source != null )
 			obj.source = source;
 		if( children.length > 0 )
-			obj.children = [for( s in children ) s.saveRec()];
+			obj.children = [for( s in children ) s.saveData()];
 		if( props != null && obj.props == null )
 			obj.props = props;
 		return obj;
 	}
 
+	/**
+		Load the whole prefab data and creates its children.
+	**/
+	@:final public function loadData( v : Dynamic ) {
+		type = v.type;
+		name = v.name;
+		enabled = v.enabled == null ? true : v.enabled;
+		props = v.props;
+		source = v.source;
+		load(v);
+		if( children.length > 0 )
+			children = [];
+		var children : Array<Dynamic> = v.children;
+		if( children != null )
+			for( v in children )
+				loadPrefab(v, this);
+	}
+
+	/**
+		Updates in-place the whole prefab data and its children.
+	**/
 	public function reload( p : Dynamic ) {
+		name = p.name;
+		enabled = p.enabled == null ? true : p.enabled;
+		props = p.props;
+		source = p.source;
 		load(p);
 		var childData : Array<Dynamic> = p.children;
 		if( childData == null ) {
@@ -121,34 +214,30 @@ class Prefab {
 				prev.reload(v);
 				newchild.push(prev);
 			} else {
-				newchild.push(loadRec(v,this));
+				newchild.push(loadPrefab(v,this));
 			}
 		}
 		children = newchild;
 	}
 
-	public static function loadRec( v : Dynamic, ?parent : Prefab ) {
+	/**
+		Creates the correct prefab based on v.type and load its data and children.
+		If one the prefab in the tree is not registered, a hxd.prefab.Unkown is created instead.
+	**/
+	public static function loadPrefab( v : Dynamic, ?parent : Prefab ) {
 		var pcl = @:privateAccess Library.registeredElements.get(v.type);
 		var pcl = pcl == null ? null : pcl.cl;
 		if( pcl == null ) pcl = hxd.prefab.Unknown;
 		var p = Type.createInstance(pcl, [parent]);
-		p.type = v.type;
-		p.name = v.name;
-		if(v.enabled != null)
-			p.enabled = v.enabled;
-		p.props = v.props;
-		if( v.source != null )
-			p.source = v.source;
-		p.load(v);
-		var children : Array<Dynamic> = v.children;
-		if( children != null )
-			for( v in children )
-				loadRec(v, p);
+		p.loadData(v);
 		return p;
 	}
 
-	public function makeInstanceRec( ctx : Context ) : Context {
-		if(!enabled)
+	/**
+		Creates an instance for this prefab and its children.
+	**/
+	public function make( ctx : Context ) : Context {
+		if( !enabled )
 			return ctx;
 		if( ctx == null ) {
 			ctx = new Context();
@@ -156,11 +245,14 @@ class Prefab {
 		}
 		ctx = makeInstance(ctx);
 		for( c in children )
-			c.makeInstanceRec(ctx);
+			c.make(ctx);
 		return ctx;
 	}
 
 	#if castle
+	/**
+		Returns which CDB model this prefab props represents
+	**/
 	public function getCdbModel( ?p : Prefab ) : cdb.Sheet {
 		if( p == null )
 			p = this;
@@ -170,6 +262,9 @@ class Prefab {
 	}
 	#end
 
+	/**
+		Search the prefab tree for the prefab matching the given name, returns null if not found
+	**/
 	public function getPrefabByName( name : String ) {
 		if( this.name == name )
 			return this;
@@ -181,17 +276,15 @@ class Prefab {
 		return null;
 	}
 
+	/**
+		Simlar to get() but returns null if not found.
+	**/
 	public function getOpt<T:Prefab>( cl : Class<T>, ?name : String ) : T {
-		var parts = name == null ? null : name.split(".");
+		if( name == null || this.name == name ) {
+			var cval = to(cl);
+			if( cval != null ) return cval;
+		}
 		for( c in children ) {
-			if( (name == null || c.name == name) ) {
-				var cval = c.to(cl);
-				if( cval != null ) return cval;
-			}
-			if( parts != null && parts.length > 1 && c.name == parts[0] ) {
-				parts.shift();
-				return c.getOpt(cl, parts.join("."));
-			}
 			var p = c.getOpt(cl, name);
 			if( p != null )
 				return p;
@@ -199,6 +292,10 @@ class Prefab {
 		return null;
 	}
 
+	/**
+		Search the prefab tree for the prefab matching the given prefab class (and name, if specified).
+		Throw an exception if not found. Uses getOpt() to return null instead.
+	**/
 	public function get<T:Prefab>( cl : Class<T>, ?name : String ) : T {
 		var v = getOpt(cl, name);
 		if( v == null )
@@ -206,22 +303,43 @@ class Prefab {
 		return v;
 	}
 
+	/**
+		Return all prefabs in the tree matching the given prefab class.
+	**/
 	public function getAll<T:Prefab>( cl : Class<T>, ?arr: Array<T> ) : Array<T> {
 		return findAll(function(p) return p.to(cl));
 	}
 
-	public function findAll<T>( f : Prefab -> Null<T>, ?arr : Array<T> ) : Array<T> {
-		if(arr == null)
-			arr = [];
-		for( c in children ) {
-			var v = f(c);
-			if( v != null )
-				arr.push(v);
-			c.findAll(f, arr);
+	/**
+		Find a single prefab in the tree by calling `f` on each and returning the first not-null value returned, or null if not found.
+	**/
+	public function find<T>( f : Prefab -> Null<T> ) : Null<T> {
+		var v = f(this);
+		if( v != null )
+			return v;
+		for( p in children ) {
+			var v = p.find(f);
+			if( v != null ) return v;
 		}
+		return null;
+	}
+
+	/**
+		Find several prefabs in the tree by calling `f` on each and returning all the not-null values returned.
+	**/
+	public function findAll<T>( f : Prefab -> Null<T>, ?arr : Array<T> ) : Array<T> {
+		if( arr == null ) arr = [];
+		var v = f(this);
+		if( v != null )
+			arr.push(v);
+		for( o in children )
+			o.findAll(f,arr);
 		return arr;
 	}
 
+	/**
+		Returns all prefabs in the tree matching the specified class.
+	**/
 	public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
 		if(arr == null)
 			arr = [];
@@ -237,13 +355,9 @@ class Prefab {
 		return arr;
 	}
 
-	public function visitChildren(func: Prefab->Bool) {
-		for(c in children) {
-			if(func(c))
-				c.visitChildren(func);
-		}
-	}
-
+	/**
+		Returns the first parent in the tree matching the specified class or null if not found.
+	**/
 	public function getParent<T:Prefab>( c : Class<T> ) : Null<T> {
 		var p = parent;
 		while(p != null) {
@@ -254,20 +368,32 @@ class Prefab {
 		return null;
 	}
 
+	/**
+		Converts the prefab to another prefab class.
+		Returns null if not of this type.
+	**/
 	public function to<T:Prefab>( c : Class<T> ) : Null<T> {
 		return Std.instance(this, c);
 	}
 
+	/**
+		Returns the absolute name path for this prefab
+	**/
 	public function getAbsPath() {
 		var p = this;
 		var path = [];
 		while(p.parent != null) {
-			path.unshift(p.name);
+			var n = p.name;
+			if( n == null ) n = getDefaultName();
+			path.unshift(n);
 			p = p.parent;
 		}
 		return path.join('.');
 	}
 
+	/**
+		Returns the default name for this prefab
+	**/
 	public function getDefaultName() : String {
 		if(source != null) {
 			var f = new haxe.io.Path(source).file;
@@ -277,8 +403,11 @@ class Prefab {
 		return type.split(".").pop();
 	}
 
+	/**
+		Clone this prefab and all its children
+	**/
 	public function clone() : Prefab {
-		var obj = saveRec();
-		return loadRec(haxe.Json.parse(haxe.Json.stringify(obj)));
+		var obj = saveData();
+		return loadPrefab(haxe.Json.parse(haxe.Json.stringify(obj)));
 	}
 }

+ 1 - 1
hxd/res/Prefab.hx

@@ -9,7 +9,7 @@ class Prefab extends hxd.res.Resource {
 			return lib;
 		var data = haxe.Json.parse(entry.getText());
 		lib = hxd.prefab.Library.create(entry.extension);
-		lib.load(data);
+		lib.loadData(data);
 		watch(function() lib.reload(haxe.Json.parse(entry.getText())));
 		return lib;
 	}