Hooking

Hooking provides a lightweight way to add flexibility to applications, especially for components. We just can not handle all types of skins, themes and interactions internally as it is too complex and vast, instead, allow some kind of hooking will allow user to handle things their way without the need of reading and understanding how things work internally.

Let’s see a simple sample for size-handling ! Let’s say we have a component base class

package {
	public class AComponent {
		// ...
		public function setSize(w: int, h: int): void {
			//do skin handling here
		}
		// ...
	}
}

As we can see we will need to do all supported skin resizing inside setSize function to enable skin to change its size. Let’s think of the various cases when the skin is just a simple textfield, a bitmap, or a sprite contains a textfield, or a sprite contains many other sprites (bg, border, textfield …) how our almighty setSize handle that ? just can’t with all possible cases, right ? And we usually agree on a compromise solution that we will only support some type of skins or defining some naming rules that user must follow in order for the component to woks. This will work for most cases although we will need quite a bit of work but it’s worth to note that there are special cases that user just can not fit their need into our rules … what will they do then ?

Adding hooking support will solve the problem, all we need to do is only support one simple and popular case of resizing, for other cases, user will need to hook and make changes to the way they want. The hook, which is just a function, can be collected into a class / source folder for later uses. As we will need to solve only one special simple case, our Component will be much more lightweight than the traditional approach and we can always solve special cases as user will be in charged then !

Let’s have a look into how can we add a simple size hooking support

package {
	public class AComponent {
		// ...
		public var hookSize : Function;
		public function setSize(w: int, h: int): void {
			if (hookSize != null){
				hookSize(w,h);
			} else {
				//default size handling will go here
				//just support one simple popular case!
			}
		}
		// ...
	}
}

This seems to be quite simple and clean, but to this point, we have some other things to care about :

In the hookSize sample above, we called it with exactly same parameters as setSize to provided user a hopefully no need to remember syntax, but user might still be confused as there are no visual connection between the hook and the method setSize for them to rely on. What user see is that we have a hook, called hookSize, which is just a function, don’t know yet which parameters will be passed by, and what should be return.

Well, as the only resizing case we support is the most popular, we can safely assume that users of hooking should rather well verse with actionscript 3 so we can safely define some rules and then public some documentation about the hooking for them to follow, that will do the job.

Let’s add some rules
rule #1 : the hookSize will need to have exactly 2 parameters w & h
rule #2 : do autoSize (resize to fit content) when (w==0 && h==0)
rule #3 : return the new size if autosized otherwise, return null

Let’s see how our code looks like now

package {
	public class AComponent {
		// ...
		public var hookSize : Function;
		public function setSize(w: int, h: int): void {
			var newSize : Object;
			if (hookSize != null){
				newSize = hookSize(w,h);
				if (w==0 && h==0) {
					_width = newSize.width;
					_height = newSize.height;
				}
			} else {
				//default size handling will go here
				//just support one simple popular case !
				//change  _width / _height directly if w==0, h==0
			}
		}
		// ...
	}
}

and the hookSize function should look somewhat like

//assume that comp is a Component
comp.hookSize = function (w: int, h: int): Object {
	var obj : Object;
	if (w==0 && h==0){
		//calculate the autosize (min) size
		//w, h to the correct values
	}
	//update comp.skin to w, h
	return obj;
}

To this point, we see that it’s rather inefficient creating a newSize object each time we need to send the values back, and it’s not explicit either that _width / _height will be updated to newSize’s properties (how can user know exactly what will be read from newSize object ?)

Another thing to consider is that in hookSize handler we will need to have access to the component to get its skin and old width/height values, this slow down user usage as they need to keep a reference to the component in order to resizing things, passing skin, old width, old height values by will helps minimize the hooking code. User might wishes to send some configuration values to the hook instead of using fixed values so he can write the hook once with parameters (margin, text aligning, max size, min size, items to show/hide based on size …) and use it multiple times later on.

So these two problems can be solved by refining the only rule (dismissing the previous three ones): Use the only hData object and modify values directly on it if needed.

Let’s have a look through our new implementation !

package {
	public class AComponent {
		// ...
		public var hookSize	: Function;
		//user can add more properties to this if needed
		public var hSizeData 	: Object;
		
		public function setSize(w: int, h: int): void {
			if (hookSize != null){
				hSizeData.width = w;
				hSizeData.height = h;
				hSizeData.skin = _skin;
				
				hookSize(hSizeData);
				if (w==0 && h==0) {
					_width = hSizeData.width;
					_height = hSizeData.height;
				}
			} else {
				//default size handling will go here
				//just support one simple popular case !
				//change  _width / _height directly if w==0, h==0
			}
		}
		// ...
	}
}

and the hookSize function should be now

//assume that comp is a Component
comp.hookSize = function (hData: Object): void {
	var w : int = hData.width;
	var h : int = hData.height;
	if (w==0 && h==0){
		//calculate the autosize (min) size, set to w, h
	}
	//update comp.skin to w, h
}

And for strong typing lovers :

package {
	public class AComponent {
		// ...
		public var hookSize	: Function;
		//user can add more properties to this if needed
		public var hSizeData 	: HSizeData;
		
		public function setSize(w: int, h: int): void {
			if (hookSize != null){
				hSizeData.inject(w,h,_skin);
				
				hookSize(hSizeData);
				if (w==0 && h==0) {
					_width = hSizeData.width;
					_height = hSizeData.height;
				}
			} else {
				//default size handling will go here
				//just support one simple popular case !
				//change  _width / _height directly if w==0, h==0
			}
		}
		// ...
	}
}
//assume that comp is a Component
comp.hookSize = function (hData: HSizeData): void {
	if ( hData.width==0 && hData.height==0){
		//calculate the autosize (min) size
		//set to hData.width, hData.height
	}
	//update comp.skin to hData.width, hData.height
}
package {
	public dynamic class HSizeData {
		public var width : int;
		public var height : int;
		public var skin : DisplayObject;
	
		public function inject(w: int, h: int, s: DisplayObject): void{
			width = w;
			height = h;
			skin = s;
		}
	}
}

One last thing to remember is that hooking is for user, don’t use it internally while trying to make composite components.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s