4 Replies Latest reply on Oct 22, 2007 3:34 AM by atta707

    Lines connecting UIComponents

    nwebb.co.uk
      Hi,

      I was recently given the following requirement: to create an interface which allows users to draw lines between multiple "entities" (i.e. people, places etc).

      In my interface each entity will probably be represented by a UIComponent of some kind - perhaps a panel containing an image and some text. The user should be allowed to draw/delete lines between each entity and the line will follow the entity as it's dragged.

      In Flash this is easy enough (I'd draw each line on an empty Sprite), but in Flex I'm not sure of the best approach, especially when mixing the Drawing API with UIComponents.

      In Flex, as I need to track the x and y positions of each entity, Canvas seemed the most obvious choice for my outer container, yet I can't add sprites directly to the Canvas (unless I wrap it in a UIContainer or use rawChildren). Should I do this, or is there a better way to approach the whole thing?

      I thought this is probably quite a common type of interface requirement so I hope to get some feedback here :]
      Thanks
        • 1. Re: Lines connecting UIComponents
          atta707 Level 2
          get the file at:

          http://weblogs.macromedia.com/khoyt/files/rviewer.zip

          open it up, run and see if it helps you get started. Google it up for Hoyt.

          ATTA
          • 2. Re: Lines connecting UIComponents
            atta707 Level 2
            I actually find the link to Kevin's original blog entry. Here it is:

            http://weblogs.macromedia.com/khoyt/archives/2006/11/index.cfm

            and as he says the connector should be lighter than a canvas. Perhaps a custom implementation of UIComponent.

            HTH.


            ATTA
            • 3. Re: Lines connecting UIComponents
              nwebb.co.uk Level 1
              Thanks ATTA, this looks like it's going to be extremely helpful
              • 4. Re: Lines connecting UIComponents
                atta707 Level 2
                sure. in one of recent project we had very simple requirement for "connecting" certain boxes with arrow. My reusable solution a class called connector:

                package drawing {
                import flash.display.Graphics;
                import flash.geom.Point;

                import mx.core.IUIComponent;
                import mx.core.UIComponent;

                public class Connector extends UIComponent {

                private static const ARROW_HEAD_DOWN:int = 0;
                private static const ARROW_HEAD_UP:int = 1;

                public static const FROM_TOP_RIGHT:int = 0;
                public static const FROM_BOTTOM_RIGHT:int = 1;
                public static const FROM_MIDDLE_RIGHT:int = 2;

                public static const TO_TOP_CENTER:int = 3;
                public static const TO_BOTTOM_CENTER:int = 4;


                protected var src:IUIComponent = null;
                protected var dest:IUIComponent = null;
                protected var startingPoint:Point = null;
                protected var destinationPoint:Point = null;

                protected var arrowDirection:int = ARROW_HEAD_DOWN;
                protected var startFrom:int = FROM_TOP_RIGHT;
                protected var finishAt:int = TO_TOP_CENTER;

                public function Connector(source:IUIComponent,
                destination:IUIComponent, startFrom:int = FROM_TOP_RIGHT, finishAt:int = TO_TOP_CENTER) {

                super();
                this.src = source;
                this.dest = destination;
                this.startFrom = startFrom;
                this.finishAt = finishAt;

                if (finishAt == TO_BOTTOM_CENTER) {
                this.arrowDirection = ARROW_HEAD_UP;
                }

                this.startingPoint = getStartingPoint();
                this.destinationPoint = getEndingPoint();
                }

                private function getStartingPoint() : Point {
                var x:int = this.src.x + this.src.width;
                var y:int = this.src.y + 10;

                switch(this.startFrom) {
                case FROM_TOP_RIGHT:
                x = this.src.x + this.src.width;
                y = this.src.y + 10;
                break;
                case FROM_MIDDLE_RIGHT:
                x = this.src.x + this.src.width;
                y = this.src.y + this.src + (height / 2);
                break;
                case FROM_BOTTOM_RIGHT:
                x = this.src.x + this.src.width;
                y = (this.src.y + this.src.height) - 10;
                break;
                }
                return new Point(x, y);
                }

                private function getEndingPoint() : Point {
                var x:int = this.dest.x + this.dest.width / 2;
                var y:int = this.dest.y;

                switch(this.finishAt) {
                case TO_BOTTOM_CENTER:
                x = this.dest.x + this.dest.width / 2;
                y = this.dest.y + this.dest.height;
                break;
                }
                return new Point(x, y);
                }

                override protected function measure() : void {
                super.measure();
                // this.measuredHeight = this.destinationPoint.y - this.startingPoint.y;
                // this.measuredWidth = this.destinationPoint.x - this.startingPoint.x;
                }
                override protected function updateDisplayList(_unscaledWidth:Number, _unscaledHeight:Number):void {
                super.updateDisplayList(_unscaledWidth, _unscaledHeight);

                var g:Graphics = this.graphics;

                g.clear();
                g.lineStyle(2, 0x5C67F3);

                g.moveTo(this.startingPoint.x, startingPoint.y);

                g.lineTo(destinationPoint.x , startingPoint.y);
                g.lineTo(destinationPoint.x, destinationPoint.y);

                drawArrowHead(destinationPoint, this.arrowDirection);
                }

                private function drawArrowHead(at:Point, direction:int = ARROW_HEAD_DOWN) : void {

                var g:Graphics = this.graphics;
                var x1:int = 0;
                var y1:int = 0 ;
                var x2:int = 0;
                var y2:int = 0;

                switch(direction) {
                case ARROW_HEAD_DOWN:
                x1 = 4; y1 = -4; x2 = -4; y2 = -4; break;
                case ARROW_HEAD_UP:
                x1 = -4; y1 = 4; x2 = 4; y2 = 4; break;
                }

                g.moveTo(at.x, at.y);
                g.lineTo(at.x + x1, at.y + y1);
                g.moveTo(at.x, at.y);
                g.lineTo(at.x + x2, at.y + y2);
                }
                } // end of class
                } // end of package

                and then

                var c:Connector = new Connector(this.blocksVBox, this.preTapeoutOutput);
                c.name = "connector1";
                this.bg.addChild(c);


                HTH.

                ATTA