Sunday, 19 May 2013

LongTouch event on single-touch LCD

I have a T35 LCD touchscreen as part of my Gadgeteer kit. I was disappointed to find that although the drivers for the screen suggested gestures, in fact the display is a single-touch and so registers only TouchDown and TouchUp events.

I needed a second gesture for an app I was writing so created a subclass of the Display_T35 class. Here's how it works.

The Class

As it subclasses the Display_T35 class, it implements the public constructors of that class. There's one additional parameter on each constructor; the interval in milliseconds after which the LongTouch event will fire - this defaults to a second. The constructors also register handlers for the TouchDown and TouchUp events of the base class.

    public class GestureEnabledDisplay_T35 : Gadgeteer.Modules.GHIElectronics.Display_T35
    {
        public GestureEnabledDisplay_T35(int socket1, int socket2, int socket3, long longTouchRegisterInterval = 1000)
            : base(socket1, socket2, socket3)
        {
            Initialise(longTouchRegisterInterval);
        }

        public GestureEnabledDisplay_T35(int socket1, int socket2, int socket3, int socket4, long longTouchRegisterInterval = 1000)
            : base(socket1, socket2, socket3, socket4)
        {
            Initialise(longTouchRegisterInterval);
        }

        private void Initialise(long longTouchRegisterInterval)
        {
            base.WPFWindow.TouchDown += new Microsoft.SPOT.Input.TouchEventHandler(WPFWindow_TouchDown);
            base.WPFWindow.TouchUp += new Microsoft.SPOT.Input.TouchEventHandler(WPFWindow_TouchUp);
            LongTouchRegisterInterval = longTouchRegisterInterval;
        }
The subclass needs to expose the new LongTouch event. This is the .Net Micro Framework, so generic event handlers are not available - remember .Net 1.1 anyone?

        public delegate void LongTouchEventHandler(object sender, EventArgs args);
        public event LongTouchEventHandler LongTouch;
        
  private void OnLongTouch()
        {
            if (LongTouch != null)
            {
                LongTouch(this, EventArgs.Empty);
            }
        }
The functionality is fairly simple. When the TouchDown event is raised, the current clock tick is recorded. When the TouchUp event is raised, the number of ticks since TouchDown is calculated and compared with the LongTouchRegisterInterval. If the interval has passed, the LongTouch event is raised.

        public long LongTouchRegisterInterval { get; private set; }

  private long _touchdownTicks;
        private const long NanosecondsInMilliseconds = 10000;

        void WPFWindow_TouchUp(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
        {
            var touchupTicks = DateTime.Now.Ticks;
            if (touchupTicks - _touchdownTicks > (NanosecondsInMilliseconds * LongTouchRegisterInterval))
            {
                OnLongTouch();
            }
        }

        void WPFWindow_TouchDown(object sender, Microsoft.SPOT.Input.TouchEventArgs e)
        {
            _touchdownTicks = DateTime.Now.Ticks;
        }

The Usage

If you use the standard Gadgeteer designer to connect your module...
...you'll have an instance of the Display_T35 defined for you in the generated.cs file:

        private Gadgeteer.Modules.GHIElectronics.Display_T35 display;

        private void InitializeModules()
        {
            this.display = new GTM.GHIElectronics.Display_T35(14, 13, 12, 10);
        }
This will need to be edited to use the new GestureEnabledDisplay_T35 class

        private GestureEnabledDisplay_T35 display;

        private void InitializeModules()
        {
            this.display = new GestureEnabledDisplay_T35(14, 13, 12, 10);
        }
However, this means that each time you edit the diagram in the designer, your changes will be overwritten. And the generated class is called generated for a reason - it's not yours so keep your mitts off. A better way to create the display class is to leave the module unconnected in the designer, and create the class explicitly in your code.

    public partial class Program
    {
        GestureEnabledDisplay_T35 gestureEnabledDisplay;

        void ProgramStarted()
        {
            gestureEnabledDisplay = new GestureEnabledDisplay_T35(14, 13, 12, 10);
            gestureEnabledDisplay.LongTouch += new GestureEnabledDisplay_T35.LongTouchEventHandler(display_LongTouch);
        }
 }

Known Issues

There are a couple of annoyances with the implementation, but not enough to make me swear lots:

  • The TouchDown and TouchUp events are defined in the Display_T35.WPFWindow class, but the LongTouch event is defined in the Display_T35 class.
  • If the WPFWindow.TouchUp event is handled as well as the LongTouch event, both events will still fire - the LongTouch event does not swallow the TouchUp event.

Where's the code?

The code's available as a GitHub Gist, here.

No comments:

Post a Comment