diff --git a/src/as/com/threerings/mx/controls/ScrollBox.as b/src/as/com/threerings/mx/controls/ScrollBox.as new file mode 100644 index 000000000..5e29e2386 --- /dev/null +++ b/src/as/com/threerings/mx/controls/ScrollBox.as @@ -0,0 +1,147 @@ +package com.threerings.mx.controls { + +import flash.display.DisplayObject; +import flash.display.Graphics; +import flash.display.Sprite; + +import flash.events.Event; +import flash.events.MouseEvent; + +import flash.geom.Matrix; +import flash.geom.Rectangle; + +import mx.containers.Canvas; + +/** + * A control for scrolling some component separately. + */ +public class ScrollBox extends Canvas +{ + public function ScrollBox ( + target :DisplayObject, maxWidth :int, maxHeight :int) + { + _target = target; + _maxWidth = maxWidth; + _maxHeight = maxHeight; + + opaqueBackground = 0xFFFFFF; + + _box = new Sprite(); + rawChildren.addChild(_box); + + recheckBounds(); + + // TODO: only when something changes + addEventListener(Event.ENTER_FRAME, enterFrame); + + _box.addEventListener(MouseEvent.MOUSE_DOWN, spritePressed); + } + + override public function setActualSize (w :Number, h :Number) :void + { + super.setActualSize(w, h); + + recheckBounds(); + + graphics.clear(); + graphics.lineStyle(1, 0); + graphics.drawRect(0, 0, width, height); + } + + protected function enterFrame (vent :Event) :void + { + if (!_dragging) { + recheckBounds(); + } + } + + /** + * Get the bounds of the scrollable area. Broken-out for easy overridding. + */ + protected function getScrollBounds () :Rectangle + { + var bounds :Rectangle = _target.transform.pixelBounds; +// var m :Matrix = _target.transform.concatenatedMatrix; +// bounds.topLeft = m.transformPoint(bounds.topLeft); +// bounds.bottomRight = m.transformPoint(bounds.bottomRight); + return bounds; + } + + protected function recheckBounds () :void + { + var bounds :Rectangle = getScrollBounds(); + + // see what's visible, what's not.. + var scroller :Rectangle = _target.scrollRect; + if (scroller == null) { + scroller = bounds.clone(); + } + + if ((_bounds != null) && _bounds.equals(bounds) && + (_scroller != null) && _scroller.equals(scroller)) { + return; + } + + _bounds = bounds.clone(); + _scroller = scroller; + + _scale = Math.min(1, + Math.min(_maxWidth / _bounds.width, _maxHeight / _bounds.height)); + width = _scale * _bounds.width; + height = _scale * _bounds.height; + + drawBox(_box, _scroller.width * _scale, _scroller.height * _scale); + + _box.x = (_scroller.x - _bounds.x) * _scale; + _box.y = (_scroller.y - _bounds.y) * _scale; + } + + protected function drawBox (box :Sprite, ww :Number, hh :Number) :void + { + var g :Graphics = _box.graphics; + g.clear(); + g.beginFill(0x0000FF); + g.drawRect(0, 0, ww, hh); + g.endFill(); + } + + protected function spritePressed (evt :MouseEvent) :void + { + _box.addEventListener(Event.ENTER_FRAME, boxUpdate); + _box.startDrag(true, + new Rectangle(0, 0, width - _box.width, height - _box.height)); + stage.addEventListener(MouseEvent.MOUSE_UP, spriteReleased); + _dragging = true; + } + + protected function spriteReleased (evt :MouseEvent) :void + { + stage.removeEventListener(MouseEvent.MOUSE_UP, spriteReleased); + _box.stopDrag(); + _box.removeEventListener(Event.ENTER_FRAME, boxUpdate); + _dragging = false; + } + + protected function boxUpdate (evt :Event) :void + { + var r :Rectangle = _target.scrollRect; + r.x = (_box.x / _scale) + _bounds.x; + r.y = (_box.y / _scale) + _bounds.y; + _target.scrollRect = r; + } + + protected var _dragging :Boolean; + + protected var _target :DisplayObject; + + protected var _maxWidth :int; + protected var _maxHeight :int; + + protected var _box :Sprite; + + protected var _scale :Number; + + protected var _bounds :Rectangle; + protected var _scroller :Rectangle; +} +}