Wednesday, December 5, 2012
Ignore single-tap before double-tap on Windows 8 apps
The new development platform for Windows 8 has been released too early. The number of dirty tricks I had to resort to in just a few months working with it made it pretty clear to me.
One of the most infamous bugs I found affects all the versions of the platform (x86, x64 and ARM) and it's an erroneous sequence of events that is propagated after a double-tap. I found it propagating from a ScrollViewer control, but I'm pretty sure other controls may behave this way as well.
When a user double-tapped on the screen I was expecting a single call to my double-tap event handler to be made, but with a bit of sadness a single-tap event was called immediately before it... with no fucking reason, if you ask me.
My code needs to be certain about which one of the two is called, because I need to react in two different, alternative ways to them. So I was stuck, trying to find a clever way to figure out when a single tap erroneously fired by the framework. And obviously when it's not.
With the help of my collegues we found a really simplicistic way of dealing with it, but it's actually a trick that mitigates the issue, not a definitive cure.
Here it is:
// Here's the part I hate the most: heuristics...
private const int DOUBLETAP_DELAY_MILLIS = 190;
private DateTime singleTapTime;
private bool singleTapCancelled;
private async void scrollViewer_Tapped(object sender,
TappedRoutedEventArgs args)
{
// Save the moment the single tap
// has been detected...
this.singleTapTime = DateTime.Now;
// ... but wait some time to be sure
// that no double-tap event follows
await Task.Delay(Viewer.DOUBLETAP_DELAY_MILLIS + 10);
// A double-tap event will cancel our single- one
// before we reach this point...
if (!this.singleTapCancelled)
{
// ...
}
this.singleTapCancelled = false;
}
private void scrollViewer_DoubleTapped(object sender,
DoubleTappedRoutedEventArgs args)
{
TimeSpan tapsDelay =
DateTime.Now - this.singleTapTime;
// If we are between the configured timespan,
// we cancel the previously fired single-tap event
this.singleTapCancelled = (tapsDelay <= TimeSpan.FromMilliseconds(Viewer.DOUBLETAP_DELAY_MILLIS));
// ...
}
An improvement would be protecting the boolean variable with a lock.
Also would be better to be tolerant on the number of milliseconds we wait. E.g. we could wait Viewer.DOUBLETAP_DELAY_MILLIS +/- some time.
But the only improvement should be done by Microsoft I guess...
Subscribe to:
Posts (Atom)