Override VCL class/component protected method - How to code and use? - c++

I am rewriting old existing code and I'm giving the icons an overhaul. I used to have bitmaps assigned to TMenuItems but I'm changing that in favor of ImageIndex and a TImageList with colordepth 32bit, containing icons with an alpha channel. The ImageList is created and populated with icons at design time. The ImageIndex are assigned during program startup and changed if/when appropriate.
I noticed that when a MenuItem is disabled (enabled = false), the resulting image doesn't look great (at all) and I read that this is due to VCL. Mentioned link also links to Delphi code that can convert an icon to its greyscale values.
I'm not fluent in Delphi nor changing VCL components, subclassing them, inheriting from them etc. I normally simply use what is available without changing it. so I'm starting with some basic questions:
Here's a very simple attempt to inherit from TImage and override DoDraw(), to make it never disable the icon in the first place (decipering the Delphi code to greyscale can be done in a second step)
class MyTImageList : public TImageList
{
public:
__fastcall MyTImageList(Classes::TComponent* AOwner)
: TImageList(AOwner) {} ;
virtual __fastcall DoDraw(int Index, TCanvas *Canvas, int X, int Y, unsigned int Style, bool Enabled = true)
{
return TImageList::DoDraw(Index, Canvas, X, Y, Style) ;
}
};
FYI: I use C++ Builder 2009
It does not compile, error: [BCC32 Error] Main.h(1018): E2113 Virtual function '_fastcall TMainForm::MyTImageList::DoDraw(int,TCanvas *,int,int,unsigned int,bool)' conflicts with base class 'TCustomImageList'
Since I'm very insecure about inheriting from VCL component classes I'm not sure if I'm dealing with a typo or something very constructively wrong ? Kindly enlighten me.
Assuming this compiles I'm actually not sure how to proceed further either.
Because the ImageList is created at design time, and used throughout the code. For this change to work I have to work with 'MyTimageList'.
So, do I create MyTimageList during Form construction and Assign() the content of the design-time-ImageList, or is there a more efficient way to avoid copying over everything ?
Actually, thinking about the latter question more, I could simply use the internal ImageList of the design time Imagelist instance.

Here is a C++ translation of the Delphi code:
class MyTImageList : public TImageList
{
protected:
virtual void __fastcall DoDraw(int Index, Graphics::TCanvas *Canvas, int X, int Y, unsigned Style, bool Enabled = true);
public:
__fastcall MyTImageList(Classes::TComponent* AOwner, TImageList *DesignImageList);
};
__fastcall MyTImageList::MyTImageList(Classes::TComponent* AOwner, TImageList *DesignImageList)
: TImageList(AOwner)
{
ColorDepth = DesignImageList->ColorDepth;
Handle = DesignImageList->Handle; // Use the internally kept List of the design time ImageList
ShareImages = true;
}
unsigned __fastcall GetRGBColor(TColor Value)
{
unsigned Result = ColorToRGB(Value);
switch (Result)
{
case clNone: Result = CLR_NONE; break;
case clDefault: Result = CLR_DEFAULT; break;
}
return Result;
}
void __fastcall MyTImageList::DoDraw(int Index, TCanvas *Canvas, int X, int Y, unsigned Style, bool Enabled)
{
if ((Enabled) || (ColorDepth != cd32Bit))
{
TImageList::DoDraw(Index, Canvas, X, Y, Style, Enabled);
}
else if (HandleAllocated())
{
IMAGELISTDRAWPARAMS Options = {0};
Options.cbSize = sizeof(Options);
Options.himl = (HIMAGELIST) Handle;
Options.i = Index;
Options.hdcDst = Canvas->Handle;
Options.x = X;
Options.y = Y;
Options.cx = 0;
Options.cy = 0;
Options.xBitmap = 0;
Options.yBitmap = 0;
Options.rgbBk = GetRGBColor(BkColor);
Options.rgbFg = GetRGBColor(BlendColor);
Options.fStyle = Style;
Options.fState = ILS_SATURATE; // Grayscale for 32bit images
ImageList_DrawIndirect(&Options);
}
}

OK, the answer is simple, and it needed some trial and error to get there, because the documentation I had access to was not clear about that. (E.g. also wrong about the TCanvas pointer).
The function/method returns void. THAT's what was missing in the originally posted code and what caused the error. I got following code to work nicely.
class MyTImageList : public TImageList
{
public:
__fastcall MyTImageList(Classes::TComponent* AOwner, TImageList *DesignImageList)
: TImageList(AOwner)
{
Handle = DesignImageList->Handle ; // Use the internally kept List of the design time ImageList
ShareImages = true;
}
protected:
virtual __fastcall void DoDraw(int Index, TCanvas *Canvas, int X, int Y, unsigned int Style, bool Enabled = true)
{
return TImageList::DoDraw(Index, Canvas, X, Y, Style, true /*Enabled*/) ; // Always draw enabled
}
};
This issue is closed.

Related

can i cast a template instance with arg uint to same template instance with arg int - is it compliant - to which standard?

Plz check following c++ code: (nothing special, should be compliant to c++ 2nd edition from 1991)
class C
{
// also defines all the methods called in template method below with the obvious name and args.
public: template<typename TEnum> void SetNullableEnumValuePtr(CNullable<TEnum>* aEnumPtr)
{
if(sizeof(TEnum) == 4)
{
this->SetNullableUInt32Ptr(reinterpret_cast<CNullable<UInt32>*>(aEnumPtr));
}
else if (sizeof(TEnum) == 2)
{
this->SetNullableUInt16Ptr(reinterpret_cast<CNullable<UInt16>*>(aEnumPtr));
}
else if (sizeof(TEnum) == 1)
{
this->SetNullableUInt8Ptr(reinterpret_cast<CNullable<UInt8>*>(aEnumPtr));
}
else
{
this->FailEnumSize();
}
}
}
basic conditions
class CNullable follows the well known, basic nullable pattern implemented i.e. for c# in certain frameworks but could also be any template with one argument.
as the names imply, TEnum is used for different enum types. i.e. enum FooEnum { foo };
For different platforms/compilers it is true, that with for the same enum type it goes to different if branches: On Win32 all enums are compiled with a size of 4 bytes always. On my MCU it compiles to uint8, if the values of the enum field implies to do so.
1. Not my focus:
i'm not sure, if the c++ standard allows compliant compilers to generate enums with different sizes in respect of the required size but i think it is said, that it is compliant.
2. not my focus:
it is also obvious that a cleaner / more robust solution would generate explicit template instances for the function for ALL used enum types. however i might do this and hope the compiler/optimizer is smart/compliant enough not to generate extra code for all types.
3. Not my focus:
i know that reinterpret_cast can be evil but there is a reason the c++ standard defines it. so please stop downrating my questions just because you dont like it or dont understand it.
4. Not my focus:
i know there are certain websites, you can test code on different compilers.
5. would help too:
also if you have a better idea how to treat this coding issue you're welcome.
6. My focus:
i wanna focus this question on the issue, if the c++ standard [1] (or any other standard) defines rule(s), that the templates instances for intXX and uintXX (Where XX is the bit width and u indicates a unsigned version of the native integer) must forcefully compile to a code where a reinterpret_cast from CNullable<unsigned intXX> to CNullable<intXX> is guaranteed to run properly. it seems to be likely that a straight forward implementation of the tool chain may not cause problems in this case, but i got used to be carefull with such assumptions.
conclusion:
if one could give me the reference(s) to chapter inside the (c++) or other standard treading these issues (1 to 6) - that would be great.
thank you!
[1] Please understand the sad reality, that the platform i work on does even not have a ready compiler specification. so it is quite trial and error and i wanna understand the techniques behind to get a better feeling for it - because treading the issue deterministic seems not to be possible since the compiler reference does not give a clear statement what standard it supports: They stated an ETSI standard rather than an iso c++ standard and they didnt give enough info to find the documentation of the standard - no version number, no document number, no document name. when i search for the info given in the compiler documentation on the ETSI page i get > 24.000 results (!) XD
PS: Edit: I request the info:
a) Which standards / specifications ensure that this code runs properly?
b) Does anyone have a clean solution for an abstraction using different template instances for different enum types?
Ok think i i have a clean solution. It uses a wrapper pattern to hide the abstraction of concrete value types behind a virtual method.
I hope that is enough code to get the intention. I also put a very simple example of how i use it.
I guess in newer c++ version there are some stl elements which can replace the classes i wrote. But i need it for a very old c++ standard, which even isn't iso.
I really hope there are no errors since not all symbols are defined. but the essential stuff shall be shown in this example implementation.
// platform dependent UInt32 type.
typedef unsigned __int32 UInt32;
template<typename T>
class CNullable
{
public: bool HasValue;
public: T Value;
public: CNullable(): Value(), HasValue(false) {}
public: CNullable(const T& aValue): Value(aValue), HasValue(true) {}
public: template<typename T1>
CNullable<T1> To() const
{
return this->HasValue ? CNullable<T1>(static_cast<T1>(this->Value)) : CNullable<T1>();
}
CNullable<UInt32> ToU32n() const
{
return this->To<UInt32>();
}
};
// U32-Nullable type
typedef CNullable<UInt32> UInt32n;
// U32-Nullable argument type.
typedef const CNullable<UInt32>& UInt32na;
// Some kind of variant value that can store different types of values pointing into an (intended) statically allocated model
class CValue
{
// Report failure. Break debuger, raise exception, log error - whatever.
private: static void Fail(const char* aTextPtr);
// Sets the value in the (intended) statically allocated datamodel
// Simple example with a UInt32 as new value
public: void SetTargetValue(UInt32na rhs);
// Sets the value in the (intended) statically allocated datamodel
// May use a visitor pattern (GOF) for resolving assigned types, but thats not the topic here.
public: void SetTargetValue(const CValue& rhs);
// Ensures that object is not sealed an fails if so.
private: bool CheckNotSealed();
// Allows to change to sealed state to protect the object agains modification.
// protection for "as static as possible"-memory-design
public: void Seal();
// Base class for Wrappers
class CValueWrapper
{
public: virtual ~CValueWrapper() {}
// Converts the current value as an U32.
public: virtual UInt32n GetInterpretedU32n() { Fail("Data conversion not supported."); return UInt32n(); }
// converts the new value from an U32 and sets the value
public: virtual void SetInterpretedU32n(UInt32na aU32n) { Fail("Data conversion not supported."); }
};
// Wrappers Base class for any enum related type.
class CEnumWrapperBase : public CValueWrapper
{
public: virtual UInt32n GetU32n() const = 0;
public: virtual void SetU32n(UInt32na aU32n) const = 0;
public: virtual UInt32n GetInterpretedU32n() { return this->GetU32n(); }
public: virtual void SetInterpretedU32n(UInt32na aU32n) { this->SetU32n(aU32n); }
};
// Wrapper base class for values of type = Nullable<TEnums>
template<class TEnum> class CNullableEnumWrapper : public CEnumWrapperBase
{
private: CNullable<TEnum>* mNullableEnumPtr;
public: CNullableEnumWrapper(CNullable<TEnum>* aNullableEnumPtr)
:
mNullableEnumPtr(aNullableEnumPtr)
{
}
public: virtual UInt32n GetU32n() const
{
return this->mNullableEnumPtr ? this->mNullableEnumPtr->ToU32n() : UInt32n();
}
public: virtual void SetU32n(UInt32na aU32n) const
{
if (this->mNullableEnumPtr)
{
*this->mNullableEnumPtr = aU32n.To<TEnum>();
}
}
};
// Wrapper base class for values of type = Nullable<TEnums>
template<class TEnum> class CEnumWrapper : public CEnumWrapperBase
{
public: CEnumWrapper(TEnum* aEnumPtr)
:
mEnumPtr(aEnumPtr)
{
}
private: TEnum* mEnumPtr;
public: virtual UInt32n GetU32n() const
{
return this->mEnumPtr ? static_cast<UInt32>(*this->mEnumPtr) : UInt32n();
}
public: virtual void SetU32n(UInt32na aU32n) const
{
if (this->mEnumPtr
&& aU32n.HasValue)
{
*this->mEnumPtr = static_cast<TEnum>(aU32n.Value);
}
}
};
// Allows to lock instantian of wrapper objects.
// In my bare metal application all wrappers are created on application startup
// and stay allocated until the device is switched of (by disconnecting power)
// [ThreadStatic]
public: static bool InstanciateValueWrapperEnabled;
// Set pointer to enum value (intended to be allocated in a static model)
public: template<class TEnum> void SetEnumValuePtr(TEnum* aEnumPtr)
{
if (this->InstanciateValueWrapperEnabled)
{
if (this->CheckNotSealed())
{
this->SetValueWrapperPtr(new CEnumWrapper<TEnum>(aEnumPtr), true);
}
}
else
{
Fail("Invalid operation.");
}
}
// Set pointer to nullable<enum> value (intended to be allocated in a static model)
public: template<class TEnum> void SetNullableEnumValuePtr(CNullable<TEnum>* aEnumPtr)
{
if (this->InstanciateValueWrapperEnabled)
{
if (this->CheckNotSealed())
{
this->SetValueWrapperPtr(new CNullableEnumWrapper<TEnum>(aEnumPtr), true);
}
}
else
{
Fail("Invalid operation.");
}
}
// Sets the member var and data type to 'CValueWrapper' (may support some types natively without a wrapper object)
public: void SetValueWrapperPtr(CValueWrapper* aValueWrapperPtr, bool aOwning);
};
// Model Base Code
//////////////////////////////////////
/// Application specific code
enum FooEnum { FooEnum_Val1 };
// Reads data from StdIn, uart, or whatever.
UInt32 ReadU32();
// Process data, for example output calculated data to another hardware interface.
void Process(CValue** aValuePtrs);
// Simple example of how its being used.
// in real environment its likely to encapsulate a set of CValue objects
// in a ModelInstance-Object and build it by a ModelDefinition object parsing a model definiton language (mdl)
// or adapt generated code. etc. etc...
void main()
{
// Define the static model:
static FooEnum gFooEnum;
static CNullable<FooEnum> gNullableFooEnum;
// Define the values to access the static model
CValue aFooEnumVal;
CValue aNullableFooEnumVal;
// Begin of init:
CValue::InstanciateValueWrapperEnabled = true;
aFooEnumVal.SetEnumValuePtr(&gFooEnum);
aNullableFooEnumVal.SetNullableEnumValuePtr(&gNullableFooEnum);
CValue::InstanciateValueWrapperEnabled = false;
// End of init
// Create an array of values
const UInt32 aPropertyCount = 2;
CValue* aPropertyPtrs[aPropertyCount] =
{
&aFooEnumVal,
&aNullableFooEnumVal
};
for (UInt32 aIdx = 0; aIdx < aPropertyCount; ++aIdx)
{
aPropertyPtrs[aIdx]->Seal();
}
// Very simple and unsave data receiption loop.
while (true)
{
UInt32 aPropertyIdToSet = ReadU32(); // The property id to receive.
UInt32 aNewValHasValue = ReadU32(); // Wether the value is defined
UInt32 aNewValValue = ReadU32(); // The value
UInt32n aNewVal // Nullable for NewValue
= aNewValHasValue // if value is defined
? UInt32n(aNewValValue) // Create a nullable which has a value
: UInt32n() // Create a nullable which has no value
;
CValue* aValuePtr = aPropertyPtrs[aPropertyIdToSet]; // Get the value to receive.
aValuePtr->SetTargetValue(aNewVal); // Set the value to the static model
Process(aPropertyPtrs); // Process data newly received.
}
}

Call Method from QT slot in static method

I have a static method which shows a list (Items) and return an array of selected object in Items.
I would like to add a checkbox to reload the list along some parameters. We are using QT 3.3.
//// Static
int CMSUI_InputDialog::FittingList(QWidget* parent,
const CString& Title,
const CStringArray& Items,
bool IsMultiSelect,
int DefaultItem,
bool OkIsDefault,
CArray<int, int>& Selecteds)
{
int ret = 0;
Selecteds.RemoveAll();
/// Create grid
QDialog dialog(parent, 0, true);
dialog.setCaption(QString(Title));
QGridLayout* pLayoutGrid = Init(&dialog, OkIsDefault);
//Create checkBox
QCheckBox* pCheckBox = new QCheckBox(&dialog, "m_pApply_Filter");
pCheckBox->setText("Norm Filter");
pLayoutGrid->addWidget(pCheckBox, 0, 0);
QObject::connect(pCheckBox, SIGNAL(stateChanged(int)), &dialog, SLOT(checkboxClicked()));
/// Create ListBox
QListBox* pList = new QListBox(&dialog);
pList->setMinimumSize(QSize(370, 90));
pList->setSelectionMode(QListBox::Extended);
// Load the list
int Count = Items.GetSize();
for (int i = 0; i < Count; i++)
{
QString QS(Items[i]);
pList->insertItem(QS);
}
if (DefaultItem >= 0 && DefaultItem < Count)
pList->setSelected(DefaultItem, true);
pLayoutGrid->addWidget(pList, 1, 0);
// Connect double clic on QDialog accept
QObject::connect(pList, SIGNAL(doubleClicked(QListBoxItem*)), &dialog, SLOT(accept()));
if (dialog.exec() == QDialog::Accepted)
{
for (int i = 0; i < Count; i++)
{
if (pList->isSelected(i))
Selecteds.Add(i);
}
ret = 1;
}
return ret;
}
void CMSUI_InputDialog::checkboxClicked()
{
//To do
}
checkboxClicked() is declared in CMSUI_InputDialog.h as slot
class CMSUI_InputDialog
{
protected :
static QGridLayout* Init(QWidget* pParent, bool OkIsDefault);
public slots:
void checkboxClicked();
public:
/// CheckBox + ListBox for fittings
static int FittingList(QWidget* parent,
const CString& Title,
const CStringArray& Items,
bool IsMultiSelect,
int DefaultItem,
bool OkIsDefault,
CArray<int, int>& Selecteds);
};
I tried many things but I'm stupid with QT and didn't got any success to catch the action on the checkbox
There's little Qt-specific here as far as the primary problem goes: it's all understandable in plain C++, no need for Qt knowledge.
You're not checking the result returned by the connect method: it returns false when it fails. The connect fails in your case, so there's no point in going any further than that. The slot will not be called. The reason for that: dialog is just a QDialog, not CMSUI_InputDialog. You can't add any slots to an existing Qt class.
Slots must be methods in QObjects. The class where you added the "slot" is not derived from QObject. You'd need to create such a class (to replace the QDialog that you're using).
slots is a macro that is empty (expands to nothing) and thus has no effect on the compilation. It has a purpose, though: the moc (meta object compiler) that processes the header file will notice that macro and process the subsequent methods as slots. It won't do that unless you also have the Q_OBJECT macro within the class.
First, you'd want to factor out the options for that dialog into a FittingOptions struct, to make the code manageable.
struct FittingOptions {
const CStringArray& items,
bool isMultiSelect,
int defaultItem,
bool okIsDefault,
};
The dialog should become a new class, where your slot would go:
class FittingList : public QDialog {
Q_OBJECT
FittingOptions opt;
CArray<int, int>* selecteds;
public:
FittingList(QWidget *parent, const FittingOptions &options) :
QDialog(parent), opt(options), selecteds(0)
{
// TODO: initialization code that creates widgets etc.
}
void setSelecteds(CArray<int, int> &selecteds)
{
this->selecteds = &selecteds;
}
// ...
public slots:
void checkboxChanged() { /* TODO */ }
};
And then, put the initialization code from the FittingList method into the constructor. Change that method into:
int CMSUI_InputDialog::FittingList(QWidget* parent,
const CString& title,
const CStringArray& items,
bool isMultiSelect,
int defaultItem,
bool okIsDefault,
CArray<int, int>& selecteds)
{
const FittingOptions options = {
items, isMultiSelect, defaultItem, okIsDefault
};
::FittingList dialog(parent, options);
dialog.setCaption(QString(title));
dialog.setSelecteds(selecteds);
if (dialog.exec() != QDialog::Accepted) return 0;
return 1;
}
Look at other examples within your project to see how they went about such problems. This code is quite stale by today's standards, but it's a maintenance job as far as I understand, so you got to do more of the same - since I imagine you're not upgrading the Qt version.
Note: this is obviously something that concerns the original author(s) of the code, not you. You haven't written this stuff. Even in Qt 3's times this code would have been considered crusty - the seemingly pervasive use of globals/singletons is cringe-worthy. I always wonder why people who work on such presumably large scale projects won't look into the code they paid for and is available to them: Qt 3 includes full source code to Qt Designer, and that's where one might have looked for inspiration/tutoring. These days, Qt Creator is a much bigger code base than Designer (by more than an order of magnitude) and is fairly decently architected, so large-scale projects might take inspiration from there.

C++ Builder derived TComboBox to have Items by default

A VCL component newbie here, so pardon me if this is a stupid question...
I'm trying to make a TComboBox component with default items in it upon dropped onto the form, i.e. a TMonthComboBox that will have the list of months in its Items list, when dropped on the form.
I've found that trying to access the Items property during Construction will result in a "Control '' has no parent window" error if I try to drop such combobox on the form.
here is (part of) the constructor:
__fastcall TMonthCombo::TMonthCombo(TComponent *Owner):TComboBox(Owner)
{
this->Style = csDropDownList; // this is okay
this->Items->Add("January"); // This is causing problem
}
I figured that the problem is stemming from the fact that the Items property is not available yet at this point of the construction.
Is there anyway to make sure the component is ready to accept values into its Items property, within the component source code itself (i.e. not adding the list items in the Properties Editor at design time)?
Before anyone tells me to "Just add the items in your Application code at run time", I have to explain that this ComboBox will be used quite frequently in many places, and Month selection is just a simple example I used to explain the problem, the actual values I want to put in the ComboBox is much more varied and most of the time, dynamic in nature. It also have to response to the user selection in varied ways.
I have tried the run-time way, but it's getting very tedious. That's why I'm making it into a component, so that it will handle itself without me having to repeatedly input multiple versions of codes just to populate the ComboBoxes.
Thanks for any help.
Edit: After tried manlio's solution, the ComboBox has an odd look in run-time:
The ComboBox has double image at run time. What have I done wrong?
__fastcall TYearCombo::TYearCombo(TComponent* Owner) : TComboBox(Owner), init_items(true)
{
}
//---------------------------------------------------------------------------
void __fastcall TYearCombo::CreateWnd()
{
unsigned short yr, mn, dy;
this->Width = 90;
this->Style = csDropDownList;
this->DropDownCount = 11;
TDate d = Today();
d.DecodeDate(&yr, &mn, &dy);
year = yr;
if (init_items)
{
init_items = false;
TComboBox::CreateWnd();
Items->BeginUpdate();
for(int i=year-5; i<=year+5; i++)
{
Items->Add(IntToStr(i));
}
this->ItemIndex = 5;
Items->EndUpdate();
}
}
//---------------------------------------------------------------------------
You can try this:
Override the CreateWnd virtual method and add a init_items private data member:
class TMonthCombo : public TComboBox
{
// ...
protected:
virtual void __fastcall CreateWnd();
private:
bool init_items;
// ...
};
Set the init_items flag:
TMonthCombo::TMonthCombo(TComponent *Owner) : TComboBox(Owner),
init_items(true)
{
// ...
}
Inside CreateWnd you can add new items:
void __fastcall TMonthCombo::CreateWnd()
{
TComboBox::CreateWnd();
if (init_items)
{
init_items = false;
Items->BeginUpdate();
Items->Add("January");
// ...
Items->EndUpdate();
}
}
Further notes:
"Control has no parent" in Create ComboBox (TComboBox requires an allocated HWND in order to store strings in its Items property).
simply cast the constructor's Owner parameter to TWinControl and assign the result to the component's Parent property isn't a solution:
TMonthCombo::TMonthCombo(TComponent *Owner) : TComBoBox(Owner)
{
Parent = static_cast<TWinControl *>(Owner);
// ...
}
the assignment solves the "Control has no parent window error" but creates another problem: the form is always the component's parent (you cannot add the form to a different container).

Best way to structure classes in C++

For a long time I have been thinking about following aspect of classes structure. Let's see we have Style class which stores font size, font color and other font-style settings. We have also a Font class.
And now we have two approaches for describing our tasks. The first one is:
class Style {
public:
unsigned short size;
unsigned short color; // just for example
};
class Font{
private:
Style style;
public:
void setSize( unsigned short fontSize ) {
this->style.size = fontSize;
}
void setColor( unsigned short fontColor ) {
this->style.color = fontColor;
}
void setStyle( Style style ) {
this->style = style;
}
};
The second one is:
class Style {
private:
unsigned short size;
unsigned short color; // just for example
public:
void setSize( unsigned short fontSize ) {
this->style.size = fontSize;
}
void setColor( unsigned short fontColor ) {
this->style.color = fontColor;
}
};
class Font{
private:
Style style;
public:
void setStyle( Style style ) {
this->style = style;
}
};
I use Style-object very often in my app:
Style style;
style.size = 10;
style.color = 02034023; // doesn't matter :)
font.setStyle( style );
So if we define setColor, setFont and other voids in Style class we have loaded them all in our memory (by each copy of Style-object). If we define setColor and others in Font class we have just one copy of setColor loaded in a memory. As I use creating of Style object very often I don't want to load setColor and others in a memory just to have an opportunity to use this class something like this: style.setSize( 10 );. Using such technique I load only one copy of setSize and others in a memory.
What do you think about it? Which structures do you use and why?
Option 1
Pros: Partial encapsulation of the Style object
Cons: You have to provide a method in Font for each field in Style. This may work for simple application like this, but imagine you would have Paragraph class that would have its Font and Content fields (or maybe more). Then you would have to either use option 2 or rewrite all the methods from Font again.
Option 2
Pros: You can fully access the Style object from any level of hierarchy
Cons: You have to instantiate a new object every time you want to change something. Problems with object lifetime/ownership coming in. (Do I delete the style here? Is it referenced somewhere else?). Style object has no encapsulation.
My proposal
Use const-correctness with getters:
class Style
{
public:
unsigned short size;
unsigned short color; // just for example
};
class Font
{
private:
Style style;
public:
const Style& GetStyle() const { return style; }
Style& GetStyle() { return style; }
};
This way you clearly state that the ownership of Style object belongs to Font while also enabling it to be read/changed in given places, i.e.
font.GetStyle().size = 14;
Also you can use function signatures to clearly state hat happens with the Font inside.
void AccessFont(const Font& font)
{
unsigned size = font.GetStyle().size; // works
font.GetStyle().size = 16; // doesn't compile
}
And again, if you came to a hierarchy with Paragraph class as mentioned, you would just add the two getters for Font field. You can then pass around the const and non-const version of Paragraph.
I think either is fine.
If you are going to go with Option 1, I suggest you don't provide a setStyle method so the Style is fully encapsulated. That way, clients of Font don't even need to know that Font contains a Style object. Changes in Style only impact Font and you can even change the implementation of Font to not use a Style object in the future without breaking clients.
If Style is unlikely to change and is just a small object that you are going to pass-by-value then I think Option 2 is fine too.

Inheritance and pointer arrays

If I have a class that inherit from 2 different classes and I want to put the new class into an array. The array hold pointers to only one of the inherit classes. Will that array overflow or will the pointer-array only “contain” the part of the class that inherit from the class the array is made for?
Example:
I have a class called 'screens' it has 3 arrays of pointers to hold reference to other objects.
class Screens
{
protected:
//Edit these to fit the amount of elements used to save space :-) Then we don't need to use dynamic (8 bit MCU :-()
#define NUMBER_OF_BUTTONS 10
#define NUMBER_OF_GRAPHS 10
#define NUMBER_OF_TXTS 10
UTFT *scr;
UTouch *touch;
ButtonTft *buttons[NUMBER_OF_BUTTONS];
GraphTft *graphs[NUMBER_OF_GRAPHS];
TFT_printer *texts[NUMBER_OF_TXTS];
uint8_t ellementsButtons;
uint8_t ellementsGraphs;
uint8_t ellementsTexts;
uint8_t addButIndex;
uint8_t addGrafIndex;
uint8_t addTxtIndex;
public:
Screens(UTFT *_screen);
void addButton(ButtonTft *but);
void addGraphs(GraphTft *graf);
void addText(TFT_printer *txt);
void printScreen(uint8_t textToPrint = ALL_TXT, graphIDs_e graphToDraw = ALL_GRAPS);
void printButton(uint8_t index,bool presed = false);
void printButton(buttonIDs_e buttonID,bool presed = false);
void printGraph(graphIDs_e graphMode = ALL_GRAPS,bool whatToClear = WHOLE_GRAPH);
void printTxt(uint8_t txtMode = ALL_TXT);
ButtonTft* getButton(buttonIDs_e buttonID);
uint8_t getButtonIndex(buttonIDs_e buttonID);
ButtonTft* getPressedButton(uint8_t x,uint8_t y);
GraphTft* getGraph(graphIDs_e graphID);
GraphTft* getPressedGraph(uint8_t x, uint8_t y);
`};
Then I have another class called “button” that is a button on a screen.
#define PRESSED true
class ButtonTft
{
public:
ButtonTft(UTFT *TFTdisplay, int xTopL,int yTopL, int xButR, int yButR,buttonIDs_e buttonID);
~ButtonTft();
void drawButton(bool pressed = false);
void setPos(int x, int y);//This is never used... remove?
void text(char *txt);
bool isPressed(uint16_t x, uint16_t y);
buttonIDs_e getButtonID();
protected:
buttonIDs_e _buttonID;
uint16_t xTopLeft;
uint16_t yTopLeft;
uint16_t xButRight;
uint16_t yBotRight;
char *butonText;
uint16_t color;
UTFT *scr;
};
Buttons are added to the screen likes this:
/*==============================================================================
| Function Name: addButton
| Description: Adds a button element to the screen.
| Input: A pointer to the button element
| Return: -
*=============================================================================*/
void Screens::addButton(ButtonTft *but)
{
buttons[addButIndex++] = but;//Add the element to the array
if (addButIndex == NUMBER_OF_BUTTONS+1) addButIndex = 0;//Should not do this. Avoid adding more than 10! But Safety first!
if(ellementsButtons++ == NUMBER_OF_BUTTONS+1) ellementsButtons = NUMBER_OF_BUTTONS;//Safety first!
}
And the function is called with a reference to a button object:
module5.addButton(&backButton);
Now I want to make a new class called 'modules', it inherits from both 'screens' and 'button'. My idea was that the module IS a button that HAS a screen with it.
class Modules : public Screens , public ButtonTft
{
public:
Modules(UTFT *_display, int xTopL,int yTopL, int xButR, int yButR, buttonIDs_e buttonID);
bool isConected;
protected:
};
I pass the constructor arguments to the inherited class like this:
Modules::Modules(UTFT *_display, int xTopL,int yTopL, int xButR, int yButR, buttonIDs_e buttonID) : Screens (_display) , ButtonTft (_display, xTopL, yTopL, xButR, yButR, buttonID)
{
isConected = 0;
}
I would like to collect all the modules into a screen object, which means, I would like to put the modules into the button-pointer-array in the 'screen'-object. (Since the modules inherit from the buttons.) I would also like to add other buttons to the 'module'-object as it also inherit from the screen-class.
Modules module5(&display,10,75,100,100,MODULE_5);
module5.addButton(&backButton);
mainScreen.addButton(&module5);
If I add new objects (like new buttons) to my module-object and then add that object to a screen, will the button-reference-array in the screen class overflow?
Thanks for the help.
I see no reason why Modules should inherit from ButtonTft. That would add unnecessary data to Modules. If you need to access protected members of ButtonTft from Modules (which would be bad programming), then make it a friend. The array will not overflow unless you put too many elements into it.
If I understood what you are asking, then no. The arrays only hold pointers, which are of fixed size. It should work fine, although it seems like a convoluted way to do things.