iKebab897
iKebab897

Reputation: 83

Closing Popup and setting button label

I'm writing a C++ wxWidgets calculator application. I want to compress trigonometric function buttons into a single one to save on space, using what's basically a split button. If you left click on it, the current option is used. If you right click, a popup menu is opened, which contains all the buttons; when you click on one of them, it is used and the big button changes.

I've been suggested to use wxComboBox and other stuff for this job, but I preferred using wxPopupTransientWindow because this way I can display the buttons in a grid, making everything - in my opinion - neater.

Problem is, when I choose an option from the menu, the main button's ID changes (because when I reopen the menu the previously clicked button is light up as its ID and the big button's ID match), but the label does not. Furthermore, the popup is supposed to close itself when you left click on one of the buttons, but it does not.

My app

This is the code for the custom button in the popup which is supposed to do all that stuff:

void expandButton::mouseReleased(wxMouseEvent& evt)
{
    if (pressed) {
        pressed = false;
        paintNow();

        wxWindow* mBtn = this->GetParent()->GetParent();
        mBtn->SetLabel(this->GetLabel());
        mBtn->SetId(this->GetId());

        this->GetParent()->Close(true);
    }
}

This is the code for the custom button in the main frame which opens up the popup (temporary setup just to test if the whole thing is working):

void ikeButton::rightClick(wxMouseEvent& evt) // CREA PANNELLO ESTENSIONE
{
    if (flags & EXPANDABLE)
    {
        std::vector<expandMenuInfo> buttons;
        buttons.push_back(expandMenuInfo(L"sin", 3001));
        buttons.push_back(expandMenuInfo(L"cos", 3002));
        buttons.push_back(expandMenuInfo(L"tan", 3003));
        buttons.push_back(expandMenuInfo(L"arcsin", 3004));
        buttons.push_back(expandMenuInfo(L"arccos", 3005));
        buttons.push_back(expandMenuInfo(L"arctan", 3006));

        wxPoint p = this->GetScreenPosition();
        size_t sz = this->GetSize().GetHeight() / 1.15;

        expandMenu* menu = new expandMenu(this, buttons, sz, wxPoint(
                p.x, p.y + this->GetSize().GetHeight() + 2));
        menu->SetPosition(wxPoint(
            menu->GetPosition().x - ((menu->GetSize().GetWidth() - this->GetSize().GetWidth()) / 2),
            menu->GetPosition().y));
        menu->Popup();
    }
}

Let me know if I need to show more code. This is probably a terrible way of doing this, but this is basically the first "serious" application I'm creating using the wxWidgets framework. Any help is appreciated.

Upvotes: 0

Views: 236

Answers (1)

New Pagodi
New Pagodi

Reputation: 3554

when I choose an option from the menu, the main button's ID changes (because when I reopen the menu the previously clicked button is light up as its ID and the big button's ID match), but the label does not.

If you're creating the popup menu like in your previous post, you had a popup window with a panel as its child and the buttons were then children of the panel layed out with a sizer.

If that's still the case, this->GetParent() and should be the panel, this->GetParent()->GetParent() should be the popup. So this->GetParent()->GetParent()->GetParent() should be the trig function button (assuming you created the popup with the trig function button as the parent).

So I think the line wxWindow* mBtn = this->GetParent()->GetParent(); should be changed to wxWindow* mBtn = this->GetParent()->GetParent()->GetParent();.

Or slightly shorter wxWindow* mBtn = this->GetGrandParent()->GetParent();;


the popup is supposed to close itself when you left click on one of the buttons, but it does not.

It looks like wxPopupTransientWindow has a special Dismiss method that is supposed to be used to close it.

So I think the line this->GetParent()->Close(true); should be changed to this->GetGrandParent()->Dismiss(); (Assuming as above that the buttons in the popup are children pf a panel).


Alternately, if you want a solution that will work regardless of the parentage of the controls in the popup window, you could have a utility function to find the popup ancestor which would look something like this:

wxPopupTransientWindow* FindPopup(wxWindow* w)
{
    wxPopupTransientWindow* popup = NULL;

    while ( w != NULL )
    {
        popup = wxDynamicCast(w, wxPopupTransientWindow);

        if ( popup )
        {
            break;
        }

        w = w->GetParent();
    }

    return popup;
}

This uses the wxDynamicCast function which is slightly different from the c++ dynamic_cast expression. wxDynamicCast uses wxWidgets' RTTI system to check if the given pointer can be converted to the given type.

Then the mouseReleased method could use this utility function something like this:

void expandButton::mouseReleased(wxMouseEvent& evt)
{
    if (pressed) {
        pressed = false;
        paintNow();

        wxPopupTransientWindow* popup = FindPopup(this);
        if (popup ) {
            wxWindow* mBtn = popup->GetParent();
            mBtn->SetLabel(this->GetLabel());
            mBtn->SetId(this->GetId());
            mBtn->Refresh();

            popup->Dismiss();
        }
    }
}

I'm not sure why you're setting trig function button to have a new id, but I assume you have a reason.


To make the SetLabel method work in your custom button class, I think the easist thing is to call the SetLabel() method in the button's constructor. This will store the string passed to the constructor in the button's internal label member.

Based on other questions, I think the ikeButton constructor looks something like this:

ikeButton::ikeButton(wxFrame* parent, wxWindowID id, wxString text,...
{
...
    this->text = text;

To store the label, you would need to change the line this->text = text; to

    SetLabel(text);

And when you draw the button, I think the method you use looks like this:

void ikeButton::render(wxDC& dc)
{
   ...
    dc.DrawText(text, ...);
}

You would need to change, the line dc.DrawText(text, ...); to

    dc.DrawText(GetLabel(), ...);

Likewise, any other references to the button's text member should be changed to GetLabel() instead.

Finally, when you set the label in the expandButton::mouseReleased method, it might be a good idea to call button's Refresh() method to force the button to redraw itself. I added that my suggestion for the mouseReleased method above.

Upvotes: 1

Related Questions