feathersui / feathersui-starling

User interface components for Starling Framework and Adobe AIR
https://feathersui.com/learn/as3-starling/
Other
914 stars 386 forks source link

Using PickerList alongside NativeDragManager #1784

Closed kevinfoley closed 5 years ago

kevinfoley commented 5 years ago

We have a desktop application which lets the user drag content files into the application window using AIR's NativeDragManager support. This requires setting up a native display object with NativeDragEvent listeners

if (NativeDragManager.isSupported) {
    dragDropTarget = new Sprite();
    dragDropTarget.graphics.beginFill(0, 0); //draw with 0 alpha (invisible)
    dragDropTarget.graphics.drawRect(0, 0, Starling.current.nativeStage.stageWidth, Starling.current.nativeStage.stageHeight);
    dragDropTarget.graphics.endFill();
    Starling.current.nativeStage.addChild(dragDropTarget);
    Starling.current.nativeStage.addEventListener(flash.events.Event.RESIZE, onResize);

    dragDropTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onNativeDragIn);
    dragDropTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onNativeDragDrop);
}

However, the dragDropTarget in the above example interferes with some Feathers features like the PickerList which rely on the DefaultFocusManager to decide when to close. When we click the picker list button, the popup list appears. When we attempt to click an item in the popup list, the DefaultFocusManager interprets the click as being focused on the dragDropTarget sprite. This removes focus from the popup list, which causes it to close immediately without detecting that the user tried to click one of the options.

I've tried adding the NativeDragEvents to the native stage or to Starling's nativeOverlay, but neither approach seems to work. How might we resolve this?

joshtynjala commented 5 years ago

You might try listening for FocusEvent.MOUSE_FOCUS_CHANGE and calling event.preventDefault().

kevinfoley commented 5 years ago

@joshtynjala I tried this, but it doesn't seem to work (the event is never fired):

dragDropTarget.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, onDragDropTargetFocusChange, false, 0, true);

private function onDragDropTargetFocusChange(e:FocusEvent):void {
    e.stopImmediatePropagation();
    e.preventDefault();
}

Did you mean something else?

joshtynjala commented 5 years ago

That's what I meant. I didn't try it myself before suggesting it, though.

kevinfoley commented 5 years ago

@joshtynjala Since that doesn't work, do you have any other suggestions about how to resolve this?

joshtynjala commented 5 years ago

Unfortunately, no. I may be able to look at this in more detail when I have some time, but that may not be soon.

kevinfoley commented 5 years ago

@joshtynjala After reviewing the code for DefaultFocusManager, I figured out the correct solution:

//add to stage, not to problem display object. priority should be higher than 0 to make sure this listener is called before the listener in DefaultFocusManager
Starling.current.nativeStage.addEventListener(FocusEvent.MOUSE_FOCUS_CHANGE, onFocusChange, false, 100, true);

private function onFocusChange(e:FocusEvent):void {
    if (e.relatedObject == dragDropTarget) {
        e.stopImmediatePropagation();
        e.preventDefault();
    }
}