Double Clicking Menu2InputDelegate Items

If you accidentally double click a Menu2InputDelegate item, then the handler will be called twice, so you'll potentially end up pushing two of the exact same view. The result is that the user will click back once, be taken to the same view, and then have to click back to return to the previous menu. From an end-user's perspective this is broken.

The question is: should CIQ handle the 'lock out' period necessary to ignore double-click events, or is it the app's responsibility?

IMHO this should be the responsibility of CIQ itself. We probably don't put the responsibility on every developer to write their own custom 'lock out' code to handle what seems like sensible default behavior. Besides if a double-click becomes a supported behavioral event, that it could just dispatch to a separate `onDoubleClick` receiver.
  • I think it kind of depends what you're doing. For example, in some of my MenuItems for menu2, I just alternate 2 items (ToggleMenuItem). On one, I do 5 (a MenuItem - select a number, 0 to 4). Those are cases where I wouldn't want CIQ to do a lockout.

    Think of it as some in-app settings. Change them as desired, then hit back to leave the view.

    In fact, this is something I really like about Menu2. Go to a menu once, change all the settings, and be done, instead of pulling up the menu for each item to be changed.

    Maybe try popping the menu2 then push the view you want and that way the Menu2 wouldn't see both clicks.
  • Former Member
    Former Member
    rconradharris,

    What device are you seeing this on? Do you have a specific app or code snippet? I'm not reproducing this yet, but perhaps it's device specific. I think what Jim says about a lockout is correct in this case, but I don't think it should be pushing a view twice either.

    Thanks,
    - Coleman
  • rconradharris,

    What device are you seeing this on? Do you have a specific app or code snippet? I'm not reproducing this yet, but perhaps it's device specific. I think what Jim says about a lockout is correct in this case, but I don't think it should be pushing a view twice either.

    Thanks,
    - Coleman


    I was seeing this on a Vivoactive 3m.

    I believe this issue is in theory observable on any device running any app that pushes a view from Menu2InputDelegate; however in 99.9% of the cases in won't be observable in practice.

    You need a couple of things to come together to 'open up the race'. First, you need a touchscreen since it can generate touch events much faster than the physical buttons of say a 645m (their movement provides a natural hysteresis).

    The other main thing you need is to push a `Ui.View` class that takes a long time to construct, say >= 250ms.

    Because the Ui.View takes so long to construct, there is a relatively long period of time where the Menu2InputDelegate is still foregrounded and accepting events, so instead of getting just one `onSelect` invocation, you'll potentially get as many as the user can generate in that ~250ms window we're using in this example. Once the view is constructed and pushed onto the view stack, the race window closes and the system behaves normally. That is, until the user hits back and has to page through a bunch of the same windows to get back to the 'real' parent.


    The workaround I implemented basically creates a lockout window where the Menu2InputDelegate will disregard additional touch events.

    ???????// Accidental double-taps can cause multiple copies of the same view to be

    // pushed on to the stack. This issue is likely to occur on a touchscreen device

    // when a view takes a long time to construct (since that opens up the race

    // window).

    //

    // The solution is to provide a brief lockout period to prevent double/triple

    // clicks from having an effect.

    //

    // Since touchscreen events are only dequeued *after* the previous handler was

    // run we must use the *finished* time of the previous call to start our lockout

    // period.

    class DebouncedDelegate extends Ui.Menu2InputDelegate {

    public const LOCKOUT_MS = 250;




    hidden var mStartedAt, mFinishedAt;




    function initialize() {

    Menu2InputDelegate.initialize();

    }




    function onSelectDebounced(item) {

    // Subclasses override this...

    }




    function onSelect(item) {

    var start = System.getTimer();




    if ((self.mStartedAt != null) && ((start - self.mStartedAt) <= self.LOCKOUT_MS)) {

    return;

    }




    self.mStartedAt = start;




    if ((self.mFinishedAt != null) && ((start - self.mFinishedAt) <= self.LOCKOUT_MS)) {

    return;

    }




    self.onSelectDebounced(item);




    self.mFinishedAt = System.getTimer();

    }

    }