One button, two behaviours, non-blocking code

Now that you have a 'struct' you are very close to having a 'class'. The only difference is that the first entries in a 'struct' are public and the first entries in a 'class' are private. Start by moving the functions into the struct:

struct Button
{
  Button(int pin, int weight) : pin(pin), weight(weight) {}
  void begin() {pinMode(pin, INPUT_PULLUP);}
  const uint8_t pin: 7;
    const uint8_t weight: 7;
    bool pressDetected: 1;
    bool longPressDetected: 1; /*this is new*/
    bool state: 1;
    unsigned long timestamp;
    int poll();
    const int PRESSED = LOW;
    const unsigned DEBOUNCE = 20;
    const unsigned LONG_PRESS = 2000;
  } WeightButton(2, 12);  // Declare an instance of Button

// Now that 'poll' is a member of the Button struct, you 
// don't have to pass a pointer to the struct.
  int Button::poll() /* Non-blocking version */
{
  unsigned long currentTime = millis();
  bool buttonPressed = digitalRead(pin) == PRESSED;

  if (buttonPressed != pressDetected &&
      currentTime - timestamp > DEBOUNCE)
  {
    timestamp = currentTime;
    pressDetected = buttonPressed;

    if (pressDetected)
    {
      // Button was just pressed
      longPressDetected = false;
    }
    else
    {
      // Button was just released
      if (! longPressDetected)  // Short press?
        return weight;
    }

  if (pressDetected && ! longPressDetected &&
      currentTime - timestamp > LONG_PRESS)
  {
    longPressDetected = true;
    return 0 - weight;
  }

  return 0;
}

void setup()
{
  WeightButton.begin();
}

void loop()
{
  int wb_val = WeightButton.poll();
}

Now all you need to do to make it a 'class' is change:

struct Button
{

to

class Button
{
  public: