All Downloads are FREE. Search and download functionalities are using the official Maven repository.

playn.core.Dispatcher Maven / Gradle / Ivy

/**
 * Copyright 2012 The PlayN Authors
 *
 * 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 playn.core;

import playn.core.AbstractLayer.Interaction;
import playn.core.Events.Input;

/** Some internal utilities for dispatching events. */
abstract class Dispatcher {

  static class CaptureState {
    AbstractLayer layer;
    AbstractLayer captured;
    Object capturedListener;
    boolean didCapture;

    void clear () {
      layer = null;
      captured = null;
      capturedListener = null;
      didCapture = false;
    }

    boolean check (boolean wasCaptured, AbstractLayer interacted) {
      if (!wasCaptured && didCapture) {
        captured = interacted;
      }
      return didCapture;
    }

    void capture () {
      if (!didCapture) {
        didCapture = true;
      } else {
        // TODO: warn about already being captured?
      }
    }
  }

  /** Dispatches events to a single layer. */
  static final Dispatcher SINGLE = new Dispatcher() {
    @Override
    public  void dispatch(
        AbstractLayer layer, Class listenerType, E event, Interaction interaction,
        Interaction cancel) {
      tryInteract(layer, listenerType, interaction, localize(event, layer));
    }
  };

  /** Dispatches events to a layer and all its parents. */
  static final Dispatcher PROPAGATING = new Dispatcher() {
    @Override
     void dispatch(
        AbstractLayer inLayer, Class listenerType, E event, Interaction interaction,
        Interaction cancel) {
      E localized = localize(event, inLayer);
      CaptureState ecap = event.captureState;
      if (ecap == null) {
        // make no attempts to capture, etc; just dispatch to layer + parents
        for (AbstractLayer ll = inLayer; ll != null; ll = (AbstractLayer)ll.parent()) {
          tryInteract(ll, listenerType, interaction, localized);
        }

      } else {
        Interaction delegator = DELEGATOR.cast();
        if (ecap.captured == null) {
          // no capture yet, dispatch to layer + parents and check for capturings
          DELEGATOR.prepare(interaction).mode = DelegatingInteraction.RECORD_CAPTURE;
          boolean captured = false;
          for (AbstractLayer ll = inLayer; ll != null; ll = (AbstractLayer)ll.parent()) {
            tryInteract(ll, listenerType, delegator, localized);
            captured = ecap.check(captured, ll);
          }

          if (captured) {
            if (cancel != null) {
              // someone captured on this dispatch, cancel everything else
              DELEGATOR.prepare(cancel).mode = DelegatingInteraction.EXCEPT_CAPTURED;
              for (AbstractLayer ll = inLayer; ll != null; ll = (AbstractLayer)ll.parent()) {
                tryInteract(ll, listenerType, delegator, localized);
              }
            } else {
              // TODO: warn that capture is occurring for an unsupported event?
            }
          }
        } else {
          // someone captured on a previous dispatch, divert all events to captured layer
          // TODO: should we update the hit layer?
          DELEGATOR.prepare(interaction).mode = DelegatingInteraction.ONLY_CAPTURED;
          tryInteract(ecap.captured, listenerType, delegator, localized);
        }
      }
    }
  };

  static class DelegatingInteraction
      implements Interaction {

    static final int ONLY_CAPTURED = 0, EXCEPT_CAPTURED = 1, RECORD_CAPTURE = 2;

    int mode;
    Interaction delegate;

    @SuppressWarnings("unchecked")
     DelegatingInteraction prepare(
        Interaction delegate) {
      this.delegate = (Interaction)delegate;
      return this;
    }

    @SuppressWarnings("unchecked")
     Interaction cast () {
      return (Interaction)this;
    }

    public void interact(Object listener, Input.Impl event) {
      CaptureState ecap = event.captureState;
      switch (mode) {
      case ONLY_CAPTURED:
        if (listener == ecap.capturedListener) {
          delegate.interact(listener, event);
        }
        break;
      case EXCEPT_CAPTURED:
        if (listener != ecap.capturedListener) {
          delegate.interact(listener, event);
        }
        break;
      case RECORD_CAPTURE:
        boolean prevCapture = ecap.didCapture;
        delegate.interact(listener, event);
        if (ecap.didCapture && !prevCapture) {
          ecap.capturedListener = listener;
        }
        break;
      }
    }
  }

  static final DelegatingInteraction DELEGATOR = new DelegatingInteraction();

  static Dispatcher select(boolean propagating) {
    return propagating ? PROPAGATING : SINGLE;
  }

  static  void tryInteract (AbstractLayer layer,
      Class listenerType, Interaction interaction, E event) {
    try {
      layer.interact(listenerType, interaction, event);
    } catch (Throwable t) {
      PlayN.reportError("Interaction failure [layer=" + layer + ", iact=" + interaction +
                        ", event=" + event + "]", t);
    }
  }

  @SuppressWarnings("unchecked")
  static  E localize (E event, AbstractLayer layer) {
    return (E)event.localize(layer);
  }

  /** Issues an interact call to {@code layer}'s listener(s) with a localized copy of the given
   * event. {@code onCancel} is used if this is a multi-layer dispatcher and one of the layer
   * listeners calls {@link Events.Input#capture()}. */
  abstract  void dispatch(
    AbstractLayer layer, Class listenerType, E event,
    Interaction interaction, Interaction onCancel);

  /** Issues an interact call to the captured layer's listener(s) with a localized copy of the
   * given event. {@code onCancel} is used if this is a multi-layer dispatcher and one of the layer
   * listeners calls {@link Events.Input#capture()}. */
   void dispatch(
      Class listenerType, E event, Interaction interaction, Interaction onCancel) {
    dispatch(event.captureState.layer, listenerType, event, interaction, onCancel);
  }

  /** Issues an interact call to a layer and listener with a localized copy of the given event. */
   void dispatch(
      AbstractLayer layer, Class listenerType, E event, Interaction interaction) {
    dispatch(layer, listenerType, event, interaction, null);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy