[SOLVED] OOP Inheretance

I am trying to expand a class for a TFT LCD screen. My objective is to create a few primitive elements and then a "Screen" base class from which I can poly-morph.

Attached is the library I am trying to build on. I added GuiBase.h GuiBase.cpp Button.h and Button.cpp.

The purpose of the GuiBase class is to hold LCD and touch screen objects, along with a few color choices.
The Button class then inherits publicly from the GuiBase class, where it uses the GuiBase members to draw objects on the screen. I tried the below code to test my implementation :

#include <Button.h>

Button b1(0,0,150,100,"Test");

void setup() {
  Serial.begin(9600);
  Serial.println("Hello");
  b1.draw();
  Serial.println("World");
}

void loop() {
  
}

The code compiles, however the seial port does not print (Both "Hello" and "World" does not print), and nothing is drawn on the screen. This to me indicates some sort of hang like a null pointer.

I would appreciate it if someone could please have a look at the GuiBase and Button classes to see where I screwed up. Or if my plan is fundamentally flawed.

Regards
Kerneels

SPFD5408.zip (32.6 KB)

If you hide them in a ZIP file fewer people are going to bother to look.

GuiBase.h

#ifndef GUIBASE_H
#define GUIBASE_H

#include <SPFD5408_Adafruit_GFX.h>
#include <SPFD5408_Adafruit_TFTLCD.h>
#include <SPFD5408_TouchScreen.h>

class GuiBase {

public:
  void clear();

protected:
  TouchScreen _ts;
  Adafruit_TFTLCD _lcd;

  const uint16_t _TEXT_COLOR;
  const uint16_t _BACK_COLOR;
  const uint16_t _CREATED_COLOR;
  const uint16_t _TOUCHED_COLOR;
  const uint16_t _INACTIVE_COLOR;
  const uint16_t _INACTIVE_TEXT_COLOR;

  GuiBase(  uint16_t textColor         = 0xFFFF,
            uint16_t backColor         = 0x0000,
            uint16_t createdColor      = 0x07FF,
            uint16_t touchedColor      = 0xF81F,
            uint16_t inactiveColor     = 0xF800,
            uint16_t inactiveTextColor =  0x0000);

private:
  GuiBase(const GuiBase&);
  GuiBase& operator=(const GuiBase&);
};
#endif

GuiBase.cpp

#include "GuiBase.h"

GuiBase::GuiBase(
    uint16_t textColor, uint16_t backColor,
    uint16_t createdColor, uint16_t touchedColor,
    uint16_t inactiveColor, uint16_t inactiveTextColor)
    : _TEXT_COLOR(textColor),
      _BACK_COLOR(backColor),
      _CREATED_COLOR(createdColor),
      _TOUCHED_COLOR(touchedColor),
      _INACTIVE_COLOR(inactiveColor),
      _INACTIVE_TEXT_COLOR(inactiveTextColor),
      _ts(8, 9, A3, A2, 300),
      _lcd(A3, A2, A1, A0, A4)
{
  _lcd.reset();
  _lcd.begin(0x9341);
  _lcd.setRotation(3);
}

void GuiBase::clear() {
  _lcd.fillScreen(_BACK_COLOR);
}

Button.h

#ifndef BUTTON_H
#define BUTTON_H

#include "GuiBase.h"

class Button : public GuiBase {

public:
  Button(int16_t x,int16_t y,int16_t w,int16_t h, char *label, uint8_t textSize = 2);

  void draw();

private:
  int16_t _x,_y;
  int16_t _w,_h;
  char _label[10];
  uint8_t _tSize;
};
#endif

Button.cpp

#include "Button.h"

Button::Button(int16_t x,int16_t y,int16_t w,int16_t h, char *label, uint8_t textSize)
  : GuiBase()
{
  _x = x;
  _y = y;
  _w = w;
  _h = h;
  strncpy(_label, label,9);
  _label[9] = 0;
  _tSize = textSize;
}

void Button::draw() {
  _lcd.fillRoundRect(_x,_y,_w,_h,min(_w,_h)/4,_CREATED_COLOR);
  _lcd.setCursor(_x + (_w/2) - strlen(_label)*3*_tSize, _y +(_h/2)-4*_tSize);
  _lcd.setTextColor(_TEXT_COLOR);
  _lcd.setTextSize(_tSize);
  _lcd.print(_label);
}
  strncpy(_label, label,9);
  _label[9] = 0;

If the string passed in is 3 characters, why are you copying 9? Why are you putting a NULL in the last position of the array?

GuiBase::GuiBase(
    uint16_t textColor, uint16_t backColor,
    uint16_t createdColor, uint16_t touchedColor,
    uint16_t inactiveColor, uint16_t inactiveTextColor)
    : _TEXT_COLOR(textColor),
      _BACK_COLOR(backColor),
      _CREATED_COLOR(createdColor),
      _TOUCHED_COLOR(touchedColor),
      _INACTIVE_COLOR(inactiveColor),
      _INACTIVE_TEXT_COLOR(inactiveTextColor),
      _ts(8, 9, A3, A2, 300),
      _lcd(A3, A2, A1, A0, A4)
{
  _lcd.reset();
  _lcd.begin(0x9341);
  _lcd.setRotation(3);
}

Why do you suppose that the Adafruit_TFTLCD class has a begin() method? Why doesn't it just do what begin() does in the constructor? Do you suppose that maybe what it does in begin() can't be done in the constructor? So, why are you trying to defeat the purpose of that class having a begin() method be calling it in your constructor?

If the string passed in is 3 characters, why are you copying 9? Why are you putting a NULL in the last position of the array?

Fixed it to copy only the string length, and if the length is greater 10, then only the first 9. I am appending NULL to identify the end of the string, since print only takes one argument, I assumed it required an end of string identifier.

if( strlen(label) < 10) {
      strncpy(_label, label,strlen(label));
      _label[strlen(label)] = '\0';
  } else {
    strncpy(_label, label, 9);
    _label[9] = '\0';
  }

Why do you suppose that the Adafruit_TFTLCD class has a begin() method? Why doesn't it just do what begin() does in the constructor? Do you suppose that maybe what it does in begin() can't be done in the constructor? So, why are you trying to defeat the purpose of that class having a begin() method be calling it in your constructor?

From what I can see, the begin() method determines what LCD driver is being used, and I honestly don't know why it cant be called in the Adafruit_TFTLCD constructor?

Interestingly, the program works if I use a pointer to create the object.

#include <Button.h>

Button *b1;

void setup() {
  Serial.println("Hello");
  b1 = new Button(0,0,150,100,"Test");
  b1->draw();
  Serial.println("World");
}

void loop() {
}

Any idea as to why that would be? Something to do with preprocessing or the compiler?

I honestly don't know why it cant be called in the Adafruit_TFTLCD constructor?

Take a look at what the begin() method does. If, as is typical with begin() methods, it does stuff with the hardware, setting pin modes, for instance, it has to be called AFTER init() is called, which happens AFTER all the global constructors are called.

Any idea as to why that would be?

Sure. Now, your constructor is not called until setup() is called, which happens AFTER init() is called.

Add a begin() method to your class. Move all the stuff that is in the body of the constructor to the begin() method. Call the begin() method on any global objects in setup().

Take a look at what the begin() method does. If, as is typical with begin() methods, it does stuff with the hardware, setting pin modes, for instance, it has to be called AFTER init() is called, which happens AFTER all the global constructors are called.

That you for clarifying. Sometimes easy to get lost in the abstraction the environment provides.