Add support for custom radio button controls in wxFileDialog

Update the dialogs sample to show using them too.
This commit is contained in:
Vadim Zeitlin 2022-05-30 23:38:17 +01:00
parent bf5ddc200b
commit 07d7dd19f8
5 changed files with 274 additions and 5 deletions

View File

@ -15,6 +15,7 @@
class wxFileDialogCustomControlImpl;
class wxFileDialogButtonImpl;
class wxFileDialogCheckBoxImpl;
class wxFileDialogRadioButtonImpl;
class wxFileDialogTextCtrlImpl;
class wxFileDialogStaticTextImpl;
class wxFileDialogCustomizeImpl;
@ -90,6 +91,25 @@ private:
wxDECLARE_NO_COPY_CLASS(wxFileDialogCheckBox);
};
// A class representing a custom radio button.
class WXDLLIMPEXP_CORE wxFileDialogRadioButton : public wxFileDialogCustomControl
{
public:
bool GetValue() const;
void SetValue(bool value);
// Ctor is only used by wxWidgets itself.
explicit wxFileDialogRadioButton(wxFileDialogRadioButtonImpl* impl);
protected:
virtual bool OnDynamicBind(wxDynamicEventTableEntry& entry) wxOVERRIDE;
private:
wxFileDialogRadioButtonImpl* GetImpl() const;
wxDECLARE_NO_COPY_CLASS(wxFileDialogRadioButton);
};
// A class representing a custom text control.
class WXDLLIMPEXP_CORE wxFileDialogTextCtrl : public wxFileDialogCustomControl
{
@ -130,6 +150,7 @@ class WXDLLIMPEXP_CORE wxFileDialogCustomize
public:
wxFileDialogButton* AddButton(const wxString& label);
wxFileDialogCheckBox* AddCheckBox(const wxString& label);
wxFileDialogRadioButton* AddRadioButton(const wxString& label);
wxFileDialogTextCtrl* AddTextCtrl(const wxString& label = wxString());
wxFileDialogStaticText* AddStaticText(const wxString& label);

View File

@ -38,6 +38,13 @@ public:
virtual void SetValue(bool value) = 0;
};
class wxFileDialogRadioButtonImpl : public wxFileDialogCustomControlImpl
{
public:
virtual bool GetValue() = 0;
virtual void SetValue(bool value) = 0;
};
class wxFileDialogTextCtrlImpl : public wxFileDialogCustomControlImpl
{
public:
@ -60,6 +67,7 @@ class wxFileDialogCustomizeImpl
public:
virtual wxFileDialogButtonImpl* AddButton(const wxString& label) = 0;
virtual wxFileDialogCheckBoxImpl* AddCheckBox(const wxString& label) = 0;
virtual wxFileDialogRadioButtonImpl* AddRadioButton(const wxString& label) = 0;
virtual wxFileDialogTextCtrlImpl* AddTextCtrl(const wxString& label) = 0;
virtual wxFileDialogStaticTextImpl* AddStaticText(const wxString& label) = 0;

View File

@ -1605,7 +1605,8 @@ public:
MyExtraPanel(wxWindow *parent);
wxString GetInfo() const
{
return wxString::Format("checkbox=%d, text=\"%s\"", m_checked, m_str);
return wxString::Format("paper=%s, enabled=%d, text=\"%s\"",
m_paperSize, m_checked, m_str);
}
private:
@ -1615,6 +1616,16 @@ private:
m_btn->Enable(m_checked);
}
void OnRadioButton(wxCommandEvent& event)
{
if ( event.GetEventObject() == m_radioA4 )
m_paperSize = "A4";
else if ( event.GetEventObject() == m_radioLetter )
m_paperSize = "Letter";
else
m_paperSize = "Unknown";
}
void OnText(wxCommandEvent& event)
{
m_str = event.GetString();
@ -1632,9 +1643,12 @@ private:
wxString m_str;
bool m_checked;
wxString m_paperSize;
wxButton *m_btn;
wxCheckBox *m_cb;
wxRadioButton *m_radioA4;
wxRadioButton *m_radioLetter;
wxStaticText *m_label;
wxTextCtrl *m_text;
};
@ -1648,6 +1662,11 @@ MyExtraPanel::MyExtraPanel(wxWindow *parent)
m_btn->Enable(false);
m_cb = new wxCheckBox(this, -1, "Enable Custom Button");
m_cb->Bind(wxEVT_CHECKBOX, &MyExtraPanel::OnCheckBox, this);
m_radioA4 = new wxRadioButton(this, wxID_ANY, "A4",
wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
m_radioA4->Bind(wxEVT_RADIOBUTTON, &MyExtraPanel::OnRadioButton, this);
m_radioLetter = new wxRadioButton(this, wxID_ANY, "Letter");
m_radioLetter->Bind(wxEVT_RADIOBUTTON, &MyExtraPanel::OnRadioButton, this);
m_label = new wxStaticText(this, wxID_ANY, "Nothing selected");
m_label->Bind(wxEVT_UPDATE_UI, &MyExtraPanel::OnUpdateLabelUI, this);
@ -1660,6 +1679,8 @@ MyExtraPanel::MyExtraPanel(wxWindow *parent)
wxSizerFlags().Centre().Border());
sizerTop->Add(m_text, wxSizerFlags(1).Centre().Border());
sizerTop->AddSpacer(10);
sizerTop->Add(m_radioA4, wxSizerFlags().Centre().Border());
sizerTop->Add(m_radioLetter, wxSizerFlags().Centre().Border());
sizerTop->Add(m_cb, wxSizerFlags().Centre().Border());
sizerTop->AddSpacer(5);
sizerTop->Add(m_btn, wxSizerFlags().Centre().Border());
@ -1695,6 +1716,8 @@ public:
// ShowModal() returns, TransferDataFromCustomControls() is the latest
// moment when they can still be used.
m_text = customizer.AddTextCtrl("Just some extra text:");
m_radioA4 = customizer.AddRadioButton("A4");
m_radioLetter = customizer.AddRadioButton("Letter");
m_cb = customizer.AddCheckBox("Enable Custom Button");
m_cb->Bind(wxEVT_CHECKBOX, &MyCustomizeHook::OnCheckBox, this);
m_btn = customizer.AddButton("Custom Button");
@ -1715,7 +1738,8 @@ public:
// And another one called when the dialog is accepted.
virtual void TransferDataFromCustomControls() wxOVERRIDE
{
m_info.Printf("checkbox=%d, text=\"%s\"",
m_info.Printf("paper=%s, enabled=%d, text=\"%s\"",
m_radioA4->GetValue() ? "A4" : "Letter",
m_cb->GetValue(), m_text->GetValue());
}
@ -1740,6 +1764,8 @@ private:
wxFileDialogButton* m_btn;
wxFileDialogCheckBox* m_cb;
wxFileDialogRadioButton* m_radioA4;
wxFileDialogRadioButton* m_radioLetter;
wxFileDialogTextCtrl* m_text;
wxFileDialogStaticText* m_label;

View File

@ -27,6 +27,7 @@
#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/radiobut.h"
#include "wx/stattext.h"
#include "wx/textctrl.h"
#endif // WX_PRECOMP
@ -143,6 +144,34 @@ void wxFileDialogCheckBox::SetValue(bool value)
GetImpl()->SetValue(value);
}
wxFileDialogRadioButton::wxFileDialogRadioButton(wxFileDialogRadioButtonImpl* impl)
: wxFileDialogCustomControl(impl)
{
}
bool wxFileDialogRadioButton::OnDynamicBind(wxDynamicEventTableEntry& entry)
{
if ( entry.m_eventType == wxEVT_RADIOBUTTON )
return GetImpl()->DoBind(this);
return wxFileDialogCustomControl::OnDynamicBind(entry);
}
wxFileDialogRadioButtonImpl* wxFileDialogRadioButton::GetImpl() const
{
return static_cast<wxFileDialogRadioButtonImpl*>(m_impl);
}
bool wxFileDialogRadioButton::GetValue() const
{
return GetImpl()->GetValue();
}
void wxFileDialogRadioButton::SetValue(bool value)
{
GetImpl()->SetValue(value);
}
wxFileDialogTextCtrl::wxFileDialogTextCtrl(wxFileDialogTextCtrlImpl* impl)
: wxFileDialogCustomControl(impl)
{
@ -222,6 +251,12 @@ wxFileDialogCustomize::AddCheckBox(const wxString& label)
return StoreAndReturn(new wxFileDialogCheckBox(m_impl->AddCheckBox(label)));
}
wxFileDialogRadioButton*
wxFileDialogCustomize::AddRadioButton(const wxString& label)
{
return StoreAndReturn(new wxFileDialogRadioButton(m_impl->AddRadioButton(label)));
}
wxFileDialogTextCtrl*
wxFileDialogCustomize::AddTextCtrl(const wxString& label)
{
@ -374,6 +409,58 @@ private:
wxEvtHandler* m_handler;
};
class RadioButtonImpl : public ControlImplBase<wxFileDialogRadioButtonImpl>
{
public:
RadioButtonImpl(wxWindow* parent, const wxString& label)
: ControlImplBase<wxFileDialogRadioButtonImpl>
(
new wxRadioButton(parent, wxID_ANY, label)
)
{
m_handler = NULL;
}
virtual bool GetValue() wxOVERRIDE
{
return GetRadioButton()->GetValue();
}
virtual void SetValue(bool value) wxOVERRIDE
{
GetRadioButton()->SetValue(value);
}
virtual bool DoBind(wxEvtHandler* handler) wxOVERRIDE
{
if ( !m_handler )
{
m_handler = handler;
m_win->Bind(wxEVT_RADIOBUTTON, &RadioButtonImpl::OnRadioButton, this);
}
return true;
}
private:
wxRadioButton* GetRadioButton() const
{
return static_cast<wxRadioButton*>(m_win);
}
void OnRadioButton(wxCommandEvent& event)
{
// See comments in OnButton() above, they also apply here.
wxCommandEvent eventCopy(event);
eventCopy.SetEventObject(m_handler);
m_handler->ProcessEvent(eventCopy);
}
wxEvtHandler* m_handler;
};
class TextCtrlImpl : public ControlImplBase<wxFileDialogTextCtrlImpl>
{
public:
@ -441,7 +528,8 @@ public:
Panel(wxWindow* parent, wxFileDialogCustomizeHook& customizeHook)
: wxPanel(parent),
wxFileDialogCustomize(this),
m_customizeHook(customizeHook)
m_customizeHook(customizeHook),
m_lastWasRadio(false)
{
// Use a simple horizontal sizer to layout all the controls for now.
wxBoxSizer* const sizer = new wxBoxSizer(wxHORIZONTAL);
@ -467,16 +555,36 @@ public:
// Implement wxFileDialogCustomizeImpl pure virtual methods.
wxFileDialogButtonImpl* AddButton(const wxString& label) wxOVERRIDE
{
m_lastWasRadio = false;
return AddToLayoutAndReturn<ButtonImpl>(label);
}
wxFileDialogCheckBoxImpl* AddCheckBox(const wxString& label) wxOVERRIDE
{
m_lastWasRadio = false;
return AddToLayoutAndReturn<CheckBoxImpl>(label);
}
wxFileDialogRadioButtonImpl* AddRadioButton(const wxString& label) wxOVERRIDE
{
RadioButtonImpl* const impl = AddToLayoutAndReturn<RadioButtonImpl>(label);
if ( !m_lastWasRadio )
{
// Select the first button of a new radio group.
impl->SetValue(true);
m_lastWasRadio = true;
}
return impl;
}
wxFileDialogTextCtrlImpl* AddTextCtrl(const wxString& label) wxOVERRIDE
{
m_lastWasRadio = false;
if ( !label.empty() )
{
AddToLayout(new wxStaticText(this, wxID_ANY, label));
@ -487,6 +595,8 @@ public:
wxFileDialogStaticTextImpl* AddStaticText(const wxString& label) wxOVERRIDE
{
m_lastWasRadio = false;
return AddToLayoutAndReturn<StaticTextImpl>(label);
}
@ -509,6 +619,8 @@ private:
wxFileDialogCustomizeHook& m_customizeHook;
bool m_lastWasRadio;
wxDECLARE_NO_COPY_CLASS(Panel);
};

View File

@ -57,6 +57,7 @@
#include "wx/button.h"
#include "wx/checkbox.h"
#include "wx/radiobut.h"
#include "wx/stattext.h"
#include "wx/textctrl.h"
@ -332,6 +333,47 @@ public:
}
};
class wxFileDialogRadioButtonImplFDC
: public wxFileDialogImplFDC<wxFileDialogRadioButtonImpl>
{
public:
wxFileDialogRadioButtonImplFDC(IFileDialogCustomize* fdc, DWORD id, DWORD item)
: wxFileDialogImplFDC<wxFileDialogRadioButtonImpl>(fdc, id),
m_item(item)
{
}
virtual bool GetValue() wxOVERRIDE
{
DWORD selected = 0;
HRESULT hr = m_fdc->GetSelectedControlItem(m_id, &selected);
if ( FAILED(hr) )
wxLogApiError(wxS("IFileDialogCustomize::GetSelectedControlItem"), hr);
return selected == m_item;
}
virtual void SetValue(bool value) wxOVERRIDE
{
// We can't implement it using the available API and this shouldn't be
// ever needed anyhow.
wxCHECK_RET( value, wxS("clearing radio buttons not supported") );
HRESULT hr = m_fdc->SetSelectedControlItem(m_id, m_item);
if ( FAILED(hr) )
wxLogApiError(wxS("IFileDialogCustomize::SetSelectedControlItem"), hr);
}
virtual bool DoBind(wxEvtHandler* WXUNUSED(handler)) wxOVERRIDE
{
// We don't need to do anything special to get the events here.
return true;
}
private:
const DWORD m_item;
};
class wxFileDialogTextCtrlImplFDC
: public wxFileDialogImplFDC<wxFileDialogTextCtrlImpl>
{
@ -391,7 +433,8 @@ public:
: wxFileDialogCustomize(this)
{
m_lastId =
m_lastAuxId = 0;
m_lastAuxId =
m_radioListId = 0;
}
bool Initialize(IFileDialog* fileDialog)
@ -424,6 +467,8 @@ public:
// Implement wxFileDialogCustomizeImpl pure virtual methods.
wxFileDialogButtonImpl* AddButton(const wxString& label) wxOVERRIDE
{
m_radioListId = 0;
HRESULT hr = m_fdc->AddPushButton(++m_lastId, label.wc_str());
if ( FAILED(hr) )
{
@ -436,6 +481,8 @@ public:
wxFileDialogCheckBoxImpl* AddCheckBox(const wxString& label) wxOVERRIDE
{
m_radioListId = 0;
HRESULT hr = m_fdc->AddCheckButton(++m_lastId, label.wc_str(), FALSE);
if ( FAILED(hr) )
{
@ -446,8 +493,45 @@ public:
return new wxFileDialogCheckBoxImplFDC(m_fdc, m_lastId);
}
wxFileDialogRadioButtonImpl* AddRadioButton(const wxString& label) wxOVERRIDE
{
HRESULT hr;
bool firstButton = false;
if ( !m_radioListId )
{
hr = m_fdc->AddRadioButtonList(--m_lastAuxId);
if ( FAILED(hr) )
{
wxLogApiError(wxS("IFileDialogCustomize::AddRadioButtonList"), hr);
return NULL;
}
m_radioListId = m_lastAuxId;
firstButton = true;
}
hr = m_fdc->AddControlItem(m_radioListId, ++m_lastId, label.wc_str());
if ( FAILED(hr) )
{
wxLogApiError(wxS("IFileDialogCustomize::AddControlItem"), hr);
return NULL;
}
wxFileDialogRadioButtonImplFDC* const
impl = new wxFileDialogRadioButtonImplFDC(m_fdc, m_radioListId, m_lastId);
// Select the first button of a new radio group.
if ( firstButton )
impl->SetValue(true);
return impl;
}
wxFileDialogTextCtrlImpl* AddTextCtrl(const wxString& label) wxOVERRIDE
{
m_radioListId = 0;
HRESULT hr;
if ( !label.empty() )
@ -476,6 +560,8 @@ public:
wxFileDialogStaticTextImpl* AddStaticText(const wxString& label) wxOVERRIDE
{
m_radioListId = 0;
HRESULT hr = m_fdc->AddText(++m_lastId, label.wc_str());
if ( FAILED(hr) )
{
@ -497,6 +583,10 @@ private:
// IDs used for any other controls, they're negative (which means they
// decrement from USHORT_MAX down).
DWORD m_lastAuxId;
// ID of the current radio button list, i.e. the one to which the next call
// to AddRadioButton() would add a radio button. 0 if none.
DWORD m_radioListId;
};
#endif // wxUSE_IFILEOPENDIALOG
@ -638,8 +728,20 @@ public:
wxSTDMETHODIMP
OnItemSelected(IFileDialogCustomize*,
DWORD WXUNUSED(dwIDCtl),
DWORD WXUNUSED(dwIDItem)) wxOVERRIDE
DWORD dwIDItem) wxOVERRIDE
{
// Note that we don't use dwIDCtl here because we use unique item IDs
// for all controls.
if ( wxFileDialogCustomControl* const
control = m_customize.FindControl(dwIDItem) )
{
wxCommandEvent event(wxEVT_RADIOBUTTON, dwIDItem);
event.SetEventObject(control);
event.SetInt(true); // Ensure IsChecked() returns true.
control->SafelyProcessEvent(event);
}
return S_OK;
}