package omino { import mx.core.*; import mx.controls.*; import flash.display.*; import flash.events.*; import flash.filters.*; import flash.geom.*; /** * This class lets you position one or more "markers" atop * another UIComponent. You can add a marker, specifying * its position and color. A mouse-listener is registered, * and all markers are draggable, within the rectangular * bounds of the parent. * You can set a callback listener to find out when * markers are moved. */ public class Markers { /** Array of markers. Each marker is a plain old object, so you may * attach ancillary information to it. */ public var markers:Array = new Array(); /** The parent component, set during creation */ private var u:UIComponent; private var draggingIndex:int; // or -1 for nobody dragging. private var draggingDiffX:Number; private var draggingDiffY:Number; private var selectedIndex:int; // or -1 for nobody dragging. private var maxSelectionDistance:Number = 12; // must be this many pixels to highlight a pip... private var markerLayer:UIComponent; private var label:Label; // we reuse this label as needed... private var debug:Text; // also reuse... private var listener:Function; /** Create a new set of markers, associated with a particular parent component. */ public function Markers(u:UIComponent) { super(); this.u = u; this.draggingIndex = -1; this.selectedIndex = -1; this.listener = null; this.markerLayer = new UIComponent(); this.markerLayer.visible = false; u.addChild(this.markerLayer); this.markerLayer.x = 0; this.markerLayer.y = 0; this.markerLayer.width = this.u.width; this.markerLayer.height = this.u.height; this.markerLayer.mouseChildren = false; var shadow:DropShadowFilter = new DropShadowFilter(); shadow.distance = 2.3; shadow.angle = 25; shadow.alpha = 0.5; this.markerLayer.filters = [shadow]; this.u.addEventListener(MouseEvent.MOUSE_MOVE,this.mousey); this.u.addEventListener(MouseEvent.MOUSE_DOWN,this.mousey); this.u.addEventListener(MouseEvent.MOUSE_UP,this.mousey); //this.u.stage.addEventListener(MouseEvent.MOUSE_UP,this.mousey); this.u.addEventListener(MouseEvent.MOUSE_OUT,this.mousey); this.u.addEventListener(MouseEvent.MOUSE_OVER,this.mousey); this.label = new Label(); this.label.text =""; this.label.toolTip = ""; this.label.x = 20; this.label.y = 30; this.label.width = 20; this.label.height = 20; this.label.alpha = 0.6; this.label.mouseEnabled = false; // not block our clicks! this.label.setStyle("color",0xffffff); this.label.filters = [shadow]; this.u.addChild(label); this.debug = new Text(); this.debug.text =""; this.debug.x = 0; this.debug.y = 190; this.debug.width = 1200; this.debug.height = 200; this.debug.mouseEnabled = false; // not block our clicks! this.debug.visible = false; this.u.addChild(debug); } /** Set a listener callback to find out when a marker is moved. * Only one callback can be registered, or null to clear it. * The callback must accept arguments (m:Object) for the marker that moved. */ public function setListener(listener:Function):void { this.listener = listener; } /** Add a marker to the collection. The new marker object is returned. */ public function addMarker(x:Number, y:Number, color:int = 0xffffff,name:String = ""):Object { var marker:Object = new Object(); marker.x = Math.floor(x); marker.y = Math.round(y); marker.color = color; marker.name = name; marker.radius = 2.2; this.markers.push(marker); this.draw(); return marker; } private function fmt(n:Number,decimals:int):String { var i:int = Math.floor(n); var f:int = (n - i) * Math.pow(10,decimals); var s:String = ""; for(var j:int = 0; j < decimals; j++) { s = "" + f % 10 + s; f = Math.floor(f / 10); } s = "" + i + "." + s; return s; } public function draw():void { this.debug.text += "\nDRAW"; this.markerLayer.x = 0; this.markerLayer.y = 0; this.markerLayer.width = this.u.width; this.markerLayer.height = this.u.height; this.markerLayer.graphics.clear(); this.markerLayer.graphics.beginFill(0x0000ff); this.markerLayer.graphics.drawRect(0,0,10,10); this.markerLayer.graphics.endFill(); var g:Graphics = this.markerLayer.graphics; g.clear(); for(var i:int = 0; i < this.markers.length; i++) { var m:Object = this.markers[i]; var rad:Number = m.radius; var color:int = m.color; if(i == this.selectedIndex) { rad = m.radius * 2; } if(i == this.draggingIndex) { rad = m.radius * 3; } g.lineStyle(1,0xffffff); g.beginFill(color); g.drawCircle(m.x,m.y,rad); g.endFill(); } var m2:Object = null; if(this.selectedIndex >= 0) m2 = this.markers[this.selectedIndex]; else if(this.draggingIndex >= 0) m2 = this.markers[this.draggingIndex]; if(m2 != null) { this.label.text = m2.name + " (" + fmt(m2.x,2) + "," + fmt(m2.y,2) + ")"; this.label.height = this.label.textHeight + 4; this.label.width = this.label.textWidth + 8; this.label.x = m2.x; this.label.y = m2.y - 13; this.label.opaqueBackground = 0x000000; } else { this.label.text = ""; this.label.opaqueBackground = null; } } private function dist(x1:Number,y1:Number,x2:Number,y2:Number):Number { var dx:Number = x2 - x1; var dy:Number = y2 - y1; var d:Number = dx * dx + dy * dy; d = Math.sqrt(d); return d; } public function mousey(e:MouseEvent):void { this.markerLayer.width = this.u.width; this.markerLayer.height = this.u.height; var m:Object; //var p:Point = new Point(e.stageX,e.stageY); //p = this.markerLayer.globalToContent(p); var p:Point = new Point(0,0); p.x = this.markerLayer.contentMouseX; p.y = this.markerLayer.contentMouseY; // any time p is outside the markerLayer, we act mouse-out-ey... if(p.x < 0 || p.x >= this.markerLayer.width || p.y < 0 || p.y >= this.markerLayer.height) { this.selectedIndex = -1; this.markerLayer.visible = false; this.label.visible = false; this.draw(); return; } // else this.markerLayer.visible = true; this.label.visible = true; if(e.type == MouseEvent.MOUSE_OVER) { this.markerLayer.visible = true; this.draw(); } else if(e.type == MouseEvent.MOUSE_OUT) { // ALREADY DEALT WITH. } else if(e.type == MouseEvent.MOUSE_UP) { this.selectedIndex = this.draggingIndex; this.draggingIndex = -1; this.draw(); } else if(e.type == MouseEvent.MOUSE_DOWN) { this.draggingIndex = this.selectedIndex; this.selectedIndex = -1; if(this.draggingIndex >= 0) { m = this.markers[this.draggingIndex]; this.draggingDiffX = p.x - m.x; this.draggingDiffY = p.y - m.y; this.draw(); } } else if(e.type == MouseEvent.MOUSE_MOVE) { this.debug.text = "mouse: " + p.x + "," + p.y; if(this.draggingIndex >= 0) { m = this.markers[this.draggingIndex]; m.x = p.x - this.draggingDiffX; m.y = p.y - this.draggingDiffY; if(m.x < 0) m.x = 0; if(m.x >= this.markerLayer.width) m.x = this.markerLayer.width; if(m.y < 0) m.y = 0; if(m.y >= this.markerLayer.height) m.y = this.markerLayer.height; this.draw(); if(this.listener != null) { this.listener(m); } } else { // find the closest pip... var closestDist:Number = this.maxSelectionDistance; var closestIx:int = -1; for(var i:int = 0; i < this.markers.length; i++) { m = this.markers[i]; var dist:Number = dist(m.x,m.y,p.x,p.y); this.debug.text += " "+dist; if(dist < closestDist) { this.debug.text += "*"; closestDist = dist; closestIx = i; } } //if(closestIx != this.selectedIndex) { this.selectedIndex = closestIx; this.draw(); } } } } } }