// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.eclipse.che.ide.util.dom; import elemental.client.Browser; import elemental.events.Event; import elemental.events.EventListener; import elemental.events.EventTarget; import java.util.ArrayList; import java.util.List; /** Utility class which allows simulation of Event Capture. */ public class MouseEventCapture { private interface Remover { public void remove(); } /** Current mouse capture owner. */ private static MouseCaptureListener captureOwner; /** * These are the Remover objects for the hooks into Window that fire to this * class. We have exactly one listener per event type that supports capture. * When no UI component possesses capture (meaning that the captureOwner stack * is empty) we can disconnect these. */ private static final List<Remover> mouseRemovers = new ArrayList<>(); /** * Capture happens on a per listener instance basis. We throw the * CaptureListener on the top of the capture stack and then provide a handle * to an object that can be used to * * @param listener */ public static void capture(final MouseCaptureListener listener) { // Make sure to release the previous capture owner. if (captureOwner != null) { captureOwner.release(); } // Lazily initialize event hookups (this should be below the release above // since the above in turn clears the capture hookups) if (mouseRemovers.isEmpty()) { registerEventCaptureHookups(); } captureOwner = listener; listener.setCaptureReleaser(new CaptureReleaser() { @Override public void release() { // nuke the reference to this releaser in the listener (which should // still be the capture owner). listener.setCaptureReleaser(null); // nuke the captureOwner captureOwner = null; // Release the event listeners. for (int i = 0, n = mouseRemovers.size(); i < n; i++) { mouseRemovers.get(i).remove(); } mouseRemovers.clear(); } }); } private static Remover addCaptureEventListener(final String type, final EventTarget source, final EventListener listener) { source.addEventListener(type, listener, true); return new Remover() { @Override public void remove() { source.removeEventListener(type, listener, true); } }; } private static void forwardToCaptureOwner(Event event) { if (captureOwner != null) { captureOwner.handleEvent(event); } } /** * Registers for relevant events on the Window. * <p/> * Capture should be lazily initialized, and then destroyed each time nothing * has capture (as to not cause useless event dispatch and handling for events * like move events). */ private static void registerEventCaptureHookups() { mouseRemovers.add(addCaptureEventListener(Event.MOUSEMOVE, Browser.getWindow(), new EventListener() { @Override public void handleEvent(Event event) { forwardToCaptureOwner(event); } })); mouseRemovers.add(addCaptureEventListener(Event.MOUSEUP, Browser.getWindow(), new EventListener() { @Override public void handleEvent(Event event) { forwardToCaptureOwner(event); } })); mouseRemovers.add(addCaptureEventListener(Event.BLUR, Browser.getWindow(), new EventListener() { @Override public void handleEvent(Event event) { captureOwner.release(); } })); } }