Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
Enrique Albertos
Published © GPL3+

Arduino Date and Time Picker

This datetime picker allows you to enter a specific date and time values. From setting an alarm to scheduling or to record your time series.

BeginnerFull instructions provided1 hour6,414

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
AZ-Delivery 2.4 TFT LCD Touch Display Arduino
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Pinout

Code

Date and Time Picker

C/C++
/*
  TFT Date and Time Picker

  Date and Time picker for touch TFT LCD displays that let users
  select a date and a specific time value to adjust TimeLib clock

  Parts needed:
      Ardunio UNO
      AZ-Delivery 2.4 TFT LCD Touch Display Arduino Shield

  This example code is in the public domain.

  Modified 12 10 2020
  By Enrique Albertos

  https://www.hackster.io/javagoza/arduino-date-and-time-picker-daa2fe

*/


#include "MCUFRIEND_kbv.h"
#include <TouchScreen.h>
#include <TimeLib.h>
MCUFRIEND_kbv tft;

#define LOWFLASH (defined(__AVR_ATmega328P__) && defined(MCUFRIEND_KBV_H_))

#define countof(a) (sizeof(a) / sizeof(a[0]))

#define PRIMARY_COLOR 0x4A11
#define PRIMARY_LIGHT_COLOR 0x7A17
#define PRIMARY_DARK_COLOR 0x4016
#define PRIMARY_TEXT_COLOR 0x7FFF
// WIDGETS ID
#define HOUR_ID 0
#define TIME_SEPARATOR1_ID 1
#define MINUTE_ID 2
#define TIME_SEPARATOR2_ID 3
#define SECOND_ID 4
#define YEAR_ID 5
#define DATE_SEPARATOR1_ID 6
#define MONTH_ID 7
#define DATE_SEPARATOR2_ID 8
#define DAY_ID 9
#define TIME_SET_ID 10
#define TIME_CANCEL_ID 11
#define TIME_BUTTON_ID 12
#define DATE_BUTTON_ID 13
#define WIDGETS_NO 14


// Aligment modes for buttons and labels
#define ALIGN_LEFT 0
#define ALIGN_CENTER 1
#define ALIGN_RIGHT 2
// base size for up and down spinner triangles
#define UP_DOWN_WIDTH 24
// Touch screen presure threshold
#define MINPRESSURE 200
#define MAXPRESSURE 1000

// UI Items IDs linked to date spinner controls
const uint16_t dateSpinnerWidgets[] = {YEAR_ID, DATE_SEPARATOR1_ID, MONTH_ID, DATE_SEPARATOR2_ID, DAY_ID};
// UI Items IDs linked to time spinner controls
const uint16_t timeSpinnerWidgets[] = {HOUR_ID, TIME_SEPARATOR1_ID, MINUTE_ID, TIME_SEPARATOR2_ID, SECOND_ID};

// Touch screen calibration
const int16_t XP = 8, XM = A2, YP = A3, YM = 9; //240x320 ID=0x9341
const int16_t TS_LEFT = 122, TS_RT = 929, TS_TOP = 77, TS_BOT = 884;

char format2d[] = "%02d";
char format4d[] = "%04d";
char colon[] = ":";
char dash[] = "-";
char SET_LABEL[] = "SET";
char CANCEL_LABEL[] = "CANCEL";
char DEFAULT_DATE[] = "2020-09-01";
char DEFAULT_TIME[] = "12:00:00";

const TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

typedef struct RectRegion {
  int16_t x1, x2, y1, y2;
} RectRegion;

typedef struct DialogSize_type {
  int16_t x, y, width, height;
} DialogSize_type;

// Defintion of an UI Element
struct UiElement_type;
// Action callback to respond to events
typedef void (*Action)(struct UiElement_type* caller, int x, int y) ;
// Paint callback to respond to paint messages
typedef void (*Paint)(struct UiElement_type* self);

typedef struct UiElement_type {
  int16_t x; // horizontal position of the label
  int16_t y; // vertical position of the label
  int16_t width; // widht of the label
  int16_t height; // height of the label
  uint16_t fontsize; // size of the font label
  bool visible; // is visble the element
  int16_t value; //  int value of the element
  int16_t minValue; //  minimum admisible value
  int16_t maxValue; //  maximum admisible value
  bool enabled; // is enabled
  bool pressed; // is pressed
  char* text; // label text
  char* format; // format for the value
  Action onTap; // callback when tapped
  Paint paint; // paint callback
  RectRegion hitbox; // hit box region to check tap is inside the element
  uint16_t foreColor; // forground color for the element
  uint16_t backgroundColor; // background color for the element
} UiElement_type;

// array of ui elements
UiElement_type uiWidgets[WIDGETS_NO];
DialogSize_type dialogSize;


//////////////////////////////////////////////////////////////
// ARDUINO SETUP
//////////////////////////////////////////////////////////////
void setup()
{
  initTft(tft);
  dialogSize = {0, 0, tft.width(), tft.height()};
  clearDialog(dialogSize);
  createDateTimePickerDialog(dialogSize);
  paintDateTimePickerDialog();
}

//////////////////////////////////////////////////////////////
// ARDUINO LOOP
//////////////////////////////////////////////////////////////
int secondsOld = -1;
int selection = -1;
void loop(void)
{
  selection = readUiSelection(selection);
  const int secondsNew = second();
  if ( !(secondsNew == secondsOld)) {
    refreshTime();
  }
  secondsOld = secondsNew;
}

//////////////////////////////////////////////////////////////
// TFT SETUP
//////////////////////////////////////////////////////////////

void initTft(MCUFRIEND_kbv &tft) {
  tft.reset();
  uint16_t ID = tft.readID();
  tft.begin(ID);
  tft.setRotation(2);
}

//////////////////////////////////////////////////////////////
// Screen Painting methods
//////////////////////////////////////////////////////////////

/**
  Print a text in forecolor over a filled box with background color.
  Rectangle size is calculated to include the whole text without margins

  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param fontsize font size of the text to print
  @param foreColor forecolor of the text to print
  @param backgroundColor color of the filled rect
  @return void
*/
void drawBoxedString(const uint16_t x, const uint16_t y, const char* string, const uint16_t fontsize, const uint16_t foreColor, const uint16_t backgroundColor) {
  tft.setTextSize(fontsize);
  int16_t  x1, y1;
  uint16_t w, h;
  tft.getTextBounds(string, x, y, &x1, &y1, &w, &h);
  tft.fillRect(x, y, w, h, backgroundColor);
  tft.setCursor(x, y);
  tft.setTextColor(foreColor);
  tft.print(string);
}

/**
  Paint an ui label element

  @param label pointer to an ui label type element to be drawn in the screen
  @return void
*/
void paintLabel(UiElement_type *label) {
  if (label->visible) {
    if (label->pressed) {
      drawBoxedString(label->x, label->y, label->text, label->fontsize, label->backgroundColor, label->foreColor);
    } else {
      drawBoxedString(label->x, label->y, label->text, label->fontsize, label->foreColor, label->backgroundColor);
    }
  }
}

/**
  Paint an ui spinner element

  @param label pointer to an ui spinner type element to be drawn in the screen
  @return void
*/
void paintSpinner(UiElement_type * spinner) {

  tft.setTextSize(spinner->fontsize);
  int16_t  x1, y1;
  uint16_t wChar, hChar;

  char buffer[5];
  snprintf(buffer, sizeof(buffer), spinner->format, spinner->value);
  spinner->text = buffer;

  tft.getTextBounds(buffer, spinner->x, spinner->y, &x1, &y1, &wChar, &hChar);

  paintLabel(spinner);

  int xUpDown = spinner->x + (wChar - UP_DOWN_WIDTH) / 2;

  tft.fillTriangle( xUpDown + 2,  spinner->y - 4,
                    xUpDown + UP_DOWN_WIDTH / 2, spinner->y - UP_DOWN_WIDTH / 2 ,
                    xUpDown + UP_DOWN_WIDTH - 3, spinner->y - 4 ,
                    spinner->foreColor);
  tft.fillTriangle( xUpDown + 2 ,  spinner->y + UP_DOWN_WIDTH  + 3,
                    xUpDown + UP_DOWN_WIDTH / 2, spinner->y + UP_DOWN_WIDTH + UP_DOWN_WIDTH / 2 ,
                    xUpDown + UP_DOWN_WIDTH - 2 , spinner->y + UP_DOWN_WIDTH + 3,
                    spinner->foreColor);
}

/**
  Paint all the visible ui elements on the screen

  @param label pointer to an ui spinner type element to be drawn in the screen
  @return void
*/
void paintDateTimePickerDialog(void) {
  for (int i = 0; i < WIDGETS_NO ; i++) {
    if (uiWidgets[i].visible == true) {
      uiWidgets[i].paint(&uiWidgets[i]);

    } else {
      tft.fillRect(uiWidgets[i].hitbox.x1,
                   uiWidgets[i].hitbox.y1,
                   uiWidgets[i].hitbox.x2 - uiWidgets[i].hitbox.x1,
                   uiWidgets[i].hitbox.y2 - uiWidgets[i].hitbox.y1, uiWidgets[i].backgroundColor);
    }
  }
}

/**
  Clear the screen to the default backgrounds

  @param void
  @return void
*/
void clearDialog(DialogSize_type dialogSize) {
  tft.fillRect(dialogSize.x, dialogSize.y, dialogSize.width, dialogSize.height, PRIMARY_DARK_COLOR );
  tft.fillRect(dialogSize.x, dialogSize.y, dialogSize.width, 30, PRIMARY_COLOR);
}

//////////////////////////////////////////////////////////////
// UI ELEMENTS CREATION
/////////////////////////////////////////////////////////////

/**
  Create a label widget neither nor editable not tapable

  @param label the ui element
  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param text label text to print
  @param fontsize font size of the text to print
  @return void
*/
void createLabelWidget(UiElement_type * label, const int16_t x , const int16_t y, char * text, const uint16_t fontsize) {
  tft.setTextSize(fontsize);
  int16_t  x1, y1;
  uint16_t wChar, hChar;
  tft.getTextBounds(text, 0, 0, &x1, &y1, &wChar, &hChar);
  label->x = x;
  label->y = y;
  label->width = wChar;
  label->height = hChar;
  label->visible = true;
  label->value = 0;
  label->minValue = 0;
  label->maxValue = 0;
  label->text = text;
  label->fontsize = fontsize;
  label->paint = paintLabel;
  label->hitbox.x1 = x;
  label->hitbox.y1 = y;
  label->hitbox.x2 = x + wChar;
  label->hitbox.y2 = y + hChar;
  label->foreColor = PRIMARY_TEXT_COLOR;
  label->backgroundColor = PRIMARY_DARK_COLOR;
  label->onTap = NULL ;
  label->enabled = false;
}

/**
  Create a spinner widget editable and tapable

  @param spinner the ui element
  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param fontsize font size of the text to print
  @param value current value
  @param minValue minimum settable value included
  @param maxValue maximum  settable value included
  @param format c format string to format value as string
  @param onTap onTap event callback Action function pointer
  @return void
*/
void createSpinnerWidget(UiElement_type* spinner, const int16_t x ,
                         const int16_t y, const uint16_t fontsize, const int16_t value,
                         const int16_t minValue, const int16_t maxValue, char* format, Action onTap) {
  tft.setTextSize(fontsize);
  int16_t  x1, y1;
  uint16_t wChar, hChar;
  char buffer[5];
  snprintf(buffer, sizeof(buffer), format, value);
  spinner->text = buffer;
  tft.getTextBounds(buffer, x, y, &x1, &y1, &wChar, &hChar);
  spinner->x = x;
  spinner->y = y;
  spinner->visible = true;
  spinner->value = value;
  spinner->minValue = minValue;
  spinner->maxValue = maxValue;
  spinner->enabled = true;
  spinner->onTap = onTap ;
  spinner->fontsize = fontsize;
  spinner->paint = paintSpinner;
  spinner->format = format;
  spinner->width = wChar;
  spinner->height = hChar;
  spinner->hitbox.x1 = x;
  spinner->hitbox.y1 = y - hChar;
  spinner->hitbox.x2 = x + wChar;
  spinner->hitbox.y2 = y + 2 * hChar;
  spinner->foreColor = PRIMARY_TEXT_COLOR;
  spinner->backgroundColor = PRIMARY_DARK_COLOR;
}

/**
  Create a button widget tapable

  @param button the ui element
  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param fontsize font size of the text to print
  @param text text value to print
  @param onTap onTap event callback Action function pointer
  @param backgroundColor backgrouncolor
  @return void
*/
void createButtonWidget(UiElement_type* button, const int16_t x ,
                        const int16_t y, char* text, const uint16_t fontsize, Action onTap,
                        const uint16_t backgroundColor) {
  createLabelWidget( button, x , y, text, fontsize);
  button->onTap = onTap ;
  button->enabled = true;
  button->backgroundColor = backgroundColor;
}


//////////////////////////////////////////////////////////////
// UI WIDGETS
/////////////////////////////////////////////////////////////


/**
  Create a time selector widget, creates the five elements of the widget
    hour spinner
    separator label 1
    minute spinner
    separator label 2
    second spinner

  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param fontsize font size of the text to print
  @return void
*/

void createTimeSelectorWidget(const int16_t x , const int16_t y, uint16_t fontsize) {

  int xOffset = 0 ;
  createSpinnerWidget(&uiWidgets[HOUR_ID], x , y,  fontsize, 12, 0, 23, format2d, &spinnerOnTapEvent);
  xOffset = xOffset + uiWidgets[HOUR_ID].width;
  createLabelWidget(&uiWidgets[TIME_SEPARATOR1_ID], x + xOffset, y, colon, fontsize);
  xOffset = xOffset + uiWidgets[TIME_SEPARATOR1_ID].width;
  createSpinnerWidget(&uiWidgets[MINUTE_ID], x + xOffset , y, fontsize, 0, 0, 59, format2d, &spinnerOnTapEvent);
  xOffset += uiWidgets[MINUTE_ID].width;
  createLabelWidget(&uiWidgets[TIME_SEPARATOR2_ID], x + xOffset, y,  colon, fontsize);
  xOffset += uiWidgets[TIME_SEPARATOR2_ID].width;
  createSpinnerWidget(&uiWidgets[SECOND_ID], x + xOffset , y, fontsize, 0, 0, 59, format2d,  &spinnerOnTapEvent);
}

/**
  Create a date selector widget, creates the five elements of the widget
    year spinner
    separator label 1
    month spinner
    separator label 2
    day spinner

  @param x horizontal coordinate in points left upper corner
  @param y vertical coordinate in points left upper corner
  @param fontsize font size of the text to print
  @return void
*/
void createDateSelectorWidget(const int16_t x , const int16_t y, uint16_t fontsize) {
  int xOffset = 0 ;
  createSpinnerWidget(&uiWidgets[YEAR_ID], x  , y,  fontsize, 2020, 2020, 2099, format4d, &spinnerOnTapEvent);
  xOffset += uiWidgets[YEAR_ID].width;
  createLabelWidget(&uiWidgets[DATE_SEPARATOR1_ID], x + xOffset, y, dash, fontsize);
  xOffset += uiWidgets[DATE_SEPARATOR1_ID].width;
  createSpinnerWidget(&uiWidgets[MONTH_ID], x + xOffset , y,   fontsize, 1, 1, 12, format2d, &spinnerOnTapEvent);
  xOffset += uiWidgets[MONTH_ID].width;
  createLabelWidget(&uiWidgets[DATE_SEPARATOR2_ID], x + xOffset , y,  dash, fontsize);
  xOffset += uiWidgets[DATE_SEPARATOR2_ID].width;
  createSpinnerWidget(&uiWidgets[DAY_ID], x + xOffset  , y,  fontsize, 1, 1, 31, format2d, &spinnerOnTapEvent);
}

/**
  Create a button element aligned. LEFT, CENTER or RIGHT

  @param id index of the ui element to use
  @param labelText test to print
  @param onTapEvent on tap event action callback function pointer
  @param fontsize font size of the text to print
  @param yBottom y vertical coordinate in points lower corners
  @param align where to align the button
  @param backgroundColor background color of the button
  @return void
*/
void createAlignedButtonWidget(const int16_t id, char* labelText, Action onTapEvent, const uint16_t fontsize, const int16_t yBottom, const uint16_t align, const uint16_t backgroundColor) {
  tft.setTextSize(fontsize);
  int16_t  x1, y1;
  uint16_t wChar, hChar;
  tft.getTextBounds(labelText, 0, 0, &x1, &y1, &wChar, &hChar);
  int16_t x, y;
  y = yBottom - hChar;
  if (align == ALIGN_RIGHT) {
    x = dialogSize.width - wChar ;
  } else if (align == ALIGN_LEFT) {
    x = 0 ;
  } else {
    x = (dialogSize.width - wChar) / 2 ;
  }
  createButtonWidget(&uiWidgets[id], x, y, labelText, fontsize, onTapEvent, backgroundColor );
}

//////////////////////////////////////////////////////////////
// UI DIALOG
/////////////////////////////////////////////////////////////

void createDateTimePickerDialog(DialogSize_type dialogSize)
{
  const int fontsize = 3;
  tft.setTextSize(fontsize);
  int16_t  x1, y1;
  uint16_t wChar, hChar;
  tft.getTextBounds("0", 0, 0, &x1, &y1, &wChar, &hChar);

  createTimeSelectorWidget((dialogSize.width  - wChar * 8) / 2, dialogSize.height / 2 - hChar * 3, fontsize );
  createDateSelectorWidget((dialogSize.width - wChar * 10) / 2, dialogSize.height / 2 - hChar * 3 + 100, fontsize);
  createAlignedButtonWidget(TIME_SET_ID, SET_LABEL, setButtonOnTapEvent, 3, dialogSize.height, ALIGN_RIGHT, PRIMARY_DARK_COLOR   );
  createAlignedButtonWidget(TIME_CANCEL_ID, CANCEL_LABEL, cancelButtonOnTapEvent, 3, dialogSize.height, ALIGN_LEFT, PRIMARY_DARK_COLOR  );
  createAlignedButtonWidget(TIME_BUTTON_ID, DEFAULT_TIME, timeButtonOnTapEvent, 2, 20, ALIGN_RIGHT, PRIMARY_COLOR );
  createAlignedButtonWidget(DATE_BUTTON_ID, DEFAULT_DATE, timeButtonOnTapEvent, 2, 20, ALIGN_LEFT, PRIMARY_COLOR );

}


//////////////////////////////////////////////////////////////
// CHANGE VISIBILITY METHODS
//////////////////////////////////////////////////////////////

/**
  Changes the visible state for all the date spinner components

  @param visible the new state
  @return void
*/
void setDateSpinnerVisible(const bool visible) {
  for (uint16_t i = 0; i < countof(dateSpinnerWidgets); i++) {
    uiWidgets[dateSpinnerWidgets[i]].visible = visible;
  }
}

/**
  Changes the visible state for all the time spinner components

  @param visible the new state
  @return void
*/
void setTimeSpinnerVisible(const bool visible) {
  for (uint16_t i = 0; i < countof(timeSpinnerWidgets); i++) {
    uiWidgets[timeSpinnerWidgets[i]].visible = visible;
  }
}

/**
  Changes the visible state for cancel button element

  @param visible the new state
  @return void
*/
void setCancelButtonVisible(const bool visible) {
  uiWidgets[TIME_CANCEL_ID].visible = visible;
}

/**
  Changes the visible state for set button element

  @param visible the new state
  @return void
*/
void setSetButtonVisible(const bool visible) {
  uiWidgets[TIME_SET_ID].visible = visible;
}

//////////////////////////////////////////////////////////////
// TIME AND DATE REFRESH METHODS
//////////////////////////////////////////////////////////////

/**
  Returns the current time formatted as hh:mm:ss

  @param void
  @return the formateted current time
*/
char* strTime(void) {
  static char ret[20];    //local static variable
  snprintf_P(ret, countof(ret), PSTR("%02u:%02u:%02u"), hour(), minute(), second());
  return ret;
}

/**
  Returns the current date formatted as yyyy-mm-dd

  @param void
  @return the formateted current date
*/
char* strDate(void) {
  static char ret[20];    //local static variable
  snprintf_P(ret, countof(ret), PSTR("%04u-%02u-%02u"), year(), month(), day());
  return ret;
}

/**
  refresh the time button label with the current time

  @param void
  @return void
*/
void refreshTime(void) {
  uiWidgets[TIME_BUTTON_ID].text = strTime();
  uiWidgets[TIME_BUTTON_ID].paint(&uiWidgets[TIME_BUTTON_ID]);
}

/**
  refresh the date button label with the current date

  @param void
  @return void
*/
void refreshDate(void) {
  uiWidgets[DATE_BUTTON_ID].text = strDate();
  uiWidgets[DATE_BUTTON_ID].paint(&uiWidgets[DATE_BUTTON_ID]);
}

//////////////////////////////////////////////////////////////
// ON TAP EVENT CALLBACKS
//////////////////////////////////////////////////////////////

void spinnerOnTapEvent(UiElement_type * spinner, const int x, const int y) {
  int selectorHeight = (spinner->hitbox.y2 - spinner->hitbox.y1) / 3;
  if ( x >= spinner->hitbox.x1
       && x <= spinner->hitbox.x2
       && y >= spinner->hitbox.y1
       && y <= spinner->hitbox.y2 - 2 * selectorHeight ) {
    //increase value
    if (spinner->value < spinner->maxValue) {
      spinner->value = spinner->value + 1;
    } else {
      // circular rollout
      spinner->value = spinner->minValue;
    }
    spinner->paint(spinner);

  } else if ( x >= spinner->hitbox.x1
              && x <= spinner->hitbox.x2
              && y >= spinner->hitbox.y1 - selectorHeight
              && y <= spinner->hitbox.y2 ) {
    // decrease value
    if (spinner->value > spinner->minValue) {
      spinner->value = spinner->value - 1;
    } else {
      // circular rollout
      spinner->value = spinner->maxValue;
    }
    spinner->paint(spinner);
  }

}

void timeButtonOnTapEvent(UiElement_type * spinner, const int16_t x, const int16_t y) {
  uiWidgets[HOUR_ID].value = hour();
  uiWidgets[MINUTE_ID].value = minute();
  uiWidgets[SECOND_ID].value = second();
  uiWidgets[DAY_ID].value = day();
  uiWidgets[MONTH_ID].value = month();
  uiWidgets[YEAR_ID].value = year();
  setTimeSpinnerVisible(true);
  setDateSpinnerVisible(true);
  setCancelButtonVisible(true);
  setSetButtonVisible(true);
  paintDateTimePickerDialog();
}

void cancelButtonOnTapEvent(UiElement_type * spinner, const int16_t x, const int16_t y) {
  setTimeSpinnerVisible(false);
  setDateSpinnerVisible(false);
  setCancelButtonVisible(false);
  setSetButtonVisible(false);
  clearDialog(dialogSize);
  paintDateTimePickerDialog();
}


void setButtonOnTapEvent(UiElement_type * spinner, const int16_t x, const int16_t y) {
  setTime( uiWidgets[HOUR_ID].value, uiWidgets[MINUTE_ID].value, uiWidgets[SECOND_ID].value,
           uiWidgets[DAY_ID].value, uiWidgets[MONTH_ID].value,  uiWidgets[YEAR_ID].value);
  refreshDate();
  refreshTime();
}

//////////////////////////////////////////////////////////////
// READ UI SELECTION
//////////////////////////////////////////////////////////////

/*
   Checks if the user is selecting any of the visible enabled ui elements
   The onTap callback of the selected element is called and it set as pressed

  @param lastSelected the last selection
  @return the new selection

*/
int readUiSelection(const int16_t lastSelected ) {
  int16_t xpos, ypos;  //screen coordinates
  TSPoint tp = ts.getPoint();   //tp.x, tp.y are ADC values

  // if sharing pins, you'll need to fix the directions of the touchscreen pins
  pinMode(XM, OUTPUT);
  pinMode(YP, OUTPUT);
  // we have some minimum pressure we consider 'valid'
  // pressure of 0 means no pressing!

  if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) {
    xpos = map(tp.x, TS_RT, TS_LEFT, 0, tft.width());
    ypos = map(tp.y, TS_BOT, TS_TOP, 0, tft.height());
    // are we in buttons area ?
    for (int i = 0; i < WIDGETS_NO; i++) {
      if ( uiWidgets[i].enabled == true && uiWidgets[i].visible == true) {
        if ( xpos >= uiWidgets[i].hitbox.x1 && xpos <= uiWidgets[i].hitbox.x2
             && ypos >= uiWidgets[i].hitbox.y1 && ypos <= uiWidgets[i].hitbox.y2) {
          if (lastSelected != i && lastSelected >= 0 ) { // unselect last selection
            uiWidgets[lastSelected].pressed = false;
            uiWidgets[lastSelected].paint(&uiWidgets[lastSelected]);
          }
          uiWidgets[i].pressed = true;
          uiWidgets[i].paint(&uiWidgets[i]);
          uiWidgets[i].onTap(&uiWidgets[i], xpos, ypos);
          // debounce tap
          delay(120);
          return i;
        }
      }
    }
  }
  if (lastSelected >= 0 && uiWidgets[lastSelected].pressed) {
    // unselect last selection
    uiWidgets[lastSelected].pressed = false;
    uiWidgets[lastSelected].paint(&uiWidgets[lastSelected]);
  }
  return -1;
}

Credits

Enrique Albertos
12 projects • 38 followers
Pedestrian.

Comments