Closed wimstockman closed 1 year ago
Hello Wim, you cannot create an object/widget in initLayout()
. The initLayout()
method is called dynamically, and you do not influence on when and how often. I added some logging entries (std::clog ...
) to your application to document the function calls.
#include <final/final.h>
#include <fstream>
using finalcut::FPoint;
using finalcut::FSize;
using std::endl;
using std::ofstream;
using std::string;
using namespace finalcut;
class SubForm : public FScrollView
{
public:
class row : public FWidget
{
public:
explicit row (FWidget* parent) : FWidget{parent}
{
vField.push_back(&ArtikelCode);
vField.push_back(&Aantal);
vField.push_back(&StukPrijs);
vField.push_back(&TotPrijs);
initLayout();
}
FLineEdit ArtikelCode{this};
FLineEdit Aantal{this};
FLineEdit StukPrijs{this};
FLineEdit TotPrijs{this};
int RowId;
std::vector<FLineEdit*> vField;
void initLayout() override
{
std::clog << "row::initLayout()\n";
ArtikelCode.setGeometry(FPoint{1, 1}, FSize{20, 3});
ArtikelCode.unsetShadow();
Aantal.setGeometry(FPoint{24, 1},FSize{10, 3});
Aantal.unsetShadow();
StukPrijs.setGeometry(FPoint{36, 1},FSize{10, 3});
StukPrijs.unsetShadow();
TotPrijs.setGeometry(FPoint{48, 1},FSize{10, 3});
TotPrijs.unsetShadow();
FWidget::initLayout();
}
};
explicit SubForm (FWidget* parent)
: FScrollView{parent}
{ }
explicit SubForm (const FString& txt, FWidget* parent)
: FScrollView{parent}
{ }
std::vector<row*> vRow;
std::vector<std::string> vCaption{"ArtikelCode", "Aantal", "E.Prijs", "TotPrijs"};
unsigned int InitLines{5};
void initLayout() override
{
std::clog << "SubForm::initLayout()\n";
int posy = 2;
int posx = 1;
for (unsigned int i = 0; i < InitLines; i++)
{
posy += 2;
row* r = new row{this};
this->vRow.push_back(r);
r->setGeometry(FPoint{1, posy}, FSize{100, 10});
// First Row add Caption above the Fields
if ( i == 0 )
{
for (long unsigned int j = 0; j < vCaption.size(); j++)
{
posx = r->vField[j]->getPos().getX();
long unsigned int width = r->vField[j]->getSize().getWidth();
FLabel* l = new FLabel{this};
std::clog << "Create new Label \"" << vCaption[j] << "\"\n";
if ( j == 3 )
{
l->setBackgroundColor(FColor::Yellow);
}
l->setGeometry(FPoint{posx, 1 + 8 + int(j)}, FSize{width, 1});
l->setText(vCaption[j]);
l->setAlignment(Align::Center);
if ( j == 3 )
{
std::clog << "pos(x y) = (" << l->getPos() << ");"
<< " text = \"" << l->getText() << "\"\n";
}
posy -= 1;
}
}
FScrollView::initLayout();
}
this->setText("Detail");
this->setPos(FPoint{1, 2});
this->setSize(FSize{80, 30});
this->setScrollSize(FSize{70, 100});
this->setColor(FColor::Blue,FColor::White);
this->clearArea();
}
};
class Form : public FDialog
{
public:
explicit Form (FWidget *parent) : FDialog{parent}
{ }
SubForm S{"Detail", this};
void initLayout() override
{
std::clog << "Form::initLayout()\n";
S.setGeometry(FPoint{5, 5}, FSize{50, 15});
FDialog::initLayout();
}
};
int main (int argc, char* argv[])
{
// Create the application object
finalcut::FApplication app{argc, argv};
app.initTerminal();
Form dgl{&app};
dgl.setText("SubForm");
dgl.setGeometry(FPoint{1, 1}, FSize{100, 50});
dgl.redraw();
// Set dialog object as main widget
FWidget::setMainWidget(&dgl);
// Show and start the application
dgl.show();
return app.exec();
}
To prevent the log lines from being written to the terminal, you have to start your program with ./yourapp --log-file=/tmp/fc.log
and watch the output live in a second terminal with tail -f /tmp/fc.log
.
You will see the following output:
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Form::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] SubForm::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "ArtikelCode"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "Aantal"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "E.Prijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "TotPrijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] pos(x y) = (39 12); text = "TotPrijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] SubForm::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "ArtikelCode"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "Aantal"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "E.Prijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] Create new Label "TotPrijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] pos(x y) = (48 12); text = "TotPrijs"
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
Thu, 22 Dec 2022 23:40:28 +0100 [INFO] row::initLayout()
The row::initLayout()
method is called twice! Thus, you create 8 instead of 4 FLabel
widgets. The 4th input field is not initialized when SubForm::initLayout()
is first called. It returns an x value of 39. On the second call, it returns the initialized x-value 48.
To prevent the labels from overlapping, I increased the y-value by one line at a time:
Hi , Thanks for looking at it. I moved the creation of the widgets out of the initLayout and now it does what it should do. Thanks for the std::clog example. Maybe we should put it in the Wiki . I was debugging with gdb , but this is way easier.
Here is what I come so far to easily create a subform on a form.
#include <final/final.h>
#include <fstream>
using finalcut::FPoint;
using finalcut::FSize;
using std::endl; using std::ofstream; using std::string;
using namespace finalcut;
class SubForm : public FScrollView
{
public:
//Helper Struct to create the LineEdits
struct Field
{
long unsigned int width;
bool numeric;
Align align;
};
//Row Class to manage the rows of the SubForm
class Row : public FWidget{
public:
explicit Row (FWidget* parent) : FWidget{parent}
{
}
int RowId;
std::vector<FLineEdit*> vField;
void initLayout() override
{
int px=1;
for(unsigned int i=0;i<vField.size();i++)
{
vField[i]->setPos(FPoint{px,1});
px+=vField[i]->getWidth()+2;
vField[i]->unsetShadow();
}
}
void makeFields(std::vector<Field*> vF) {
for(unsigned int i=0;i<vF.size();i++)
{
FLineEdit* f = new FLineEdit(this);
f->setSize(FSize{vF[i]->width,1});
f->setAlignment(vF[i]->align);
if (vF[i]->numeric == 1) {f->setInputFilter("[.0-9]");}
vField.push_back(f);
}
}
void onChildFocusOut (FFocusEvent * out_ev) override
{
const auto focus_Widget = FWidget::getFocusWidget();
if ( out_ev->getFocusType() == FocusTypes::NextWidget )
{
const auto& last_widget = getLastFocusableWidget(getChildren());
if ( focus_Widget == last_widget )
{
out_ev->accept();
emitCallback("end-of-row");
}
}
else if ( out_ev->getFocusType() == FocusTypes::PreviousWidget )
{
const auto& first_widget = getFirstFocusableWidget(getChildren());
if ( focus_Widget == first_widget )
{
out_ev->accept();
focusPrevChild();
}
}
}
};// End of Row Class
std::vector<Row*> vRow;
std::vector<FLabel*> vLabel;
std::vector<std::string> vCaption;
std::vector<Field*> vField;
unsigned int InitLines{5};
explicit SubForm (FWidget* parent) : FScrollView{parent} { init(); }
explicit SubForm (const FString& txt, FWidget* parent) : FScrollView{parent} { init(); }
void createField(std::string FieldCaption,long unsigned int width ,bool numeric=false , Align align=Align::Left)
{
Field* F = new Field;
F->width = width;
F->align = align;
vField.push_back(F);
vCaption.push_back(FieldCaption);
FLabel* l = new FLabel(FieldCaption,this);
vLabel.push_back(l);
}
void createRow()
{
Row* r = new Row{this};
r->setGeometry(FPoint{1,posy},FSize{100,1});
r->makeFields(vField);
r->addCallback("end-of-row",this,&SubForm::cb_rowchange);
this->vRow.push_back(r);
this->posy+=2;
}
void cb_rowchange()
{
auto lastfield = vRow[0]->vField.size()-1;
for (long unsigned int i=0;i<vRow.size();i++)
{
if (vRow[i]->vField[lastfield]->hasFocus())
{
if (i == vRow.size()-1) {(*vRow[0]->vField.begin())->setFocus(); break;}
else {(*vRow[++i]->vField.begin())->setFocus(); break;}
}
}
this->redraw();
}
//Init function creates the dynamic objects
void init()
{
//Create the amount of columns you want to have
// with Column Caption,width ,numeric field or not , alignment
// Defaults for numeric = alphanumeric , default alignment is left
this->createField("ArtikelCode",20,0,Align::Center);
this->createField("Aantal",10,1,Align::Right);
this->createField("E.Prijs",10,1,Align::Right);
this->createField("TotPrijs",10,1,Align::Right);
//Create the amount of rows you want
this->InitLines = 20;
for(unsigned int i=0;i<InitLines;i++){this->createRow();}
}
//InitLayout sets the layout how the objects look -- !!! don't create new objects here !!! ---
void initLayout() override
{
int posx=2;
for(long unsigned int j=0;j<vLabel.size();j++)
{
posx = vRow[0]->vField[j]->getPos().getX();
long unsigned int width = vRow[0]->vField[j]->getSize().getWidth();
vLabel[j]->setGeometry(FPoint{posx,1},FSize{width,1});
vLabel[j]->setText(vCaption[j]);
vLabel[j]->setAlignment(Align::Center);
}
this->setText("Detail");
this->setPos(FPoint{1,2});
this->setSize(FSize{80,30});
this->setScrollSize(FSize{70,100});
this->setColor(FColor::Blue,FColor::White);
this->clearArea();
}
private:
int posy=2;
};//End of SubForm Class
class Form : public FDialog
{
public:
explicit Form (FWidget *parent):FDialog{parent} { }
SubForm S{"Detail",this};
void initLayout() override{
S.setGeometry(FPoint{5,5},FSize{50,15});
}
};
int main (int argc, char* argv[])
{
// Create the application object
finalcut::FApplication app{argc, argv};
app.initTerminal();
Form dgl{&app};
dgl.setText("SubForm");
dgl.setGeometry(FPoint{1,1},FSize{100,50});
dgl.redraw();
// Set dialog object as main widget
FWidget::setMainWidget(&dgl);
// Show and start the application
dgl.show();
return app.exec();
}
Have a nice Christmas Kind Regards, Wim Stockman
Hi , I'm tinkering to create a kind of subform with rows of fields. But when i add labels on top it is like they are printed twice and some letters stay on the canvas.
Here is my code:
Can you spot the problem. Kind Regards, Wim