Many click events while key down on a Button

Hallo,

when you have a
Button
and you press a key without releasing it you get a lot of click events (
ClickListener
is activated). Is there any possibility to change this behaviour? I only want to have one click event, if the key is down for a second or for five minutes.

Thanks for any hints,

Philipp

I think this is the standard behaviour of key down event.
As a workaround you can try to use a client side extension that checks the repeated attribute of the keydown event;
beware that this may not work on all browsers (as stated in Event class javadocs)

public class UIExtension extends AbstractExtension {
    public UIExtension(UI target) { super(target); }
}

@Connect(UIExtension.class)
public class UIExtensionConnector extends AbstractExtensionConnector {
    @Override protected void extend(ServerConnector target) {
        UIConnector uiConnector = (UIConnector)target;
        uiConnector.getWidget().actionHandler = new MyShortcuActionHandler( target.getConnectorId(), getConnection());
  }
}

class MyShortcuActionHandler extends ShortcutActionHandler {
    public MyShortcuActionHandler(String pid, ApplicationConnection c) {
        super(pid, c);
    }
    @Override
    public void handleKeyboardEvent(Event event, ComponentConnector target) {
        if (!event.getRepeat()) {
            super.handleKeyboardEvent(event, target);
        }
    }
}

and then extend your UI

protected void init(VaadinRequest vaadinRequest) {
 ...
 new UIExtension(this);
 ...
}

HTH
Marco

Thanks,

it is a nice solution and so far I had no knowledge about
UIExtension
. But if it is not working on all browsers it is worthless :frowning:
I also created a workaround for that problem. It also has the feature that you can add a handler for button down and button up (standard click handler is still available). It also has the feature like many desktop buttons, if you press mouse down on the button, move the mouse somewhere else and release it afterwards, the button up handler is not informed.
For that I extended
Button
on server side and
ButtonConnector
on client side:

The
ButtonConnector
extension (most interesting part according the asked question):

[code]
@Connect( MyButton.class )
public class MyButtonConnectorConnector
extends ButtonConnector
implements MouseDownHandler, MouseUpHandler, KeyDownHandler, KeyUpHandler
{
private final MyButtonRpcRpc vRpc = RpcProxy.create( MyButtonRpcRpc.class, this );
private boolean vButtonDown;
private static final int C_KEYCODE_SPACE = 32;

public MyButtonConnectorConnector()
{
    super();

    vButtonDown = false;
}

@Override
public void init()
{
    super.init();

    getWidget().addMouseDownHandler( this );
    getWidget().addMouseUpHandler( this );

    getWidget().addKeyDownHandler( this );
    getWidget().addKeyUpHandler( this );
}

@Override
public void onMouseDown( final MouseDownEvent pEvent )
{
    final boolean buttonDownBefore = setButtonDown( true );
    if ( buttonDownBefore ) {
        return;
    }

    final MouseEventDetails details = MouseEventDetailsBuilder.buildMouseEventDetails(
            pEvent.getNativeEvent(), getWidget().getElement() );
       vRpc.down( details );
}

@Override
public void onMouseUp( final MouseUpEvent pEvent )
{
    final int yRelativeButton = pEvent.getRelativeY( this.getWidget().getElement() );
    final int xRelativeButton = pEvent.getRelativeX( this.getWidget().getElement() );

    final boolean buttonDownBefore = setButtonDown( false );

    final int width = getWidget().getOffsetWidth();
    final int height = getWidget().getOffsetHeight();

    // Befindet sich die Maus noch über dem Knopf?
    if ( yRelativeButton >= 0 && yRelativeButton <= height
            && xRelativeButton >= 0 && xRelativeButton <= width )
    {
        if ( !buttonDownBefore ) {
            return;
        }

        final MouseEventDetails details = MouseEventDetailsBuilder.buildMouseEventDetails(
                   pEvent.getNativeEvent(), getWidget().getElement() );
        vRpc.up( details );
    }
    else {
        setButtonDown( true );
    }
}

@Override
public void onKeyDown( final KeyDownEvent pEvent )
{
    if ( pEvent.getNativeKeyCode() == KeyCodes.KEY_ENTER
            || pEvent.getNativeKeyCode() == C_KEYCODE_SPACE )
    {
        final boolean buttonDownBefore = setButtonDown( true );
        if ( buttonDownBefore ) {
            return;
        }

        final MouseEventDetails details = MouseEventDetailsBuilder.buildMouseEventDetails(
                   pEvent.getNativeEvent(), getWidget().getElement() );
        vRpc.down( details );
    }
}

@Override
public void onKeyUp( final KeyUpEvent pEvent )
{
    if ( pEvent.getNativeKeyCode() == KeyCodes.KEY_ENTER
            || pEvent.getNativeKeyCode() == C_KEYCODE_SPACE )
    {
        final boolean buttonDownBefore = setButtonDown( false );
        if ( !buttonDownBefore ) {
            return;
        }

        final MouseEventDetails details = MouseEventDetailsBuilder.buildMouseEventDetails(
                pEvent.getNativeEvent(), getWidget().getElement() );
        vRpc.up( details );
    }
}

private synchronized boolean setButtonDown( final boolean pButtonDown )
{
    final boolean old = vButtonDown;
    vButtonDown = pButtonDown;

    return old;
}

}
[/code]
ServerRpc
:

[code]
public interface MyButtonRpcRpc
extends ServerRpc
{
public void down( MouseEventDetails pDetails );

public void up( MouseEventDetails pDetails );

}
[/code]
Button
extension:

public class MyButton
extends Button
{
    private final MyButtonRpcRpc vRpc = new MyButtonRpcRpc()
    {
        @Override
        public void down( final  MouseEventDetails pDetails )
        {
            fireButtonDown( pDetails );
        }

        @Override
        public void up( final MouseEventDetails pDetails )
        {
            fireButtonUp( pDetails );
        }
    };

    public MyButton()
    {
        super();

        registerRpc( vRpc );
    }

    public MyButton( final String pCaption )
    {
        super( pCaption );

        registerRpc( vRpc );
    }

    public MyButton(
            final String pCaption,
            final MyButtonUpListener pListener )
    {
        this( pCaption );

        addButtonUpListener( pListener );
    }

    /**
     * @deprecated Instead use {@link #adButtonDownListener(MyButtonDownListener)}
     *         or {@link #addButtonUpListener(MyButtonUpListener)}
     *
     * @see #adButtonDownListener(MyButtonDownListener)
     * @see #addButtonUpListener(MyButtonUpListener)
     */
    @Override
    @Deprecated
    public void addClickListener( final ClickListener pListener )
    {
        super.addClickListener( pListener );
    }

    public void adButtonDownListener( final MyButtonDownListener pListener )
    {
        addListener( MyButtonDownEvent.class, pListener, MyButtonDownListener.BUTTON_DOWN_METHOD );
    }

    public void removeButtonDownListener( final MyButtonDownListener pListener )
    {
        removeListener( MyButtonDownEvent.class, pListener, MyButtonDownListener.BUTTON_DOWN_METHOD );
    }

    public void removeButtonDownListeners()
    {
        final Collection<?> listeners = getListeners( MyButtonDownEvent.class );
        for ( final Object listener : listeners )
        {
            removeButtonDownListener( (MyButtonDownListener)listener );
        }
        listeners.clear();
    }

    public void addButtonUpListener( final MyButtonUpListener pListener )
    {
        addListener( MyButtonUpEvent.class, pListener, MyButtonUpListener.BUTTON_UP_METHOD );
    }

    public void removeButtonUpListener( final MyButtonUpListener pListener )
    {
        removeListener(
                MyButtonUpEvent.class, pListener, MyButtonUpListener.BUTTON_UP_METHOD );
    }

    public void removeButtonUpListeners()
    {
        final Collection<?> listeners = getListeners( MyButtonUpEvent.class );
        for ( final Object listener : listeners )
        {
            removeButtonUpListener( (MyButtonUpListener)listener );
        }

        listeners.clear();
    }

   protected void fireButtonDown( final MouseEventDetails pDetails )
    {
        fireEvent( new MyButtonDownEvent( this, pDetails ) );
    }

    protected void fireButtonUp( final MouseEventDetails pDetails )
    {
        fireEvent( new MyButtonUpEvent( this, pDetails ) );
    }

   public interface MyButtonDownListener
    extends Serializable
    {
        public static final Method BUTTON_DOWN_METHOD = ReflectTools.findMethod(
                MyButtonDownListener.class,
                "buttonDown",
                MyButtonDownEvent.class );

        public void buttonDown( final MyButtonDownEvent pEvent );
    }

    public interface MyButtonUpListener
    extends Serializable
    {
        public static final Method BUTTON_UP_METHOD = ReflectTools.findMethod(
                MyButtonUpListener.class,
                "buttonUp",
                MyButtonUpEvent.class );

        public void buttonUp( final MyButtonUpEvent pEvent );
    }

    public static class MyButtonDownEvent
    extends Component.Event
    {
        private final MouseEventDetails vDetails;

        public MyButtonDownEvent(
                final Component pSource )
        {
            super( pSource );
            vDetails = null;
        }

       public MyButtonDownEvent(
                final Component pSource,
                final MouseEventDetails pDetails )
        {
            super( pSource );
            vDetails = pDetails;
        }

        public MyButton getButton()
        {
            return (MyButton)getSource();
        }

        public int getClientX()
        {
            if ( null != vDetails ) {
                return vDetails.getClientX();
            }
            else {
                return -1;
            }
        }

        public int getClientY()
        {
            if ( null != vDetails ) {
                return vDetails.getClientY();
            }
            else {
                return -1;
            }
        }

        public int getRelativeX()
        {
            if ( null != vDetails ) {
                return vDetails.getRelativeX();
            }
            else {
                return -1;
            }
        }

        public int getRelativeY()
        {
            if ( null != vDetails ) {
                return vDetails.getRelativeY();
            }
            else {
                return -1;
            }
        }

        public boolean isAltKey()
        {
            if ( null != vDetails ) {
                return vDetails.isAltKey();
            }
            else {
                return false;
            }
        }

        public boolean isCtrlKey()
        {
            if ( null != vDetails )
            {
                return vDetails.isCtrlKey();
            }
            else {
                return false;
            }
        }

        public boolean isMetaKey()
        {
            if ( null != vDetails ) {
                return vDetails.isMetaKey();
            }
            else {
                return false;
            }
        }

        public boolean isShiftKey()
        {
            if ( null != vDetails ) {
                return vDetails.isShiftKey();
            }
            else {
                return false;
            }
        }
    }

    public static class MyButtonUpEvent
    extends Component.Event
    {
        private final MouseEventDetails vDetails;

        public MyButtonUpEvent(
                final Component pSource )
        {
            super( pSource );
            vDetails = null;
        }

       public MyButtonUpEvent(
                final Component pSource,
                final MouseEventDetails pDetails )
        {
            super( pSource );
            vDetails = pDetails;
        }

        public MyButton getButton()
        {
            return (MyButton)getSource();
        }

        public int getClientX()
        {
            if ( null != vDetails ) {
                return vDetails.getClientX();
            }
            else {
                return -1;
            }
        }

        public int getClientY()
        {
            if ( null != vDetails ) {
                return vDetails.getClientY();
            }
            else {
                return -1;
            }
        }

        public int getRelativeX()
        {
            if ( null != vDetails ) {
                return vDetails.getRelativeX();
            }
            else {
                return -1;
            }
        }

        public int getRelativeY()
        {
            if ( null != vDetails ) {
                return vDetails.getRelativeY();
            }
            else {
                return -1;
            }
        }

        public boolean isAltKey()
        {
            if ( null != vDetails ) {
                return vDetails.isAltKey();
            }
            else {
                return false;
            }
        }

        public boolean isCtrlKey()
        {
            if ( null != vDetails )
            {
                return vDetails.isCtrlKey();
            }
            else {
                return false;
            }
        }

        public boolean isMetaKey()
        {
            if ( null != vDetails ) {
                return vDetails.isMetaKey();
            }
            else {
                return false;
            }
        }

        public boolean isShiftKey()
        {
            if ( null != vDetails ) {
                return vDetails.isShiftKey();
            }
            else {
                return false;
            }
        }
    }
}

Very interesting solution; could be an idea for an addon

Best regards
Marco

Thanks.

If you have a nice name for the addon/button and you think it is also needed by others I can try to build an “official” addon. I also would add some comments. Something more?