package view { import flash.display.DisplayObject; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; internal class LayoutTarget { public var target:DisplayObject; //displayobject to layout public var scalable:Boolean; //define if target scales or not private var _relativeWidth:Number; //if scalable, this is the relative width (0.0 - 1.0) private var _relativeHeight:Number; //if scalable, this is the relative height (0.0 - 1.0) private var _minWidth:Number; //if scalable, this defines the minimal absolute width private var _minHeight:Number; //if scalable, this defines the minimal absolute height private var _maxWidth:Number; //if scalable, this defines the maximal absolute width private var _maxHeight:Number; //if scalable, this defines the maximal absolute height private var _maintainAspectRatio:Boolean; //specify if aspect ratio should be maintained private var _padding:Object; //this stores the position of the target relative to the sides private var _centerHorizontal:Boolean; private var _centerVertical:Boolean; private var _allowOverflow:Boolean; private var _updateFunction:Function; public function LayoutTarget(obj:DisplayObject, isScalable:Boolean) { _padding = {left: undefined, right: undefined, top: undefined, bottom: undefined }; target = obj; scalable = isScalable; _maintainAspectRatio = true; } //set the relative size to apply when updating. //width and height should be specified as factors (a number // between zero and one) of the stage size. public function setRelativeSize(relativeWidth:Number, relativeHeight:Number, maintainAspectRatio:Boolean, allowOverflow:Boolean):void { _relativeWidth = relativeWidth; _relativeHeight = relativeHeight; _maintainAspectRatio = maintainAspectRatio; _allowOverflow = allowOverflow; } //set a minimum and maximum width and height for the target object. //these are absolute values, and are not relative to any object. public function setSizeLimits(minWidth:Number = undefined, minHeight:Number = undefined, maxWidth:Number = undefined, maxHeight:Number = undefined):void { _minWidth = minWidth; _minHeight = minHeight; _maxWidth = maxWidth; _maxHeight = maxHeight; } //specify relative position on screen to maintain when resizing. //these values are always treated as absolute values. //only specified values will be used when defining the layout, // LEFT having priority over RIGHT and TOP over BOTTOM. //an error is thrown when too few values are supplied. in case of // too many values, the overflow is disregarded quietly. public function setPadding(types:Array, values:Array):void { if (values.length < types.length) throw new Error("LayoutManager.setPosition: too few padding values supplied"); for (var i:uint = 0; i < types.length; i++) { switch(types[i]) { case PaddingType.LEFT: _padding.left = values[i]; break; case PaddingType.RIGHT: _padding.right = values[i]; break; case PaddingType.TOP: _padding.top = values[i]; break; case PaddingType.BOTTOM: _padding.bottom = values[i]; break; } } } public function align(horizontal:String, vertical:String):void { switch(horizontal) { case PaddingType.CENTER: _centerHorizontal = true; break; default: setPadding([horizontal], [0]); _centerHorizontal = false; break; } switch(vertical) { case PaddingType.CENTER: _centerVertical = true; break; default: setPadding([vertical], [0]); _centerVertical = false; break; } } public function setUpdateFunction(updateFunction:Function):void { _updateFunction = updateFunction; } //sets the size of the registered object to meet specifications public function updateSize(w:Number, h:Number):void { if (scalable) { var matrix:Matrix = new Matrix(); //'unapply' any scaling to read actual size target.transform.matrix = matrix; //calculate necessary scaling, while keeping new size within specified limits var newWidth:Number = limit(_relativeWidth * w, _minWidth, _maxWidth); var newHeight:Number = limit(_relativeHeight * h, _minHeight, _maxHeight); var rect:Rectangle = _maintainAspectRatio ? maintainAspectRatio(newWidth, newHeight, target.width / target.height) : new Rectangle(0, 0, newWidth, newHeight); //this means always fill the specified size, don't show sidebars which will happen otherwise if maintainAspectRatio is true if (_allowOverflow) { var factor:Number; if (rect.width < _relativeWidth * w) { factor = (_relativeWidth * w) / rect.width; rect.width *= factor; rect.height *= factor; } else if (rect.height < _relativeHeight * h ) { factor = (_relativeHeight * h) / rect.height; rect.width *= factor; rect.height *= factor; } } matrix.scale(rect.width / target.width, rect.height / target.height); //apply correct scaling target.transform.matrix = matrix; } } //limit a given number to given values private function limit(nr:Number, min:Number, max:Number):Number { if (min) nr = Math.max(nr, min); if(max) nr = Math.min(nr, max); return nr; } //uses the given width, height and w/h ratio and returns a rectangle with the corrected width and height private function maintainAspectRatio(w:Number, h:Number, ratio:Number):Rectangle { if (w / h > ratio) w = h * ratio; else if (w / h < ratio) h = w / ratio; return new Rectangle(0, 0, w, h); } //sets the position of the registered object to meet specifications public function updatePosition(w:Number, h:Number):void { var origin:Point = target.globalToLocal(new Point(0, 0)); if (_padding.left != undefined) target.x += origin.x + (_padding.left < 1 ? _padding.left * w : _padding.left); else if (_padding.right != undefined) target.x += origin.x + w - (target.width + (_padding.right < 1 ? _padding.right * w : _padding.right)); if (_padding.top != undefined) target.y += origin.y + (_padding.top < 1 ? _padding.top * h : _padding.top); else if (_padding.bottom != undefined) target.y += origin.y + h - (target.height + (_padding.bottom < 1 ? _padding.bottom * h : _padding.bottom)); if (_centerHorizontal) target.x += origin.x + (w - target.width) / 2; if (_centerVertical) target.y += origin.y + (h - target.height) / 2; } public function customUpdate(w:Number, h:Number):void { if (_updateFunction != null) _updateFunction(w, h); } } }