Having issues with setting up a factory

Hello, I'm working on a simple (kinda simple, at least) class(es) that I can use to easily manage text on a TFT display. There will be a class that's initially initiated that you can pass the Adafruit_SSD1351 object to, as well as the default/initial settings (text color, bg color, size, etc etc), then after that you can create new TextBoxes and specify where to put them, and those settings will get saved to that specific text box. This will let me place textareas anywhere and update them without having to set the colors/sizes/fonts/locations, etc.

But it will be using two separate classes. One for the initial object that's given the GFX object and default settings, then it basically acts as a factory to the other class, where creating a new text area will create a new object from the "TextArea" class. (hopefully that was a summary enough).

Here's how I have it laid out currently (or at least the relevant parts), though there's something wrong with the factory bit.

Initial Class (ill call Factory):
Factory.hpp:

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "TextArea.hpp"


class Factory {
public: 

  Factory(Adafruit_SSD1351 &GFXHandler);

  void begin();
  void begin( uint16_t fgColor );
  void begin( uint16_t fgColor, uint16_t bgColor );

  void setColor( uint16_t fgColor );
  void setColor( uint16_t fgColor, uint16_t bgColor );


  TextArea newTextArea( int x, int y );

 // void print( char val );

private:
  Adafruit_SSD1351 * GFXHandler;

  void _begin();

  //int _x, _y;

  // DEFAULT fg/bg colors (can be changed on a TextArea basis);
  uint16_t _defFgColor = 0x95e7;
  uint16_t _defBgColor = 0x0000;
};

Factory.cpp:

#include "Factory.hpp"

Factory::Factory(Adafruit_SSD1351 &GFXHandler) : GFXHandler (&GFXHandler) {}

TextArea Factory::newTextArea( int x, int y ){

  TextArea _txtArea( *GFXHandler );

  // Do default stuff to this oled, using the settings
  // provided when creating the Factory object..

  return _txtArea;
}

void Factory::begin(){
  _begin();
}

void Factory::begin( uint16_t fgColor , uint16_t bgColor ){
  _defFgColor = fgColor;
  _defBgColor = bgColor;

  _begin();
}

void Factory::begin( uint16_t fgColor ){
  _defFgColor = fgColor;

  _begin();
}

void Factory::_begin(){
  GFXHandler->begin();
  GFXHandler->setFont();
  GFXHandler->fillScreen(_defBgColor);
  GFXHandler->setTextColor(_defFgColor);
  GFXHandler->setTextSize(1);
}

void Factory::setColor( uint16_t fgColor ){
  _defFgColor = fgColor;
}

void Factory::setColor( uint16_t fgColor, uint16_t bgColor ){
  _defFgColor = fgColor;
  _defBgColor = bgColor;
}

Now for the class that the Factory class will be interfacing with, lets call it TextArea.
TextArea.hpp:


#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>

class TextArea {
public: 

  TextArea(Adafruit_SSD1351 &GFXHandler);


  // Set/update the print containers display value to a string, then display it
  void print(char *charValue);
 
  void setCursor( int x, int y );

  void setColor( uint16_t fgColor );
  void setColor( uint16_t fgColor, uint16_t bgColor );

  // Function to re-print whatever value/type is saved (will be useful if the
  // printed text needs to be "reloaded" for various reasons)
  void print();
private:

  Adafruit_SSD1351 * GFXHandler;

  int _x = NULL, _y = NULL;
  uint16_t _fgColor = NULL, _bgColor = NULL;

  char _printerValPrev[MAX_CHAR_LENGTH]; 

  char _printerVal[MAX_CHAR_LENGTH]; 
};

TextArea.cpp:


#include "TextArea.hpp"

TextArea::TextArea(Adafruit_SSD1351 &GFXHandler) : GFXHandler (&GFXHandler) {}

// If user were to set/change the printer to a CHAR...
void TextArea::print( char *charValue )
{
  strcpy(_printerVal, charValue);
  print();
}

void TextArea::setCursor( int x, int y ) {
  _x = x;
  _y = y;
}

void TextArea::setColor( uint16_t fgColor ){
  _fgColor = fgColor;
  print();
}

void TextArea::setColor( uint16_t fgColor, uint16_t bgColor ){
  _fgColor = fgColor;
  _bgColor = bgColor;
  print();
}

// This would print the stored value in the specified area, but for this forum
// post, I'm just outputting the value to serial
void TextArea::print()
{
  GFXHandler->setTextSize(1);
  GFXHandler->setCursor( _x, _y );
  GFXHandler->setTextColor( _fgColor, _bgColor );
  GFXHandler->print( _printerVal );
}

And this is how I would prefer to be able to use it (or something like this)

#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include "Factory.hpp"

Adafruit_SSD1351 oled(128, 128, &SPI, 10, 9, -1 );

// Create main object the simple factory
Factory UUI(oled);

// Then define two variables that can be used globally
TextArea timeDisplay;
TextArea milliseconds;

void setup() {
  // Execute the "begin" function, providing default values..
  UUI.begin(0x95e7, 0x0000);

  // Create the timeDisplay textarea, and set the x/y location
  TextArea timeDisplay = UUI.newTextArea(15, 10);
  // Give it its own color
  timeDisplay.setColor(0xc7a9);

  // Create the milliseconds textarea, and set the x/y location
  TextArea milliseconds = UUI.newTextArea(25, 30);
  milliseconds.setColor(0xF800);


  timeDisplay.print("<Time here>");
  milliseconds.print("<Uptime here>");
}

void loop(){
  // Update the milliseconds TextArea with the new time
  milliseconds.print(millis());
  timeDisplay.print(someFunctionThatMakesAFullTimeString());
}

You'll see all of the logic for setting the values/data for the textareas goes in the setup(), but since I want them to be global, I have to define the variables at the root. But evidently the TextArea timeDisplay; at the root isn't simply setting a variable that can be populated later and accessed globally, it's actually trying to initiate the object:

Compilation error: no matching function for call to 'TextArea::TextArea()'

So I guess my question is - What's the best way to handle this?

I was thinking the Factory object would literally just be there to hand off some default settings to the TextArea objects, but not have to store the object locally, but if I want to be able to create new TextArea objects (especially without having to define them in the root), i'll have to create an array in the Factory class itself to store the created TextArea objects in, and then access the TextArea via the Factory object for every call (instead of directly once it's been handed off from the Factory). Is that right? Or is there a way to make it work like I was planning to above?

Thanks in advance,
-J

A common way to solve this is using a global pointers rather than objects for your milliseconds and textDisplay variables. Than change the newAnchor method to create a TextArea object with a new() command and return the pointer to it.
Sorry, I wrote this on my phone so I can't to dive in detail more. I hope you understand:)

Yeah I think so. That makes sense to me. Ill give it a shot. Thank you!

I was able to get it to work by changing the Factory and .ino files:
Factory.hpp:


#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1351.h>
#include "TextArea.hpp"


class Factory {
public: 

  Factory(Adafruit_SSD1351 &GFXHandler);

  void begin();
  void begin( uint16_t fgColor );
  void begin( uint16_t fgColor, uint16_t bgColor );

  void setColor( uint16_t fgColor );
  void setColor( uint16_t fgColor, uint16_t bgColor );


  TextArea * newTextArea( int x, int y );

 // void print( char val );

private:
  Adafruit_SSD1351 * GFXHandler;

  void _begin();

  //int _x, _y;

  // DEFAULT fg/bg colors (can be changed on a TextArea basis);
  uint16_t _defFgColor = 0x95e7;
  uint16_t _defBgColor = 0x0000;
};

Factory.cpp:


#include "Factory.hpp"

Factory::Factory(Adafruit_SSD1351 &GFXHandler) : GFXHandler (&GFXHandler) {}

TextArea * Factory::newAnchor( int x, int y ){

  TextArea * _anchor = new( *GFXHandler );

  // Do default stuff to this oled, using the settings
  // provided when creating the Factory object..

  return _anchor;
}

void Factory::begin(){
  _begin();
}

void Factory::begin( uint16_t fgColor , uint16_t bgColor ){
  _defFgColor = fgColor;
  _defBgColor = bgColor;

  _begin();
}

void Factory::begin( uint16_t fgColor ){
  _defFgColor = fgColor;

  _begin();
}

void Factory::_begin(){
  GFXHandler->begin();
  GFXHandler->setFont();
  GFXHandler->fillScreen(_defBgColor);
  GFXHandler->setTextColor(_defFgColor);
  GFXHandler->setTextSize(1);
}

void Factory::setColor( uint16_t fgColor ){
  _defFgColor = fgColor;
}

void Factory::setColor( uint16_t fgColor, uint16_t bgColor ){
  _defFgColor = fgColor;
  _defBgColor = bgColor;
}

Then the .ino file:

#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include "Factory.hpp"

Adafruit_SSD1351 oled(128, 128, &SPI, 10, 9, -1 );

// Create main object the simple factory
Factory Factory(oled);

// Then define two variables that can be used globally
TextAnchor * timeDisplay
TextAnchor * milliseconds;


void setup() {
  // Execute the "begin" function, providing default values..
  Factory.begin(0x95e7, 0x0000);

  timeDisplay = Factory.newAnchor(15, 10);
  milliseconds = Factory.newAnchor(25, 30);

  // Create the timeDisplay textarea, and set the x/y location
  TextArea timeDisplay = Factory.newAnchor(15, 10);

  // Give them some unique color
  timeDisplay->setColor(0xc7a9);
  milliseconds->setColor(0xF800);


  timeDisplay->print("<Time here>");
  milliseconds->print("<Uptime here>");
}

void loop(){
  // Update the milliseconds TextArea with the new time
  timeDisplay->print(someFunctionThatMakesAFullTimeString());

  milliseconds->print(millis());
  delay(500);
}

And thank you @b707 ,that does seem to work great! This got me to look into the difference between creating an object using

TextArea timeDisplay = UUI.newAnchor(15, 10);

vs.

TextArea timeDisplay = new UUI.newAnchor(15, 10);

And I found a bunch of info about how dynamic allocation is a bit "frowned upon". Here's one such comment on a SE Post:

Does this seem like something I may need to work about? The only other alternative I can think of is store the pointers to the newly created objects in an array within the Factory object, and require every action to go through the Factory. Which I guess isn't a terrible way to go about it, it's just not what I was originally intending to create.

Yeah, that's how I'm doing it now. I was thinking it would be useful to be able to create/delete textareas as needed, but I think if I went that route, the ideal way would be to limit how many can be created, and allocate memory ahead of time. Then if I set the limit to 10, the user would be required to "delete" one of the instances before creating a new one.

Would that cause a lot of memory issues?

You don't have a newAnchor() anywhere in Factory.hpp. So how can it even be part of the Factory class?

Hard to tell since you didn't include the code for the TextAnchor class.

My bad, the above was psudo-code so I could get rid of some of the irrelevant stuff to make it easier to share.

Updated the original post. s/TextAnchor/TextArea/g.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.