Sun 18 Feb 2007 01:51:39 AM UTC, original submission:
In the latest version of ParaGU (v 1.1.8) in the bouncing happy faces demo, if you hit the Quit button without ever clicking on the drop down menu, you will crash.
The problem here is that in the PG_DropDown constructor, a new PG_ListBox is created (and assigned to the my_DropList member), but is not given a parent. As a result, the PG_ListBox is added to the global widget list PG_Application::widgetList. In PG_Application::Shutdown(), we walk through the global widget list deleting each widget in turn. First we delete the PG_ListBox and later we delete the PG_DropDown widget. The PG_DropDown destructor also deletes the same PG_ListBox. Boom. (If we click on the PG_DropDown widget, then the global widget list is rearranged with the PG_DropDown widget ahead of the PG_ListBox. And when we call the PG_DropDown destructor, we delete the PG_ListBox causing it to be removed from the global list. So it's only deleted once.)
I've modified the PG_DropDown constructor, destructor, handleButtonClick() and selectHandler() to fix the problem. (At least it fixes the problem in the bouncing happy faces demo.) The new code follows:
PG_DropDown::PG_DropDown(PG_Widget* parent, const PG_Rect& r, int id, const char* style) : PG_Widget(parent, r),
// needed for AddChild() evaluation - H. C.
my_EditBox(NULL), my_DropButton(NULL), my_DropList(NULL)
{
PG_Rect rect(0, 0, r.my_width - r.my_height, r.my_height);
SetID(id);
my_EditBox = new PG_LineEdit(this, rect, style);
my_EditBox->sigEditBegin.connect(sigEditBegin.slot());
my_EditBox->sigEditEnd.connect(sigEditEnd.slot());
my_EditBox->sigEditReturn.connect(sigEditReturn.slot());
PG_Rect rbutton(abs(r.my_width - r.my_height), 0, r.my_height, r.my_height);
my_DropButton = new PG_Button(this, rbutton, NULL, -1, style);
my_DropButton->SetID(IDDROPDOWN_BOX);
my_DropButton->sigClick.connect(slot(*this, &PG_DropDown::handleButtonClick));
PG_Rect rlist(r.my_xpos, r.my_ypos + r.my_height +1, r.my_width, r.my_height /* * 5 */);
/* Old code.
my_DropList = new PG_ListBox(NULL, rlist, style);
*/
my_DropList = new PG_ListBox(this, rlist, style); // New.
my_DropList->SetAutoResize(true, false);
//my_DropList->EnableScrollBar(false);
my_DropList->sigSelectItem.connect(slot(*this, &PG_DropDown::select_handler));
LoadThemeStyle(style);
}
PG_DropDown::~PG_DropDown() {
/* Old code.
delete my_DropList;
*/
}
bool PG_DropDown::handleButtonClick(PG_Button* button) {
if(button->GetID() != IDDROPDOWN_BOX) {
return false;
}
if(my_DropList->IsVisible()) {
Uint16 collapsed_height = Height() - my_DropList->Height(); // New.
my_DropList->Hide();
SizeWidget(Width(), collapsed_height); // New.
} else {
Uint16 expanded_height = Height() + my_DropList->Height(); // New.
my_DropList->MoveRect(my_xpos, my_ypos+my_height);
my_DropList->Show();
SizeWidget(Width(), expanded_height); // New.
}
return true;
}
bool PG_DropDown::select_handler(PG_ListBoxBaseItem* item) {
Uint16 collapsed_height = Height() - my_DropList->Height(); // New.
my_EditBox->SetText(item->GetText());
item->Select(false);
my_DropList->SelectItem(NULL);
my_DropList->Hide();
SizeWidget(Width(), collapsed_height); // New.
eventSelectItem(item);
sigSelectItem(item);
return true;
}
|