org.waveprotocol.wave.client.autohide.EventPreviewAutoHiderRegistrar.java Source code

Java tutorial

Introduction

Here is the source code for org.waveprotocol.wave.client.autohide.EventPreviewAutoHiderRegistrar.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.waveprotocol.wave.client.autohide;

import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import org.waveprotocol.wave.client.common.util.SignalEvent;
import org.waveprotocol.wave.client.common.util.SignalEventImpl;
import org.waveprotocol.wave.client.common.util.SignalEvent.KeySignalType;

import java.util.ArrayList;
import java.util.List;

/**
 * Detects auto-hide events by installing an event preview.
 *
 * This registrar handles multiple simultaneous AutoHiders by treating the ones
 * opened later as 'higher up' than the ones opened earlier, and so events that
 * cause auto-hides will be routed to the higher AutoHiders first. This
 * behaviour can be changed in the future by introducing a more sophisticated
 * structure to store the AutoHiders in and, for instance, having each AutoHider
 * declaring a parent AutoHider.
 *
 */
public class EventPreviewAutoHiderRegistrar
        implements AutoHiderRegistrar, NativePreviewHandler, ResizeHandler, ValueChangeHandler<String> {
    /**
     * List of AutoHiders that currently need to be considered when interpreting
     * incoming events.
     */
    private final List<AutoHider> autoHiders = new ArrayList<AutoHider>();

    /**
     * Used to deregister this object from the event preview when there are no
     * registered AutoHiders.
     */
    private HandlerRegistration eventPreviewRegistration;

    private HandlerRegistration onResizeRegistration;

    private HandlerRegistration onHistoryRegistration;

    @Override
    public void registerAutoHider(final AutoHider autoHider) {
        autoHider.setRegistered(true);
        autoHiders.add(autoHider);

        if (eventPreviewRegistration == null) {
            eventPreviewRegistration = Event.addNativePreviewHandler(this);
        }

        if (onResizeRegistration == null) {
            onResizeRegistration = Window.addResizeHandler(this);
        }

        if (onHistoryRegistration == null) {
            onHistoryRegistration = History.addValueChangeHandler(this);
        }
    }

    @Override
    public void deregisterAutoHider(AutoHider autoHider) {
        autoHiders.remove(autoHider);
        autoHider.setRegistered(false);

        if (autoHiders.isEmpty()) {
            if (eventPreviewRegistration != null) {
                eventPreviewRegistration.removeHandler();
                eventPreviewRegistration = null;
            }
            if (onResizeRegistration != null) {
                onResizeRegistration.removeHandler();
                onResizeRegistration = null;
            }
            if (onHistoryRegistration != null) {
                onHistoryRegistration.removeHandler();
                onHistoryRegistration = null;
            }
        }
    }

    @Override
    public void onPreviewNativeEvent(NativePreviewEvent previewEvent) {
        if (autoHiders.isEmpty()) {
            return;
        }

        // TODO(danilatos,user,user): Push signal down a layer - clean this up.
        Event event = Event.as(previewEvent.getNativeEvent());
        int lowLevelType = event.getTypeInt();

        // TODO(danilatos): Insert this logic deeply rather than
        // sprinkling it in event handlers. Also the return value
        // of onEventPreview is the reverse of signal handlers.
        SignalEvent signal = SignalEventImpl.create(event, false);
        if (signal == null) {
            return;
        }

        // Key events (excluding escape) and mousewheel events use hideTopmostAutoHiderForKeyEvent
        if (lowLevelType == Event.ONMOUSEWHEEL || signal.isKeyEvent()) {
            if (hideTopmostAutoHiderForKeyEvent(false)) {
                // TODO(user): We don't call previewEvent.cancel() here, since for the floating-buttons
                // menu we want, for example, space-bar to still shift focus to the next blip.
                // The to-do is to audit the previewEvent.cancel call below and see why it's there (and if
                // it's not needed, eliminate it).
                return;
            }
        }

        // Pressing escape at any time causes us to close and discard the event.
        if (signal.getKeySignalType() == KeySignalType.NOEFFECT && event.getKeyCode() == KeyCodes.KEY_ESCAPE) {
            if (hideTopmostAutoHiderForKeyEvent(true)) {
                previewEvent.cancel();
                return;
            }
        }

        // Click events and mouse-wheel events that fall through use hideAllAfter.
        if (lowLevelType == Event.ONMOUSEDOWN || lowLevelType == Event.ONMOUSEWHEEL) {
            hideAllAfter(signal.getTarget());
        }

        // Otherwise we don't do anything and the event continues as usual.
    }

    /**
     * Causes all AutoHiders after the one that contains the given element to hide.
     *
     * @param target An element.
     */
    private void hideAllAfter(Element target) {
        List<AutoHider> toHide = new ArrayList<AutoHider>();
        for (int i = autoHiders.size() - 1; i >= 0; i--) {
            AutoHider autoHider = autoHiders.get(i);
            if (autoHider.doesContain(target)) {
                break;
            }
            toHide.add(autoHider);
        }

        for (AutoHider autoHider : toHide) {
            autoHider.hide();
        }
    }

    /**
     * Hides the topmost AutoHider that is supposed to hide on key events.
     */
    private boolean hideTopmostAutoHiderForKeyEvent(boolean keyIsEscape) {
        for (int i = autoHiders.size() - 1; i >= 0; i--) {
            AutoHider autoHider = autoHiders.get(i);
            if (autoHider.shouldHideOnAnyKey() || (keyIsEscape && autoHider.shouldHideOnEscape())) {
                autoHider.hide();
                return true;
            }
        }
        return false;
    }

    @Override
    public void onResize(ResizeEvent event) {
        List<AutoHider> toHide = new ArrayList<AutoHider>();
        for (AutoHider autoHider : autoHiders) {
            if (autoHider.shouldHideOnWindowResize()) {
                toHide.add(autoHider);
            }
        }

        for (AutoHider autoHider : toHide) {
            autoHider.hide();
        }
    }

    @Override
    public void onValueChange(ValueChangeEvent<String> event) {
        List<AutoHider> toHide = new ArrayList<AutoHider>();
        for (AutoHider autoHider : autoHiders) {
            if (autoHider.shouldHideOnHistoryEvent()) {
                toHide.add(autoHider);
            }
        }

        for (AutoHider autoHider : toHide) {
            autoHider.hide();
        }
    }
}