ResizeHandler AS2.0 Class

La siguiente clase es en realidad un componente basado en el framework v2 de Macromedia. Puede ser usado con cualquier componente basado en mx.core.UIObject, y lo que hace es servir de punto de anclaje para cambiar el tamaño del componente mediante el ratón. El ejemplo más claro serí­a usarlo con el componente mx.containers.Window pero como digo puede ser utilizado como complemento a cualquier otro componente (DataGrids, ScrollPanes, etc…) de manera muy sencilla.

Resizable Window

El componente utiliza la clase CursorManager para cambiar el cursor a otro más apropiado para la tarea que se está realizando. Además su uso es muy sencillo gracias a un método de ayuda estático incluido dentro de la clase:

	
		// --- Importar la clase
		import com.carlosrovira.controls.ResizeHandler;
		
		// --- Crea un control para cambiar el tamaño en la esquina inferior derecha (BR)
		ResizeHandler.createResizeHandler(this, ResizeHandler.BR);

El propio componente se encarga de registrar los eventos necesarios. Además disponemos de algunos más para desabilitar el componente o realizar acciones cuando empezemos o terminemos la operación de ajuste de tamaño.

A continuación la clase en cuestión:

import mx.core.UIObject;
import mx.utils.Delegate;

import com.carlosrovira.managers.CursorManager;

/*
 	Class: ResizeHandler
	
	Creates a resize handler in a parent component (based on v2 MM framework) that handles all resize logic.

	Author:
		Carlos Rovira - carlos.rovira@nulllycos.es
		
		<http ://www.carlosrovira.com>
		
		Many thanks to Manish Jethani for his inspirational post
		entitled "Resizable TitleWindow in Flex" at http://manish.revise.org/2005/04/resizable-titlewindow-in-flex.html

	Package:
		com.carlosrovira.controls
	
	Usage:
		
		// --- Imports the class in the parent component
		import com.carlosrovira.controls.ResizeHandler;
		
		// --- Create as many resize handlers as you need (all resize logic is self managed by the handler)
		ResizeHandler.createResizeHandler(this, ResizeHandler.TL);
		ResizeHandler.createResizeHandler(this, ResizeHandler.T);
		ResizeHandler.createResizeHandler(this, ResizeHandler.TR);
		ResizeHandler.createResizeHandler(this, ResizeHandler.R);
		ResizeHandler.createResizeHandler(this, ResizeHandler.BR);
		ResizeHandler.createResizeHandler(this, ResizeHandler.B);
		ResizeHandler.createResizeHandler(this, ResizeHandler.BL);
		ResizeHandler.createResizeHandler(this, ResizeHandler.L);
	
	Custom Events:
		
		// --- Parent object (viewRef) is added automaticaly as a Listener
		resizeHandlerPressed : Fired when resize handler is pressed
		resizeHandlerReleased : Fired when resize handler is released
		
		Listen to parent "resize" events.
		
		parent could dispatch "revealHandler" and "hideHandler" to disable handler
	
	Notes:
	
		Use minWidth and minHeight (or setMinWidth and setMinHeight)to define the minimun width & height
			// --- Sets min Width & Height
			minWidth = 80;
			minHeight = 80;
		
		If you need other thickness or offset you can pass as params
		ResizeHandler.createResizeHandler(this, ResizeHandler.BL, 3, 3);
		
	See Also:
	
	    <cursormanager>
*/
class com.carlosrovira.controls.ResizeHandler extends UIObject {
	
	static var symbolName:String = "ResizeHandler";
	static var symbolOwner:Object = ResizeHandler;
	var className:String = "ResizeHandler";
	
	public static var TL:Number = 1;
	public static var T:Number = 2;
	public static var TR:Number = 3;
	public static var R:Number = 4;
	public static var BR:Number = 5;
	public static var B:Number = 6;
	public static var BL:Number = 7;
	public static var L:Number = 8;
	
	private var cursorId:Number = null;
	
	private var regX:Number;
	private var regY:Number;
	
	private var xOffset:Number;
	private var yOffset:Number;
	
	private var __xM:Number;
	public function get xM():Number {
		return viewRef._xmouse;
	}
	
	private var __yM:Number;
	public function get yM():Number {
		return viewRef._ymouse;
	}
	
	private var __xP:Number;
	public function get xP():Number {
		return viewRef.x;
	}
	
	private var __yP:Number;
	public function get yP():Number {
		return viewRef.y;
	}
	
	private var __wP:Number;
	public function get wP():Number {
		return viewRef.width;
	}
	
	private var __hP:Number;
	public function get hP():Number {
		return viewRef.height;
	}
	
	private var __mWP:Number;
	public function get mWP():Number {
		return viewRef.minWidth;
	}
	
	private var __mHP:Number;
	public function get mHP():Number {
		return viewRef.minHeight;
	}
	
	// --- viewRef
	private var __viewRef:Object;
	public function get viewRef():Object {
		return __viewRef;
	}
	public function set viewRef(value:Object):Void {
		__viewRef = value;
	}
	
	// --- location
	private var __location:Number;
	public function get location():Number {
		return __location;
	}
	public function set location(value:Number):Void {
		__location = value;
	}
	
	// --- edgeRange
	private var __edgeRange:Number = 4;
	public function get edgeRange():Number {
		return __edgeRange;
	}
	public function set edgeRange(value:Number):Void {
		__edgeRange = value;
	}
	
	// --- offset
	private var __offset:Number = 0;
	public function get offset():Number {
		return __offset;
	}
	public function set offset(value:Number):Void {
		__offset = value;
	}

	private function init():Void {
		super.init();
		
		_alpha = 0;
		
		onRollOver = Delegate.create(this, rollOverHandler);
		onRollOut = Delegate.create(this, rollOutHandler);
		
		onPress = Delegate.create(this, pressResizeHandler);
		onRelease = Delegate.create(this, releaseResizeHandler);
		onReleaseOutside = Delegate.create(this, releaseOutsideResizeHandler);
		
		addEventListener("resizeHandlerPressed", viewRef);
		addEventListener("resizeHandlerReleased", viewRef);
		
		viewRef.addEventListener("resize", this);
		viewRef.addEventListener("revealHandler", this);
		viewRef.addEventListener("hideHandler", this);
		
		doLater(this, "locateHandler");
		doLater(this, "sizeHandler");
	}
	
	// --- Use this helper method to create the handler
	public static function createResizeHandler(parent:UIObject, location:Number, edgeRange:Number, offset:Number):ResizeHandler {
		
		if (edgeRange == undefined)
			edgeRange = 4;
		
		if (offset == undefined)
			offset = 0;
			
		var initObj:Object = {
			viewRef:parent, 
			location:location,
			edgeRange:edgeRange,
			offset:offset
		}
		
		return ResizeHandler(parent.createClassObject(ResizeHandler, location.toString(), parent.getNextHighestDepth(), initObj));
	}
	
	// --- Roll over handler -> show resize cursor
	private function rollOverHandler():Void {
		
		switch (location) {
			
			case ResizeHandler.TL:
				showResizeHandle("diagonalBR");
				break;
			
			case ResizeHandler.T:
				showResizeHandle("vertical");
				break;
			
			case ResizeHandler.TR:
				showResizeHandle("diagonalBL");
				break;
			
			case ResizeHandler.R:
				showResizeHandle("horizontal");
				break;
				
			case ResizeHandler.BR:
				showResizeHandle("diagonalBR");
				break;
				
			case ResizeHandler.B:
				showResizeHandle("vertical");
				break;
			
			case ResizeHandler.BL:
				showResizeHandle("diagonalBL");
				break;
				
			case ResizeHandler.L:
				showResizeHandle("horizontal");
				break;
		}
	}
	
	// --- Roll out handler -> remove resize cursor
	private function rollOutHandler():Void {
		
		removeCursor(cursorId);
		
		cursorId = null;
	}
	
	// --- Press handler -> put component in top z-index and start moving/resizing
	private function pressResizeHandler():Void {
		
		regX = xM;
		regY = yM;
		
		dispatchEvent({type:"resizeHandlerPressed"});
		
		onMouseMove = mouseMoveResizeHandler;
	}
	
	// --- Stop moving/resizing (but don't remove current cursor!!!)
	private function releaseResizeHandler():Void {
		
		dispatchEvent({type:"resizeHandlerReleased"});
		
		delete onMouseMove;
	}
	
	// --- stop moving/resizing and remove current cursor
	private function releaseOutsideResizeHandler():Void {
		
		delete onMouseMove;
		
		removeCursor(cursorId);
		
		cursorId = null;
	}
	
	// --- Moving/resizing logic based on v2 methods : setSize and move
	private function mouseMoveResizeHandler():Void {
		
		xOffset = xM - regX;
		yOffset = yM - regY;
		
		switch(location) {
			
			case ResizeHandler.TL:
				if (wP - xM >= mWP) {
					viewRef.move(xP + xOffset, yP);
					viewRef.setSize(wP - xOffset, hP);
				}
				if (hP - yM >= mHP) {
					viewRef.move(xP, yP + yOffset);
					viewRef.setSize(wP, hP - yOffset);
				}
				break;
			
			case ResizeHandler.T:
				if (hP - yM >= mHP) {
					viewRef.move(xP, yP + yOffset);
					viewRef.setSize(wP, hP - yOffset);
				}
				break;
			
			case ResizeHandler.TR:
				if (xM >= mWP) {
					viewRef.setSize(wP + xOffset, hP);
				}
				if (hP - yM >= mHP) {
					viewRef.move(xP, yP + yOffset);
					viewRef.setSize(wP, hP - yOffset);
				}
				break;
			
			case ResizeHandler.R:
				if (xM >= mWP) {
					viewRef.setSize(wP + xOffset, hP);
				}
				break;
				
			case ResizeHandler.BR:
				if (xM >= mWP)
					viewRef.setSize(wP + xOffset, hP);
				if (yM >= mHP)
					viewRef.setSize(wP, hP + yOffset);
				break;
				
			case ResizeHandler.B:
				if (yM >= mHP) {
					viewRef.setSize(wP, hP + yOffset);
				}
				break;
			
			case ResizeHandler.BL:
				if (wP - xM >= mWP) {
					viewRef.move(xP + xOffset, yP);
					viewRef.setSize(wP - xOffset, hP);
				}
				if (yM >= mHP) {
					viewRef.setSize(wP, hP + yOffset);
				}
				break;
				
			case ResizeHandler.L:
				if (wP - xM >= mWP) {
					viewRef.move(xP + xOffset, yP);
					viewRef.setSize(wP - xOffset, hP);
				}
				break;
		}
		
		regX = xM;
		regY = yM;
		
		updateAfterEvent();
	}
	
	// --- CursorManager proxy functions
	private function showResizeHandle(orientation:String):Void {
		
		if (cursorId == null) {
			
			switch(orientation) {
				case "vertical":
					cursorId = setCursor("vResizeCursor");
					break;
				case "horizontal":
					cursorId = setCursor("hResizeCursor");
					break;
				case "diagonalBR":
					cursorId = setCursor("dBRResizeCursor");
					break;
				case "diagonalBL":
					cursorId = setCursor("dBLResizeCursor");
					break;
			}
		}
	}
	
	private function setCursor(cursorSkin:String):Number {
		
		return CursorManager.getCursorManager().setCursor(cursorSkin);
	}

	private function removeCursor(cursorId:Number):Void {
		
		CursorManager.getCursorManager().removeCursor(cursorId);
	}
	
	// --- Updates hanlder's position
	private function locateHandler():Void {
		
		switch (location) {
			
			case ResizeHandler.TL:
				move(-edgeRange + offset, -edgeRange + offset);
				break;
			
			case ResizeHandler.T:
				move(edgeRange + offset, -edgeRange + offset);
				break;
			
			case ResizeHandler.TR:
				move(wP - edgeRange - offset, -edgeRange + offset);
				break;
			
			case ResizeHandler.R:
				move(wP - edgeRange - offset, edgeRange + offset);
				break;
				
			case ResizeHandler.BR:
				move(wP - edgeRange - offset, hP - edgeRange - offset);
				break;
				
			case ResizeHandler.B:
				move(edgeRange + offset, hP - edgeRange - offset);
				break;
			
			case ResizeHandler.BL:
				move(-edgeRange + offset, hP - edgeRange - offset);
				break;
				
			case ResizeHandler.L:
				move(-edgeRange + offset, edgeRange + offset);
				break;
		}
	}
	
	// --- Updates handler's size
	private function sizeHandler():Void {
		
		switch (location) {
			
			case ResizeHandler.TL:
				setSize(2*edgeRange, 2*edgeRange);
				break;
			
			case ResizeHandler.T:
				setSize(wP-2*edgeRange - 2*offset, 2*edgeRange);
				break;
			
			case ResizeHandler.TR:
				setSize(2*edgeRange, 2*edgeRange);
				break;
			
			case ResizeHandler.R:
				setSize(2*edgeRange, hP-2*edgeRange - 2*offset);
				break;
				
			case ResizeHandler.BR:
				setSize(2*edgeRange, 2*edgeRange);
				break;
				
			case ResizeHandler.B:
				setSize(wP-2*edgeRange - 2*offset, 2*edgeRange);
				break;
			
			case ResizeHandler.BL:
				setSize(2*edgeRange, 2*edgeRange);
				break;
				
			case ResizeHandler.L:
				setSize(2*edgeRange, hP-2*edgeRange - 2*offset);
				break;
		}
	}
	
	// --- Event handlers for parent events
	
		private function resize():Void {
			
			// --- Updates hanlder's position
			locateHandler();
			
			// --- Updates handler's size
			sizeHandler();
		}
		
		private function revealHandler():Void {
			
			setVisible(true);
		}
		
		private function hideHandler():Void {
			
			setVisible(false);
		}
}

ResizeHandler.as

Un ejemplo básico con el componente Window serí­a el siguiente:

import mx.containers.Window;
import com.carlosrovira.controls.ResizeHandler;

class ResizeWindowTest {
	

	private var scopeRef:MovieClip;
	private var resizableWindow:Window;
	
	function ResizeWindowTest(scope:MovieClip) {
		
		scopeRef = scope;
		
		// --- The Window instance
		resizableWindow = scopeRef.createClassObject(Window, "resizableWindow", 0);
		
		// --- Sets Window min Width & Height
		resizableWindow.setMinWidth(80);
		resizableWindow.setMinHeight(80);
		
		// --- Create handlers for resizing
		createResizeHandlers(resizableWindow);
		
		// --- Layout window
		resizableWindow.setSize(200, 200);
		resizableWindow.move(Stage.width/2 - resizableWindow.width/2, Stage.height/2 - resizableWindow.height/2);
		
		// --- Use custom events like hideHandler to disable resizing
		//resizableWindow.dispatchEvent({type:"hideHandler"});
	}
	
	private function createResizeHandlers(resizableInstance:Window):Void {
		
		// --- Create all resize handlers
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.TL);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.T);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.TR);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.R);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.BR);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.B);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.BL);
		ResizeHandler.createResizeHandler(resizableInstance, ResizeHandler.L);
	}
	
	public static function main():Void {
		
		var resizeWindowTest:ResizeWindowTest = new ResizeWindowTest(_root);
	}
}

Podeis bajar un proyecto de ejemplo entero creado con FAME (pero que compila igualmente con Flash MX 2004 descomentando la linea que arranca la aplicación en el primer frame):

En caso de compilar con FAME no olvideis:

  • Usar la opción -mx (por usar componentes de Macromedia)
  • En Path to swf utilizar {eclipse-workspace}\ResizableWindow\deploy\ResizableWindow.swf
  • Root (main) class {eclipse-workspace}\ResizableWindow\src\ResizeWindowTest.as

16 Comentarios

  1. Muy buena,
    lo veo muy útil para aplicaciones de múltiples ventanas ( dentro del marco Flash ).
    Se podrí­a combinar muy bien con la clase GridArranger que sacaron hace poco en EML labs: http://www.emllabs.com/article.php?articleId=117 o con algún mánager que se ocupara de situar y redimensionar automáticamente los componentes contenidos acorde con el tamaño de la ventana.
    Creo que Flex ya lo lleva implementado en el framework no?

  2. Hola Joan,

    Esta clase está hecha con ese motivo en mente. Funciona bastante bien con Ventanas solapadas pues no deja pasar el foco del ratón a la ventana que tengamos debajo (siempre y cuando tengamos algún componente en esa ventana que detenga el foco a ese nivel…por supuesto).

    Al principio intenté usar la clase de Manish Jethani (que comento en el código), pero el problema con esta aproximación era que el control de las ventanas al estar basado simplemente en los movimientos del ratón no entendí­a de ventanas superpuestas y podí­as terminar controlando el tamaño de ventanas que están por debajo de otras.

    Por tanto si no vas a usar ventanas que se solapen la versión de Manish es muy valida, si las ventanas se solapan…entonces mejor usar esta aproximación.

    Una de las ventajas que tiene Flex son los Layout Managers que permiten delegar el redimensioando de los componentes dentro de un contenedor a dicho contenedor, esto salva mucho tiempo. El GridArranger no he llegado a verlo pero se que Joey Lott tiene implementados algunos containers para Flash MX 2004 en su librerí­a ASCB…pero todaví­a no he realizado ningúna prueba al respecto.

  3. Muy buena solución! sí­ señor!

    Sólo una pregunta. Cuando pones:
    onPress = Delegate.create(this, pressResizeHandler);

    Es sólo por purí­smo no? 😉 Qué problema le verí­as a sobreescribir directamente el método onPress en vez de reasignarlo como propiedad?

    Saludos
    X.

  4. Hola Xavi,

    La verdad es que en esta clase en concreto no es necesario. Puesto que el propio objeto es el propietario de los eventos (y no un clip anidado). Así­ que se puede considerar “purismo” 😉 … aunque tampoco fué intencionado, solo que, ya sabes, desde que empiezas a escribir un código hasta que lo terminas pasa por multitud de fases :).

    En cambio en el caso que Joan comentó en el post sobre la clase CursorManager considero que esta es una “Best Practice” puesto que estás asignando el ambito para que “this” signifique lo deberí­a realmente ser. Es decir redirigiendo el ambito de subelementos del objeto en cuestión a métodos de la clase, con lo que a la larga tendrás menos problemas para usar miembros de la clase, y en general no sufriras de los problemas del “scope”.

  5. Hola Carlos,
    Antes que nada te felicito, es una excelente clase.
    Un pregunta:
    Como pued captar algunos de estos eventos ?
    Custom Events:
    // Parent object (viewRef) is added automaticaly as a Listener
    // resizeHandlerPressed : Fired when resize handler is pressed
    // resizeHandlerReleased : Fired when resize handler is released

    saludos….. 🙂

  6. Hola,

    fijate en las lineas 72 y 73:

    addEventListener(“resizeHandlerPressed”, viewRef);
    addEventListener(“resizeHandlerReleased”, viewRef);

    El objeto que alberga el resizehandler ya está subscrito a estos eventos, por lo tanto solo tienes que crear un método en dicha clase:

    private function resizeHandlerPressed():Void {
    // — trace de flashout en FAME
    TRACE(Flashout.DEBUG + “resizeHandlerPressed…”);

    }

  7. Estimado Carlos:
    Cree el metodo en ResizeWindowTest, pero no funciona.(no es el “scope” coorecto.
    )
    Este es mi codigo:
    /*************** _root layer1, frame 1 *********************/
    /*
    * como tu sabras esta funcion capta todos los eventos
    * que emite el componente en cuestion (la utilizo para DEBUG)
    */
    function handleEvent(evt) {
    trace (“Event Fired “+evt.type+” by: “+evt.target);
    }
    /* suscribo los eventos*/
    resizableWindow.addEventListener (“resizeHandlerReleased”, this);
    resizableWindow.addEventListener (“resizeHandlerPressed”, this);
    resizableWindow.addEventListener (“click”, this);
    resizableWindow.addEventListener (“move”, this);
    resizableWindow.addEventListener (“resize”, this);
    resizableWindow.closeButton = true;

    /*************************************************/
    /* ver com.carlosrovira.ResizeHandler.as from line:159 ~ 180*/
    private function init():Void {
    super.init();
    _alpha = 0;
    onRollOver = Delegate.create(this, rollOverHandler);
    onRollOut = Delegate.create(this, rollOutHandler);
    onPress = Delegate.create(this, pressResizeHandler);
    onRelease = Delegate.create(this, releaseResizeHandler);
    onReleaseOutside = Delegate.create(this, releaseOutsideResizeHandler);
    /*
    * @ He aqui el error creo.
    addEventListener(“resizeHandlerPressed”, viewRef);
    addEventListener(“resizeHandlerReleased”, viewRef);
    * @ el evento no es captado pues no tiene el alcanze correcto
    * @ utilize Delegate, para resolver esto..
    */
    /*
    * @ New Added to fix the event scope
    addEventListener(“resizeHandlerReleased”, Delegate.create(viewRef,releaseResizeHandler));
    addEventListener(“resizeHandlerPressed”, Delegate.create(viewRef,pressResizeHandler));
    *
    */

    viewRef.addEventListener(“resize”, this);
    viewRef.addEventListener(“revealHandler”, this);
    viewRef.addEventListener(“hideHandler”, this);

    doLater(this, “locateHandler”);
    doLater(this, “sizeHandler”);
    }
    /* End com.carlosrovira.ResizeHandler */

    Ahora con esto el Listener(escuchador) que tengo en el _level0 capta los eventos que emite el resize handler
    atravez de el UIcomponent.

    Gracias por aporta a la comunidad tu conocimiento. 😉

  8. luchyx, la clase ResizeHandler está creada para complementar a un componente de la v2 como comento en el post. Si bajas el zip adjunto podrás ver un ejemplo con el componente Window.

    No se cual es tu intención al ejecutar ese código desde la linea principal de tiempo. Si intentas hacer funcionar el código de otra manera es normal que los efectos no sean los esperados.

    De todas formas si estás buscando algún otro tipo de funcionalidad puede que esta clase te sirva modificando el código y adaptandola a tus necesidades.

    Un saludo.

  9. Estimado carlos, primeramente baje el ZIP y me parecio de una performance excelente. Y decidí­ utilizarlo.
    La custion es la siguiente.
    Quiero carptar los eventos “resizeHandlerPressed” y “resizeHandlerReleased” por lo cual a la instancia creada del componente Window en este caso, le agrego los listeners.
    compresizable_win.addEventListener(“resizeHandlerReleased”, this);
    compresizable_win.addEventListener(“resizeHandlerPressed”, this);
    compresizable_win.addEventListener(“resize”, this);
    // captadora de eventos
    function handleEvent(evt) {
    trace(“Event Fired”+evt.type+” by: “+evt.target);
    }
    Puedo captar los eventos click, resize , move ..pero no los eventos “resizeHandlerPressed” o “resizeHandlerReleased”.
    Tambien cree un methodo en la clase RezizableWindowTest
    //@ method function resizeHandlerReleased(evt:objt)
    , como dijiste, pero no funciona.

    Tu has provado de captar estos eventos ?
    Aqui veráz mi archivo
    http://cablemodem.fibertel.com.ar/riaevolution/ResizableWindow.swf

    Saludos cordiales.

  10. El código está probado y funcionando en uno de mis proyectos. De hecho, las lineas donde añades los listeners para esos eventos (salvo “resize”), no hacen falta pues como ya te comenté el propio componente las añade directamente.

    Si quieres mandame tu archivo y lo intento mirar el fin de semana.

  11. apologí­a para babelfish inglés español conversión, pero yo apenas desear para decir qué uno elegante y simple solución carencia volver a clasificar según el tamaño en estándar v2 ventana componente.
    Muy agradable… Y pienso sus miradas del sitio que interesan también, y ayudará con mi del español :-/

    Cheers!

  12. Hi Carlos,

    Very impressive component.
    Downloaded your .fla file and looked at your .swf.

    I am new to Flash actionscript and have to ask for your help:
    When I try to generate a .swf from your fla. file, I get a lot of error saying
    “‘com.carlosrovira.controls.ResizeHandler’ could not be loaded.”

    How do I implement a trial in MX2004?

    ciao
    Ruggero 😉

  13. Pingback: DEJA-VUE.NET » AS2 – getTextExtent, un bug sacrement tordu

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *