Call button click function from grandchild - c++

I'm creating my first C++ wxWidgets application. I'm trying to create some kind of split button where the options are displayed in a grid. I have a custom button class which, when right-clicked on, opens a custom wxPopupTransientWindow that contains other buttons.
When I click on the buttons in the popup, I want to simulate a left click on the main button. I'm trying to achieve this through events, but I'm kinda confused.
void expandButton::mouseReleased(wxMouseEvent& evt)
{
if (pressed) {
pressed = false;
paintNow();
wxWindow* mBtn = this->GetGrandParent();
mBtn->SetLabel(this->GetLabel());
mBtn->Refresh();
wxCommandEvent event(wxEVT_BUTTON);
event.SetId(GetId());
event.SetEventObject(mBtn);
mBtn-> //make it process the event somehow?
wxPopupTransientWindow* popup = wxDynamicCast(this->GetParent(), wxPopupTransientWindow);
popup->Dismiss();
}
}
What is the best way to do this?

You should do mBtn->ProcessWindowEvent() which is a shorter synonym for mBtn->GetEventHandler()->ProcessEvent() already mentioned in the comments.
Note that, generally speaking, you're not supposed to create wxEVT_BUTTON events from your own code. In this particular case and with current (and all past) version(s) of wxWidgets it will work, but a cleaner, and guaranteed to also work with the future versions, solution would be define your own custom event and generate it instead.

Related

myComboBox->Dismiss(); not working under wxEVT_TEXT and wxEVT_COMBOBOX_DROPDOWN

My objective:
During wxEVT_COMBOBOX_DROPDOWN load up a choiceDialog and set the choice as combo box value and finally close the combobox using dismiss().
What I tried:
void Class_Scheduler_FNFrame::myComboBoxDropdown(wxCommandEvent& event)
{
SingleChoiceDialog1->ShowModal();
int i = SingleChoiceDialog1->GetSelection();
myComboBox->SetValue(wxString::Format(("%s"), myWxStringArray[i]));
myComboBox->Dismiss();
}
The value got changed but the dismiss(); is not working.
So I tried moving dismiss to wxEVT_TEXT.
void Class_Scheduler_FNFrame::myComboBoxTextUpdated(wxCommandEvent& event)
{
testLabel->SetLabel("This should have worked");
myComboBox->Dismiss();
}
Dismiss() is not working in wxEVT_TEXT as well. But when I type onto the combobox, after triggering wxEVT_COMBOBOX_DROPDOWN, dismiss() is working through wxEVT_TEXT .
Help!
You're trying to do something rather weird: have you ever seen a modal dialog show while the combobox dropdown is opened? I haven't and, generally speaking, there should be only a single modal element shown at each moment.
So why do you have this objective? What is the point of showing the dialog whenever a combobox is opened? It doesn't even seem like you're using it like a combobox at all. Maybe you're actually after some kind of wxFilePickerCtrl-like behaviour, i.e. a small button showing the dialog near a text control? Or maybe you want to use wxComboCtrl instead?
Anyhow, it should be possible to do what you want by postponing showing the dialog using CallAfter(), but I'm almost sure that you actually don't want to do that anyhow.

How to manually show CMFCToolBarComboBoxButton sub-menu?

Standard behaviour for CMFCToolBarComboBoxButton is to have a clickable button plus a drop-down arrow for displaying a submenu. I want to show the submenu independently of where the click was made. How can I do it?
My code to create the button is, more or less, the following (it has been extracted from a larger project, so I apologize for any missing not-too-important piece of code):
// In class declaration:
CMenu m_menu;
CMFCToolBar m_toolbar;
// Where toolbar initialization takes place:
m_menu.CreateMenu();
// ... populate menu
// ID_BUTTON is the ID in the resource file for the toolbar button, 0 is the index for the button icon
CMFCToolBarMenuButton button(ID_BUTTON, m_menu.GetSafeHmenu(), 0);
m_toolbar.ReplaceButton(ID_BUTTON, button);
I've been looking around for awhile and cannot find a related answer.
The solution happened to be very straightforward, just call the OnClick function of the CMFCToolBarComboBoxButton button from its associated ON_COMMAND.
// ... message map
ON_COMMAND(ID_BUTTON, OnToolbarMenuButtonClicked)
// ...
void MyWnd::OnToolbarMenuButtonClicked()
{
const int index = m_toolbar.CommandToIndex(ID_BUTTON);
auto button = (CMFCToolBarComboBoxButton*)m_toolbar.GetButton(index);
button->OnClick(NULL, TRUE);
}
This behaviour is not documented and, contrary to what common sense told me, it doesn't create an infinite recursive call. It seems that the "main" button is still controlled by CMFCToolBarButton, while just the "arrow-button" is controlled by the CMFCToolBarComboBoxButton.
PS: obviously, and out of the scope of the question, the OnToolbarMenuButtonClicked can be used for a very different purpose, such as the default action while the sub-menu contains other less-frequent options.

Qt event when widget goes out of sight

Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
Is that possible to add some code to this two events?
Best if this can be done globally...
Is there a way in Qt to handle situation when any widget of Window goes out of sight. I.e if a widget was in tab control and user have changed active tab, or if user just scrolls and widget goes offscreen, and also when it goes back on screen.
The way the question asked makes one think that the widget show-hide-expose state changes need to be handled:
bool MyWidget::event(QEvent* pEvent)
{
if (pEvent->type() == QEvent::Show)
{
// event "shown"
}
else if (pEvent->type() == QEvent::Hide)
{
// event "hidden"
}
else if (pEvent->type() == QEvent::Expose)
{
// event "exposure changed"
// deal with QExposeEvent and evaluate the exposed region
// QExposeEvent* pExposeEvent = reinterpret_cast<QExposeEvent*>(pEvent);
}
return QWidget::event(pEvent);
}
Best if this can be done globally...
Event filter at the top level widget may solve that. Or you can override event() function for the top level widget but finding what exact widget was affected is another thing.
Refer to QExposeEvent description.

How do you close a QMenu when its QWidgetAction is triggered?

I have a QMenu for which I've created a QColorModel action widget (It's effectively just a QStandardItemModel). My desired behavior is that when a user clicks one of the colors in the model, that the action should trigger, and the menu close. However, it doesn't seem to do that, even when I trigger the action manually.
I've tried manually hiding the menu, but it's a kludge because it won't hide parent menus which th menu may be attached to.
Here's the relevant section of code:
// color menu
m_colorMenu = new QMenu("color", this);
m_colorView = new QColorView(m_colorMenu);
m_colorViewAction = new QWidgetAction(m_colorMenu);
m_colorViewAction->setDefaultWidget(m_colorView);
m_colorView->setModel(new QStandardColorModel);
connect(m_colorView, &QColorView::clicked, [&](QModelIndex index)
{
QColor color = qvariant_cast<QColor>(index.data(Qt::DecorationRole));
if (m_pen.color() != color)
{
m_pen.setColor(color);
drawIcon();
drawColorIcon();
update();
}
//this->hide(); // kludge, didn't close all parent menus
m_colorViewAction->trigger(); // doesn't seem to cause menu closure
});
m_colorMenu->addAction(m_colorViewAction);
EDIT
I've also tried adding something to the effect of:
QMenu* menu = m_colorMenu;
do
{
menu->close();
menu = dynamic_cast<QMenu*>(menu->parent());
} while (menu);
but it also is fragile/kludgey because it assumes a) all widgets are properly parented, and b) that all the parents are actually supposed to be menus. In my case, they aren't.
If the containing menus aren't in the parentage tree, and the menu you want to close isn't the top level menu, there is no easy way to do this. That said, there is:
THE NUCLEAR OPTION
Adding this to the end of the lambda function
auto topLevelWidgets = qApp->topLevelWidgets();
for (auto widget : topLevelWidgets)
{
QMenu* menu = dynamic_cast<QMenu*>(widget);
if (menu)
{
menu->close();
}
}
will cause ALL top level menus to close once the action is triggered. This is a relatively OK way to accomplish what you want because:
one of the top level menus will contain the menu in question, and
never say never, but I can't think of a single case where you would have (or want) more than one menu open at a time, so most likely the only open menu tree you would close is the intended one.

Sharing variable among class instances

class MyApp : public CWinApp {
afx_msg OnPrefrences();
};
OnPrefrences() get called when user selects tools->Preference from the menubar.
Now in one dialog(Say DlgX) there is one button, on clicking this I need to open the Preference dialog which has in fact many panes, but here I need to open the Preference dialog by selecting one the these pane as active. Also in that particular pane I need to hide some of the controls only when It gets open through this the dialog not through menu.
So I have created one variable(Say m_varX) in MainFrm class.
void DlgX::OnButtonXClick()
{
CMainFrame* pFrame = (CMainFrame*)AfxGetMainWnd();
if(pFrame)
{
pFrame->m_varX = TRUE;
((CMyApp*)(AfxGetApp()))->OnPrefrences();
pFrame->m_varX = FALSE;
}
}
And in button handler of DlgX I have made this m_varX TRUE and call the OnPreference() and after closing of this preference dialog I have made m_varX FALSE.
All this is working fine... But the problem is that things gets clutter in mainFrm. Also the project I am working on is legacy one so I cant make much changes.
Is there any patter available for handling such case?
Thanks
You could solve this with a custom dialog (if you don't have it already)
When you show the dialog from the main menu i.e. onPreferences() you fill and show all 'panes'. you would have to do a custom dialog where the ctor takes some arguments.
E.g.
enum { all, part };
void MainFrame::OnPreferences()
{
CMyPreferences dlg( GetDocument(), all );
dlg.DoModal();
}
but when you call it from within a dialog you only fill in the parts you need.
void YourDialog::OnPreferences()
{
CMyPreferences dlg( GetDocument(), part );
dlg.doModal();
}
The argument could be something more sophisticated for more fine tuned configuration of what to show/allow to edit.
I think for that special case, even if sometimes is no more considered a pattern, the singleton pattern would work for you.