diff --git a/docs/changes.txt b/docs/changes.txt index 676a77fc19..58207bbf06 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -113,6 +113,7 @@ All (GUI): - Allow wxWebView::RunScript() return values (Jose Lorenzo, GSoC 2017). - Allow using fractional pen widths with wxGraphicsContext (Adrien Tétar). - Add support for loading fonts from external files (Arthur Norman). +- Add support for using arbitrary windows as wxStaticBox labels. - Improve wxSVGFileDC to support more of wxDC API (Maarten Bent). - Add support for wxAuiManager and wxAuiPaneInfo to XRC (Andrea Zanellato). - Add support for wxSL_MIN_MAX_LABELS and wxSL_VALUE_LABEL to XRC (ousnius). diff --git a/docs/doxygen/mainpages/const_cpp.h b/docs/doxygen/mainpages/const_cpp.h index 1756d18a82..6114326616 100644 --- a/docs/doxygen/mainpages/const_cpp.h +++ b/docs/doxygen/mainpages/const_cpp.h @@ -194,6 +194,8 @@ Currently the following symbols exist: @itemdef{wxHAS_RAW_KEY_CODES, Defined if raw key codes (see wxKeyEvent::GetRawKeyCode are supported.} @itemdef{wxHAS_REGEX_ADVANCED, Defined if advanced syntax is available in wxRegEx.} @itemdef{wxHAS_TASK_BAR_ICON, Defined if wxTaskBarIcon is available on the current platform.} +@itemdef{wxHAS_WINDOW_LABEL_IN_STATIC_BOX, Defined if wxStaticBox::Create() + overload taking @c wxWindow* instead of the text label is available on the current platform.} @itemdef{wxHAS_MODE_T, Defined when wxWidgets defines @c mode_t typedef for the compilers not providing it. If another library used in a wxWidgets application, such as ACE (http://www.cs.wustl.edu/~schmidt/ACE.html), also diff --git a/include/wx/gtk/statbox.h b/include/wx/gtk/statbox.h index b019740133..64255f242d 100644 --- a/include/wx/gtk/statbox.h +++ b/include/wx/gtk/statbox.h @@ -18,6 +18,7 @@ class WXDLLIMPEXP_CORE wxStaticBox : public wxStaticBoxBase public: wxStaticBox() { + Init(); } wxStaticBox( wxWindow *parent, @@ -28,6 +29,21 @@ public: long style = 0, const wxString &name = wxStaticBoxNameStr ) { + Init(); + + Create( parent, id, label, pos, size, style, name ); + } + + wxStaticBox( wxWindow *parent, + wxWindowID id, + wxWindow* label, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + long style = 0, + const wxString &name = wxStaticBoxNameStr ) + { + Init(); + Create( parent, id, label, pos, size, style, name ); } @@ -37,7 +53,21 @@ public: const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = 0, - const wxString &name = wxStaticBoxNameStr ); + const wxString &name = wxStaticBoxNameStr ) + { + return DoCreate( parent, id, &label, NULL, pos, size, style, name ); + } + + bool Create( wxWindow *parent, + wxWindowID id, + wxWindow* label, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + long style = 0, + const wxString &name = wxStaticBoxNameStr ) + { + return DoCreate( parent, id, NULL, label, pos, size, style, name ); + } virtual void SetLabel( const wxString &label ) wxOVERRIDE; @@ -52,13 +82,39 @@ public: virtual void AddChild( wxWindowBase *child ) wxOVERRIDE; + virtual void WXDestroyWithoutChildren() wxOVERRIDE; + protected: + // Common part of all ctors. + void Init() + { + m_labelWin = NULL; + } + + // Common implementation of both Create() overloads: exactly one of + // labelStr and labelWin parameters must be non-null. + bool DoCreate(wxWindow *parent, + wxWindowID id, + const wxString* labelStr, + wxWindow* labelWin, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name); + virtual bool GTKWidgetNeedsMnemonic() const wxOVERRIDE; virtual void GTKWidgetDoSetMnemonic(GtkWidget* w) wxOVERRIDE; void DoApplyWidgetStyle(GtkRcStyle *style) wxOVERRIDE; + // If non-null, the window used as our label. This window is owned by the + // static box and will be deleted when it is. + wxWindow* m_labelWin; + wxDECLARE_DYNAMIC_CLASS(wxStaticBox); }; +// Indicate that we have the ctor overload taking wxWindow as label. +#define wxHAS_WINDOW_LABEL_IN_STATIC_BOX + #endif // _WX_GTKSTATICBOX_H_ diff --git a/interface/wx/statbox.h b/interface/wx/statbox.h index 46004f20f4..8627255027 100644 --- a/interface/wx/statbox.h +++ b/interface/wx/statbox.h @@ -84,6 +84,42 @@ public: long style = 0, const wxString& name = wxStaticBoxNameStr); + /** + Constructor for a static box using the given window as label. + + This constructor takes a pointer to an arbitrary window (although + usually a wxCheckBox or a wxRadioButton) instead of just the usual text + label and puts this window at the top of the box at the place where the + label would be shown. + + The @a label window must be a non-null, fully created window and will + become a child of this wxStaticBox, i.e. it will be owned by this + control and will be deleted when the wxStaticBox itself is deleted. + + An example of creating a wxStaticBox with window as a label: + @code + void MyFrame::CreateControls() + { + wxPanel* panel = new wxPanel(this); + wxCheckBox* checkbox = new wxCheckBox(panel, wxID_ANY, "Box checkbox"); + wxStaticBox* box = new wxStaticBox(panel, wxID_ANY, checkbox); + ... + } + @endcode + + Currently this constructor is only available in wxGTK, use + @c wxHAS_WINDOW_LABEL_IN_STATIC_BOX to check whether it can be used at + compile-time. + + @since 3.1.1 + */ + wxStaticBox(wxWindow* parent, wxWindowID id, + wxWindow* label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxString& name = wxStaticBoxNameStr); + /** Destructor, destroying the group box. */ @@ -97,5 +133,25 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxStaticBoxNameStr); -}; + /** + Creates the static box with the window as a label. + + This method can only be called for an object created using its default + constructor. + + See the constructor documentation for more details. + + Currently this overload is only available in wxGTK, use + @c wxHAS_WINDOW_LABEL_IN_STATIC_BOX to check whether it can be used at + compile-time. + + @since 3.1.1 + */ + wxStaticBox(wxWindow* parent, wxWindowID id, + wxWindow* label, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = 0, + const wxString& name = wxStaticBoxNameStr); +}; diff --git a/samples/widgets/static.cpp b/samples/widgets/static.cpp index 36827f50ae..5e16c71d89 100644 --- a/samples/widgets/static.cpp +++ b/samples/widgets/static.cpp @@ -116,6 +116,9 @@ public: protected: // event handlers void OnCheckOrRadioBox(wxCommandEvent& event); +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + void OnBoxCheckBox(wxCommandEvent& event); +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX void OnButtonReset(wxCommandEvent& event); void OnButtonBoxText(wxCommandEvent& event); @@ -137,6 +140,9 @@ protected: // the check/radio boxes for styles wxCheckBox *m_chkVert, *m_chkGeneric, +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + *m_chkBoxWithCheck, +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX *m_chkAutoResize, *m_chkEllipsize; @@ -207,6 +213,9 @@ StaticWidgetsPage::StaticWidgetsPage(WidgetsBookCtrl *book, m_chkVert = m_chkAutoResize = m_chkGeneric = +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + m_chkBoxWithCheck = +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX #if wxUSE_MARKUP m_chkGreen = #endif // wxUSE_MARKUP @@ -243,6 +252,9 @@ void StaticWidgetsPage::CreateContent() m_chkGeneric = CreateCheckBoxAndAddToSizer(sizerLeft, "&Generic wxStaticText"); +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + m_chkBoxWithCheck = CreateCheckBoxAndAddToSizer(sizerLeft, "Checkable &box"); +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX m_chkVert = CreateCheckBoxAndAddToSizer(sizerLeft, "&Vertical line"); m_chkAutoResize = CreateCheckBoxAndAddToSizer(sizerLeft, "&Fit to text"); sizerLeft->Add(5, 5, 0, wxGROW | wxALL, 5); // spacer @@ -367,6 +379,9 @@ void StaticWidgetsPage::CreateContent() void StaticWidgetsPage::Reset() { m_chkGeneric->SetValue(false); +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + m_chkBoxWithCheck->SetValue(false); +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX m_chkVert->SetValue(false); m_chkAutoResize->SetValue(true); m_chkEllipsize->SetValue(true); @@ -469,10 +484,28 @@ void StaticWidgetsPage::CreateStatic() flagsText |= align; flagsBox |= align; - wxStaticBox *staticBox = new wxStaticBox(this, wxID_ANY, - m_textBox->GetValue(), - wxDefaultPosition, wxDefaultSize, - flagsBox); + wxStaticBox *staticBox; +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX + if ( m_chkBoxWithCheck->GetValue() ) + { + wxCheckBox* const label = new wxCheckBox(this, wxID_ANY, + m_textBox->GetValue()); + label->Bind(wxEVT_CHECKBOX, &StaticWidgetsPage::OnBoxCheckBox, this); + + staticBox = new wxStaticBox(this, wxID_ANY, + label, + wxDefaultPosition, wxDefaultSize, + flagsBox); + } + else // normal static box +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX + { + staticBox = new wxStaticBox(this, wxID_ANY, + m_textBox->GetValue(), + wxDefaultPosition, wxDefaultSize, + flagsBox); + } + m_sizerStatBox = new wxStaticBoxSizer(staticBox, isVert ? wxHORIZONTAL : wxVERTICAL); @@ -559,6 +592,14 @@ void StaticWidgetsPage::OnCheckOrRadioBox(wxCommandEvent& event) CreateStatic(); } +#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX +void StaticWidgetsPage::OnBoxCheckBox(wxCommandEvent& event) +{ + wxLogMessage("Box check box has been %schecked", + event.IsChecked() ? "": "un"); +} +#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX + void StaticWidgetsPage::OnButtonBoxText(wxCommandEvent& WXUNUSED(event)) { m_sizerStatBox->GetStaticBox()->SetLabel(m_textBox->GetValue()); diff --git a/src/gtk/statbox.cpp b/src/gtk/statbox.cpp index 1e4a724497..0d144e128b 100644 --- a/src/gtk/statbox.cpp +++ b/src/gtk/statbox.cpp @@ -59,13 +59,14 @@ static gboolean expose_event(GtkWidget* widget, GdkEventExpose*, wxWindow*) // wxStaticBox //----------------------------------------------------------------------------- -bool wxStaticBox::Create( wxWindow *parent, - wxWindowID id, - const wxString& label, - const wxPoint& pos, - const wxSize& size, - long style, - const wxString& name ) +bool wxStaticBox::DoCreate(wxWindow *parent, + wxWindowID id, + const wxString* labelStr, + wxWindow* labelWin, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) { if (!PreCreation( parent, pos, size ) || !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name )) @@ -74,11 +75,41 @@ bool wxStaticBox::Create( wxWindow *parent, return false; } - m_widget = GTKCreateFrame(label); - g_object_ref(m_widget); + if ( labelStr ) + { + m_widget = GTKCreateFrame(*labelStr); - // only base SetLabel needs to be called after GTKCreateFrame - wxControl::SetLabel(label); + // only base SetLabel needs to be called after GTKCreateFrame + wxControl::SetLabel(*labelStr); + } + else // Use the given window as the label. + { + wxCHECK_MSG( labelWin, false, wxS("Label window can't be null") ); + + GtkWidget* const labelWidget = labelWin->m_widget; + wxCHECK_MSG( labelWidget, false, wxS("Label window must be created") ); + + // The widget must not have any parent at GTK+ level or setting it as + // label widget would fail. + GtkWidget* const oldParent = gtk_widget_get_parent(labelWidget); + gtk_container_remove(GTK_CONTAINER(oldParent), labelWidget); + gtk_widget_unparent(labelWidget); + + // It also should be our child at wx API level, but without being our + // child in wxGTK, i.e. it must not be added to the GtkFrame container, + // so we can't call Reparent() here (not even wxWindowBase version, as + // it still would end up in our overridden AddChild()), nor the normal + // AddChild() for the same reason. + labelWin->GetParent()->RemoveChild(labelWin); + wxWindowBase::AddChild(labelWin); + + m_labelWin = labelWin; + + m_widget = gtk_frame_new(NULL); + gtk_frame_set_label_widget(GTK_FRAME(m_widget), labelWidget); + } + + g_object_ref(m_widget); m_parent->DoAddChild( this ); @@ -121,16 +152,38 @@ void wxStaticBox::AddChild( wxWindowBase *child ) wxStaticBoxBase::AddChild(child); } +void wxStaticBox::WXDestroyWithoutChildren() +{ + // The label window doesn't count as our child, it's really a part of + // static box itself and it makes no sense to leave it alive when the box + // is destroyed, so do it even when it's supposed to be destroyed without + // destroying its children. + if ( m_labelWin ) + { + // By deleting it here, we indirectly remove this window from the list + // of our children and hence prevent the base class version of this + // method from reparenting it and thus keeping it alive. + delete m_labelWin; + m_labelWin = NULL; + } + + wxStaticBoxBase::WXDestroyWithoutChildren(); +} + void wxStaticBox::SetLabel( const wxString& label ) { wxCHECK_RET( m_widget != NULL, wxT("invalid staticbox") ); + wxCHECK_RET( !m_labelWin, wxS("Doesn't make sense when using label window") ); + GTKSetLabelForFrame(GTK_FRAME(m_widget), label); } void wxStaticBox::DoApplyWidgetStyle(GtkRcStyle *style) { GTKFrameApplyWidgetStyle(GTK_FRAME(m_widget), style); + if ( m_labelWin ) + GTKDoApplyWidgetStyle(m_labelWin, style); if (m_wxwindow) GTKApplyStyle(m_wxwindow, style);