One button, two behaviours, non-blocking code

I think this will do what you want without blocking. You should be able to encapsulate this in an object and pass in callback functions for short-press and long-press.

const byte ButtonPin = 2;
const unsigned long DebounceTime = 30;
const unsigned long ButtonLongPressTime = 2000;

boolean ButtonWasPressed;  // Defaults to 'false'
boolean ButtonWasLongPressed;  // Defaults to 'false'
unsigned long ButtonStateChangeTime = 0; // Debounce/Long Press timer

void setup()
{
  pinMode (ButtonPin, INPUT_PULLUP);  // Button between Pin and Ground
}

void loop()
{
  unsigned long currentTime = millis();

  boolean buttonIsPressed = digitalRead(ButtonPin) == LOW;  // Active LOW

  // Check for button state change and do debounce
  if (buttonIsPressed != ButtonWasPressed &&
      currentTime - ButtonStateChangeTime > DebounceTime)
  {
    // Button state has changed
    ButtonStateChangeTime = currentTime;
    ButtonWasPressed = buttonIsPressed;

    if (ButtonWasPressed)
    {
      // Button was just pressed
      ButtonWasLongPressed = false;
    }
    else
    {
      // Button was just released
      if (!ButtonWasLongPressed)
      {
        // ACT ON SHORT PRESS
      }
    }

    // Check to see if we are in a long-press
    if (ButtonWasPressed && ! ButtonWasLongPressed &&
        currentTime - ButtonStateChangeTime > ButtonLongPressTime)
    {
      ButtonWasLongPressed = true;
      // ACT ON LONG PRESS
    }
  }
}