3
votes

I think there is a simple solution to this question, just not simple enough for me to find it.

Question: How do you constrain a TitleWindow in Flex 3 from being dragged off the screen/stage? Is there a way to restrict the TitleWindow to the viewing area?

Example: Let's say I have an application that take 100% of the screen. Next, I create a TitleWindow via the PopUpManager. I can then proceed to click and hold (drag) that window off the screen, then release the mouse button. That window is now lost off-screen somewhere. Is there a way to keep the window from being dragged beyond the viewing area?

Thanks for the help in advance.

7

7 Answers

8
votes

this is a very old post, but here's another way of doing it: Whether you are extending the component or not, in the TitleWindow definition add the following line: move:"doMove(event)" Import the Application library (import mx.core.Application;) and add the doMove function:

private function doMove(event:Event):void
{//keeps TW inside layout
    var appW:Number=Application.application.width;
    var appH:Number=Application.application.height;
    if(this.x+this.width>appW)
    {
        this.x=appW-this.width;
    }
    if(this.x<0)
    {
        this.x=0;
    }
    if(this.y+this.height>appH)
    {
        this.y=appH-this.height;
    }
    if(this.y<0)
    {
        this.y=0;
    }
}
2
votes

You can set its isPopUp property to false to prevent it from being dragged in the first place.

var popupWin:TitleWindow = PopUpManager.createPopUp(this, TitleWindow);
PopUpManager.centerPopUp(popupWin);
popupWin.isPopUp = false;

I don't know if the DragManager class in flex supports bounds checking, but if you really want to allow dragging but limit its bounds, you can still set isPopUp to false and implement the dragging code yourself so that the component never goes outside the limits specified by you. Check startDrag() method for an example. Bounds rectangle is the key.

1
votes

Flex 4

<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" 
      xmlns:mx="library://ns.adobe.com/flex/mx"
      windowMoving="windowMovingHandler(event)">
.
.
.

protected function windowMovingHandler(event:TitleWindowBoundsEvent):void
{
  var appBounds:Rectangle = parentApplication.getBounds(DisplayObject(parentApplication));
  if(!appBounds.containsRect(event.afterBounds)){
    event.preventDefault();
  }
}

// for better precision, corect appBounds manualy, or, instead of "parentApplication.getBounds..." create new rectangle of your application size
0
votes

Subclass the TitleWindow and add a canvas over the title bar as a drag proxy. Then you can explicity call startDrag with a boundary rectangle.

This is pretty skeletal, but should put you on the path...

The reason for the proxy is you may get some weird behavior when you click the titleBar label if you don't have the canvas over it.

public class MyTitleWindow extends TitleWindow
{
    public var titleBarOverlay:Canvas;

    override protected function createChildren():void
    {
        super.createChildren();

        if(!titleBarOverlay)
        {
            titleBarOverlay = new Canvas();
            titleBarOverlay.width = this.width;
            titleBarOverlay.height = this.titleBar.height;
            titleBarOverlay.alpha = 0;
            titleBarOverlay.setStyle("backgroundColor", 0x000000);
            rawChildren.addChild(titleBarOverlay);
        }

            addListeners();
    }

    override protected function updateDisplayList(w:Number, h:Number):void
    {
        super.updateDisplayList(w, h);

        titleBarOverlay.width = this.width;
        titleBarOverlay.height = this.titleBar.height;
    }

    private function addListeners():void
    {
        titleBarOverlay.addEventListener(MouseEvent.MOUSE_DOWN, onTitleBarPress, false, 0, true);
        titleBarOverlay.addEventListener(MouseEvent.MOUSE_UP, onTitleBarRelease, false, 0, true);
    }

    private function onTitleBarPress(event:MouseEvent):void
    {
        // Here you can set the boundary using owner, parent, parentApplication, etc.
        this.startDrag(false, new Rectangle(0, 0, parent.width - this.width, parent.height - this.height));
    }

    private function onTitleBarRelease(event:Event):void
    {
        this.stopDrag();
    }
}
0
votes

You could simply override the move function and prevent "illegal" movement (it is called internally by the Panel drag management).

I think that you also should listen on stage resize, because reducing it (e.g. if the user resize the browser window) could send your popup out of stage even without actually moving it.

public class MyTitleWindow extends TitleWindow {

    public function MyTitleWindow() {
        // use a weak listener, or remember to remove it
        stage.addEventListener(Event.RESIZE, onStageResize, 
                false, EventPriority.DEFAULT, true);
    }

    private function onStageResize(event:Event):void {
        restoreOutOfStage();
    }

    override public function move(x:Number, y:Number):void {
        super.move(x, y);
        restoreOutOfStage();
    }

    private function restoreOutOfStage():void {
        // avoid the popup from being positioned completely out of stage
        // (use the actual width/height of the popup instead of 50 if you
        // want to keep your *entire* popup on stage)
        var topContainer:DisplayObjectContainer =
                Application.application.parentDocument;
        var minX:int = 50 - width;
        var maxX:int = topContainer.width - 50;
        var minY:int = 0;
        var maxY:int = topContainer.height - 50;                
        if (x > maxX) 
            x = maxX
        else if (x < minX)
            x = minX;
        if (y > maxY) 
            y = maxY
        else if (y < minY)
            y = minY;
    }

}
0
votes

In your TitleWindow's creationComplete handler add the following:

this.moveArea.visible=false;

This will do the job.

On the other hand, if you have a custom skin, you can remove the "moveArea" part. This should work, too.