com.metsci.glimpse.layout.GlimpseTouchWrapper Maven / Gradle / Ivy
/*
* Copyright (c) 2012, Metron, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Metron, Inc. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.metsci.glimpse.layout;
import static com.metsci.glimpse.context.TargetStackUtil.popTo;
import static com.metsci.glimpse.context.TargetStackUtil.pushToBottom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
import com.metsci.glimpse.axis.listener.touch.AxisGestureListener;
import com.metsci.glimpse.axis.listener.touch.AxisGestureListener1D;
import com.metsci.glimpse.axis.listener.touch.AxisGestureListener2D;
import com.metsci.glimpse.context.GlimpseBounds;
import com.metsci.glimpse.context.GlimpseTarget;
import com.metsci.glimpse.context.GlimpseTargetStack;
import com.metsci.glimpse.event.touch.GlimpseGestureEvent;
import com.metsci.glimpse.event.touch.GlimpseGestureListener;
import com.metsci.glimpse.event.touch.GlimpseLongPressGestureEvent;
import com.metsci.glimpse.event.touch.GlimpsePanGestureEvent;
import com.metsci.glimpse.event.touch.GlimpsePinchGestureEvent;
import com.metsci.glimpse.event.touch.GlimpseSwipeGestureEvent;
import com.metsci.glimpse.event.touch.GlimpseTapGestureEvent;
import com.metsci.glimpse.event.touch.GlimpseTouchEvent;
import com.metsci.glimpse.event.touch.GlimpseTouchListener;
import com.metsci.glimpse.event.touch.Touchable;
import com.metsci.glimpse.painter.base.GlimpsePainterCallback;
/**
* Provides a layout wrapper to augment any layout (and descendants) with
* gesture support. This wrapper sets EventConsumer and EventGenerator to
* false for any child layouts. Then, all the touch events get sent to this
* layout. Then this layout will find the actual target and get the gesture
* listeners associated with that layout and execute them.
*
*
* We may want to preemptively add touch support to all layouts, like we do
* mouse support. In that case, this class should be removed.
*
*
* @author borkholder
*/
public class GlimpseTouchWrapper extends GlimpseLayout implements Touchable
{
private static final Logger LOGGER = Logger.getLogger( GlimpseTouchWrapper.class.getName( ) );
protected Map> gestureListeners;
public GlimpseTouchWrapper( GlimpseLayout parent )
{
super( parent );
gestureListeners = new HashMap>( );
}
protected void addGestureListenersRecursive( GlimpseLayout layout )
{
layout.setEventGenerator( false );
layout.setEventConsumer( false );
addGestureListenerVarying( layout );
for ( GlimpseTarget child : layout.getTargetChildren( ) )
{
if ( child instanceof GlimpseLayout )
{
addGestureListenersRecursive( ( GlimpseLayout ) child );
}
}
}
protected void removeGestureListenersRecursive( GlimpseLayout layout )
{
gestureListeners.remove( layout );
for ( GlimpseTarget child : layout.getTargetChildren( ) )
{
if ( child instanceof GlimpseLayout )
{
removeGestureListenersRecursive( ( GlimpseLayout ) child );
}
}
}
protected void addGestureListenerVarying( GlimpseLayout layout )
{
if ( layout instanceof GlimpseAxisLayoutX )
{
addGlimpseGestureListener( createAxisGestureListener1D( true ), layout );
}
else if ( layout instanceof GlimpseAxisLayoutY )
{
addGlimpseGestureListener( createAxisGestureListener1D( false ), layout );
}
else if ( layout instanceof GlimpseAxisLayout2D )
{
addGlimpseGestureListener( createAxisGestureListener2D( ), layout );
}
}
protected AxisGestureListener createAxisGestureListener1D( boolean horizontal )
{
return new AxisGestureListener1D( );
}
protected AxisGestureListener createAxisGestureListener2D( )
{
return new AxisGestureListener2D( );
}
public void addGlimpseGestureListener( GlimpseGestureListener listener, GlimpseLayout child )
{
List list = gestureListeners.get( child );
if ( list == null )
{
list = new CopyOnWriteArrayList( );
gestureListeners.put( child, list );
}
list.add( listener );
}
@Override
public String toString( )
{
return getClass( ).getSimpleName( );
}
@Override
public void addGlimpseGestureListener( GlimpseGestureListener listener )
{
LOGGER.fine( "Do not add gesture listeners directly, use addGlimpseGestureListener(GlimpseGestureListener, GlimpseLayout)" );
}
@Override
public void addGlimpseTouchListener( GlimpseTouchListener listener )
{
LOGGER.finer( "This class does not support touch listeners" );
}
protected GlimpseTargetStack getActualTargetStack( GlimpseGestureEvent event )
{
GlimpseTargetStack stack = event.getTargetStack( );
int x = event.getX( ) + stack.getBounds( ).getX( );
int y = event.getY( ) + stack.getBounds( ).getY( );
stack = popTo( stack, this );
if ( stack == null )
{
return null;
}
stack = pushToBottom( stack, x, y );
return stack;
}
@SuppressWarnings( "unchecked" )
protected E convertEvent( E event, GlimpseTargetStack stack )
{
int x = event.getX( );
int y = event.getY( );
GlimpseBounds bounds = event.getTargetStack( ).getBounds( );
x += bounds.getX( );
y += bounds.getY( );
bounds = stack.getBounds( );
x -= bounds.getX( );
y -= bounds.getY( );
return ( E ) event.withNewTarget( stack, x, y );
}
@Override
public void panDetected( GlimpsePanGestureEvent event )
{
GlimpseTargetStack stack = getActualTargetStack( event );
if ( stack == null )
{
return;
}
GlimpsePanGestureEvent e = convertEvent( event, stack );
List listeners = gestureListeners.get( stack.getTarget( ) );
if ( listeners == null )
{
return;
}
for ( GlimpseGestureListener listener : listeners )
{
listener.panDetected( e );
}
}
@Override
public void pinchDetected( GlimpsePinchGestureEvent event )
{
GlimpseTargetStack stack = getActualTargetStack( event );
if ( stack == null )
{
return;
}
GlimpsePinchGestureEvent e = convertEvent( event, stack );
List listeners = gestureListeners.get( stack.getTarget( ) );
if ( listeners == null )
{
return;
}
for ( GlimpseGestureListener listener : listeners )
{
listener.pinchDetected( e );
}
}
@Override
public void tapDetected( GlimpseTapGestureEvent event )
{
GlimpseTargetStack stack = getActualTargetStack( event );
if ( stack == null )
{
return;
}
GlimpseTapGestureEvent e = convertEvent( event, stack );
List listeners = gestureListeners.get( stack.getTarget( ) );
if ( listeners == null )
{
return;
}
for ( GlimpseGestureListener listener : listeners )
{
listener.tapDetected( e );
}
}
@Override
public void longPressDetected( GlimpseLongPressGestureEvent event )
{
GlimpseTargetStack stack = getActualTargetStack( event );
if ( stack == null )
{
return;
}
GlimpseLongPressGestureEvent e = convertEvent( event, stack );
List listeners = gestureListeners.get( stack.getTarget( ) );
if ( listeners == null )
{
return;
}
for ( GlimpseGestureListener listener : listeners )
{
listener.longPressDetected( e );
}
}
@Override
public void swipeDetected( GlimpseSwipeGestureEvent event )
{
GlimpseTargetStack stack = getActualTargetStack( event );
if ( stack == null )
{
return;
}
GlimpseSwipeGestureEvent e = convertEvent( event, stack );
List listeners = gestureListeners.get( stack.getTarget( ) );
if ( listeners == null )
{
return;
}
for ( GlimpseGestureListener listener : listeners )
{
listener.swipeDetected( e );
}
}
@Override
public void touchesBegan( GlimpseTouchEvent event )
{
// nop
}
@Override
public void touchesMoved( GlimpseTouchEvent event )
{
// nop
}
@Override
public void touchesEnded( GlimpseTouchEvent event )
{
// nop
}
@Override
public void removeGlimpseGestureListener( GlimpseGestureListener listener )
{
for ( List listeners : gestureListeners.values( ) )
{
listeners.remove( listener );
}
}
public void removeGlimpseGestureListeners( GlimpseLayout layout )
{
gestureListeners.remove( layout );
}
@Override
public void removeGlimpseTouchListener( GlimpseTouchListener listener )
{
// nop
}
@Override
public Collection getGlimpseGestureListeners( )
{
List all = new ArrayList( );
for ( List listeners : gestureListeners.values( ) )
{
all.addAll( listeners );
}
return Collections.unmodifiableList( all );
}
@Override
public Collection getGlimpseTouchListeners( )
{
return Collections.emptyList( );
}
@Override
public void removeAllGlimpseGestureListeners( )
{
for ( List listeners : gestureListeners.values( ) )
{
listeners.clear( );
}
}
@Override
public void addLayout( GlimpseLayout layout, GlimpsePainterCallback callback, int zOrder )
{
super.addLayout( layout, callback, zOrder );
addGestureListenersRecursive( layout );
}
@Override
public void removeLayout( GlimpseLayout layout )
{
super.removeLayout( layout );
removeGestureListenersRecursive( layout );
}
@Override
public void removeAll( )
{
super.removeAll( );
gestureListeners.clear( );
}
public static GlimpseTouchWrapper wrap( GlimpseLayout parent, GlimpseLayout childAsTouchable )
{
GlimpseTouchWrapper wrapper = new GlimpseTouchWrapper( parent );
wrapper.addLayout( childAsTouchable );
return wrapper;
}
}