Merge branch 'statbox-any-label'

Add support for using any wxWindow (and not just the equivalent of
wxStaticText) as wxStaticBox label.

See https://github.com/wxWidgets/wxWidgets/pull/650
This commit is contained in:
Vadim Zeitlin 2018-01-23 15:40:31 +01:00
commit ff1fe53d66
19 changed files with 710 additions and 152 deletions

View File

@ -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 XRC handler for wxSpinCtrlDouble (Trylz).

View File

@ -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

View File

@ -2430,6 +2430,9 @@ support the following properties:
Sizer orientation, "wxHORIZONTAL" or "wxVERTICAL" (default: wxHORIZONTAL).}
@row3col{label, @ref overview_xrcformat_type_text,
Label to be used for the static box around the sizer (default: empty).}
@row3col{windowlabel, any window,
Window to be used instead of the plain text label (default: none).
This property is only available since wxWidgets 3.1.1.}}
@endTable
@subsection overview_xrcformat_wxgridsizer wxGridSizer

View File

@ -26,24 +26,18 @@ class WXDLLIMPEXP_FWD_CORE wxToolTip;
// base class name and implement GetCompositeWindowParts() pure virtual method.
// ----------------------------------------------------------------------------
// This is the base class of wxCompositeWindow which takes care of propagating
// colours, fonts etc changes to all the children, but doesn't bother with
// handling their events or focus. There should be rarely any need to use it
// rather than the full wxCompositeWindow.
// The template parameter W must be a wxWindow-derived class.
template <class W>
class wxCompositeWindow : public W
class wxCompositeWindowSettersOnly : public W
{
public:
typedef W BaseWindowClass;
// Default ctor doesn't do anything.
wxCompositeWindow()
{
this->Connect
(
wxEVT_CREATE,
wxWindowCreateEventHandler(wxCompositeWindow::OnWindowCreate)
);
}
// Override all wxWindow methods which must be forwarded to the composite
// window parts.
@ -109,7 +103,7 @@ public:
// SetLayoutDirection(wxLayout_Default) wouldn't result in a re-layout
// neither, but then we're not supposed to be called with it at all.
if ( dir != wxLayout_Default )
this->SetSize(-1, -1, -1, -1, wxSIZE_AUTO | wxSIZE_FORCE);
this->SetSize(-1, -1, -1, -1, wxSIZE_FORCE);
}
#if wxUSE_TOOLTIPS
@ -131,9 +125,10 @@ public:
}
#endif // wxUSE_TOOLTIPS
virtual void SetFocus() wxOVERRIDE
protected:
// Trivial but necessary default ctor.
wxCompositeWindowSettersOnly()
{
wxSetFocusToChild(this, NULL);
}
private:
@ -141,6 +136,50 @@ private:
// the public methods we override should forward to.
virtual wxWindowList GetCompositeWindowParts() const = 0;
template <class T, class TArg, class R>
void SetForAllParts(R (wxWindowBase::*func)(TArg), T arg)
{
// Simply call the setters for all parts of this composite window.
const wxWindowList parts = GetCompositeWindowParts();
for ( wxWindowList::const_iterator i = parts.begin();
i != parts.end();
++i )
{
wxWindow * const child = *i;
// Allow NULL elements in the list, this makes the code of derived
// composite controls which may have optionally shown children
// simpler and it doesn't cost us much here.
if ( child )
(child->*func)(arg);
}
}
wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxCompositeWindowSettersOnly, W);
};
// The real wxCompositeWindow itself, inheriting all the setters defined above.
template <class W>
class wxCompositeWindow : public wxCompositeWindowSettersOnly<W>
{
public:
virtual void SetFocus() wxOVERRIDE
{
wxSetFocusToChild(this, NULL);
}
protected:
// Default ctor sets things up for handling children events correctly.
wxCompositeWindow()
{
this->Connect
(
wxEVT_CREATE,
wxWindowCreateEventHandler(wxCompositeWindow::OnWindowCreate)
);
}
private:
void OnWindowCreate(wxWindowCreateEvent& event)
{
event.Skip();
@ -206,25 +245,6 @@ private:
event.Skip();
}
template <class T, class TArg, class R>
void SetForAllParts(R (wxWindowBase::*func)(TArg), T arg)
{
// Simply call the setters for all parts of this composite window.
const wxWindowList parts = GetCompositeWindowParts();
for ( wxWindowList::const_iterator i = parts.begin();
i != parts.end();
++i )
{
wxWindow * const child = *i;
// Allow NULL elements in the list, this makes the code of derived
// composite controls which may have optionally shown children
// simpler and it doesn't cost us much here.
if ( child )
(child->*func)(arg);
}
}
wxDECLARE_NO_COPY_TEMPLATE_CLASS(wxCompositeWindow, W);
};

View File

@ -16,21 +16,53 @@
class WXDLLIMPEXP_CORE wxStaticBox : public wxStaticBoxBase
{
public:
wxStaticBox();
wxStaticBox()
{
}
wxStaticBox( wxWindow *parent,
wxWindowID id,
const wxString &label,
const wxPoint &pos = wxDefaultPosition,
const wxSize &size = wxDefaultSize,
long style = 0,
const wxString &name = wxStaticBoxNameStr );
const wxString &name = wxStaticBoxNameStr )
{
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 )
{
Create( parent, id, label, pos, size, style, name );
}
bool Create( wxWindow *parent,
wxWindowID id,
const wxString &label,
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;
@ -46,6 +78,17 @@ public:
virtual void AddChild( wxWindowBase *child ) wxOVERRIDE;
protected:
// 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;
@ -54,4 +97,7 @@ protected:
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_

View File

@ -206,6 +206,15 @@ public:
virtual void GTKHandleRealized();
void GTKHandleUnrealize();
// Apply the widget style to the given window. Should normally only be
// called from the overridden DoApplyWidgetStyle() implementation in
// another window and exists solely to provide access to protected
// DoApplyWidgetStyle() when it's really needed.
static void GTKDoApplyWidgetStyle(wxWindowGTK* win, GtkRcStyle *style)
{
win->DoApplyWidgetStyle(style);
}
protected:
// for controls composed of multiple GTK widgets, return true to eliminate
// spurious focus events if the focus changes between GTK+ children within
@ -426,8 +435,11 @@ protected:
void GTKApplyWidgetStyle(bool forceStyle = false);
// helper function to ease native widgets wrapping, called by
// ApplyWidgetStyle -- override this, not ApplyWidgetStyle
// Helper function to ease native widgets wrapping, called by
// GTKApplyWidgetStyle() and supposed to be overridden, not called.
//
// And if you actually need to call it, e.g. to propagate style change to a
// composite control, use public static GTKDoApplyWidgetStyle().
virtual void DoApplyWidgetStyle(GtkRcStyle *style);
void GTKApplyStyle(GtkWidget* widget, GtkRcStyle* style);

View File

@ -11,11 +11,16 @@
#ifndef _WX_MSW_STATBOX_H_
#define _WX_MSW_STATBOX_H_
#include "wx/compositewin.h"
// Group box
class WXDLLIMPEXP_CORE wxStaticBox : public wxStaticBoxBase
class WXDLLIMPEXP_CORE wxStaticBox : public wxCompositeWindowSettersOnly<wxStaticBoxBase>
{
public:
wxStaticBox() { }
wxStaticBox()
: wxCompositeWindowSettersOnly<wxStaticBoxBase>()
{
}
wxStaticBox(wxWindow *parent, wxWindowID id,
const wxString& label,
@ -23,6 +28,18 @@ public:
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxStaticBoxNameStr)
: wxCompositeWindowSettersOnly<wxStaticBoxBase>()
{
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)
: wxCompositeWindowSettersOnly<wxStaticBoxBase>()
{
Create(parent, id, label, pos, size, style, name);
}
@ -34,9 +51,19 @@ public:
long style = 0,
const wxString& name = wxStaticBoxNameStr);
bool Create(wxWindow *parent, wxWindowID id,
wxWindow* label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxStaticBoxNameStr);
/// Implementation only
virtual void GetBordersForSizer(int *borderTop, int *borderOther) const wxOVERRIDE;
virtual bool SetBackgroundColour(const wxColour& colour) wxOVERRIDE;
virtual bool SetFont(const wxFont& font) wxOVERRIDE;
virtual WXDWORD MSWGetStyle(long style, WXDWORD *exstyle) const wxOVERRIDE;
// returns true if the platform should explicitly apply a theme border
@ -49,6 +76,8 @@ public:
virtual WXLRESULT MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) wxOVERRIDE;
protected:
virtual wxWindowList GetCompositeWindowParts() const wxOVERRIDE;
// return the region with all the windows inside this static box excluded
virtual WXHRGN MSWGetRegionWithoutChildren();
@ -63,8 +92,14 @@ protected:
void OnPaint(wxPaintEvent& event);
private:
void PositionLabelWindow();
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxStaticBox);
};
// Indicate that we have the ctor overload taking wxWindow as label.
#define wxHAS_WINDOW_LABEL_IN_STATIC_BOX
#endif // _WX_MSW_STATBOX_H_

View File

@ -31,24 +31,36 @@ public:
// overridden base class virtuals
virtual bool HasTransparentBackground() wxOVERRIDE { return true; }
virtual bool Enable(bool enable = true) wxOVERRIDE;
// implementation only: this is used by wxStaticBoxSizer to account for the
// need for extra space taken by the static box
//
// the top border is the margin at the top (where the title is),
// borderOther is the margin on all other sides
virtual void GetBordersForSizer(int *borderTop, int *borderOther) const
{
const int BORDER = FromDIP(5); // FIXME: hardcoded value
virtual void GetBordersForSizer(int *borderTop, int *borderOther) const;
*borderTop = GetLabel().empty() ? BORDER : GetCharHeight();
*borderOther = BORDER;
}
// This is an internal function currently used by wxStaticBoxSizer only.
//
// Reparent all children of the static box under its parent and destroy the
// box itself.
void WXDestroyWithoutChildren();
protected:
// choose the default border for this window
virtual wxBorder GetDefaultBorder() const wxOVERRIDE { return wxBORDER_NONE; }
// 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;
// For boxes with window label this member variable is used instead of
// m_isEnabled to remember the last value passed to Enable(). It is
// required because the box itself doesn't get disabled by Enable(false) in
// this case (see comments in Enable() implementation), and m_isEnabled
// must correspond to its real state.
bool m_areChildrenEnabled;
wxDECLARE_NO_COPY_CLASS(wxStaticBoxBase);
};

View File

@ -48,22 +48,6 @@
#define wxUSE_MENUS_NATIVE wxUSE_MENUS
#endif // __WXUNIVERSAL__/!__WXUNIVERSAL__
// Define this macro if the corresponding operating system handles the state
// of children windows automatically when the parent is enabled/disabled.
// Otherwise wx itself must ensure that when the parent is disabled its
// children are disabled too, and their initial state is restored when the
// parent is enabled back.
#if defined(__WXMSW__)
// must do everything ourselves
#undef wxHAS_NATIVE_ENABLED_MANAGEMENT
#elif defined(__WXOSX__)
// must do everything ourselves
#undef wxHAS_NATIVE_ENABLED_MANAGEMENT
#else
#define wxHAS_NATIVE_ENABLED_MANAGEMENT
#endif
// ----------------------------------------------------------------------------
// forward declarations
// ----------------------------------------------------------------------------

View File

@ -71,7 +71,11 @@ public:
Checkbox size.
If ::wxDefaultSize is specified then a default size is chosen.
@param style
Window style. See wxStaticBox.
Window style. There are no wxStaticBox-specific styles, but generic
::wxALIGN_LEFT, ::wxALIGN_CENTRE_HORIZONTAL and ::wxALIGN_RIGHT can
be used here to change the position of the static box label when
using wxGTK (these styles are ignored under the other platforms
currently).
@param name
Window name.
@ -84,6 +88,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 and wxMSW, 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 +137,66 @@ 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 and wxMSW, use
@c wxHAS_WINDOW_LABEL_IN_STATIC_BOX to check whether it can be used at
compile-time.
@since 3.1.1
*/
bool Create(wxWindow* parent, wxWindowID id,
wxWindow* label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = wxStaticBoxNameStr);
/**
Enables or disables the box without affecting its label window, if any.
wxStaticBox overrides wxWindow::Enable() in order to avoid disabling
the control used as a label, if this box is using one. This is done in
order to allow using a wxCheckBox, for example, label and enable or
disable the box according to the state of the checkbox: if disabling
the box also disabled the checkbox in this situation, it would make it
impossible for the user to re-enable the box after disabling it, so the
checkbox stays enabled even if @c box->Enable(false) is called.
However with the actual behaviour, implemented in this overridden
method, the following code (shown using C++11 only for convenience,
this behaviour is not C++11-specific):
@code
auto check = new wxCheckBox(parent, wxID_ANY, "Use the box");
auto box = new wxStaticBox(parent, wxID_ANY, check);
check->Bind(wxEVT_CHECKBOX,
[box](wxCommandEvent& event) {
box->Enable(event.IsChecked());
});
@endcode
does work as expected.
Please note that overriding Enable() to not actually disable this
window itself has two possibly unexpected consequences:
- The box retains its enabled status, i.e. IsEnabled() still returns
@true, after calling @c Enable(false).
- The box children are enabled or disabled when the box is, which can
result in the loss of their original state. E.g. if a box child is
initially disabled, then the box itself is disabled and, finally, the
box is enabled again, this child will end up being enabled too (this
wouldn't happen with any other parent window as its children would
inherit the disabled state from the parent instead of being really
disabled themselves when it is disabled). To avoid this problem,
consider using ::wxEVT_UPDATE_UI to ensure that the child state is
always correct or restoring it manually after re-enabling the box.
*/
virtual bool Enable(bool enable = true);
};

View File

@ -1879,6 +1879,7 @@ wxStaticBoxSizer_horz =
stdObjectNodeAttributes &
stdSizerProperties &
[xrc:p="important"] element label {_, t_text }* &
element windowlabel {windowNode}* &
[xrc:p="o"] element orient {_, "wxHORIZONTAL" }* &
(wxBoxSizer_horz_item | objectRef)*
}
@ -1889,6 +1890,7 @@ wxStaticBoxSizer_vert =
stdObjectNodeAttributes &
stdSizerProperties &
[xrc:p="important"] element label {_, t_text }* &
element windowlabel {windowNode}* &
element orient {_, "wxVERTICAL" } &
(wxBoxSizer_vert_item | objectRef)*
}

View File

@ -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);
@ -518,12 +551,12 @@ void StaticWidgetsPage::CreateStatic()
isVert ? wxLI_VERTICAL : wxLI_HORIZONTAL);
#endif // wxUSE_STATLINE
m_sizerStatBox->Add(m_statText, 0, wxGROW | wxALL, 5);
m_sizerStatBox->Add(m_statText, 0, wxGROW);
#if wxUSE_STATLINE
m_sizerStatBox->Add(m_statLine, 0, wxGROW | wxALL, 5);
m_sizerStatBox->Add(m_statLine, 0, wxGROW | wxTOP | wxBOTTOM, 10);
#endif // wxUSE_STATLINE
#if wxUSE_MARKUP
m_sizerStatBox->Add(m_statMarkup, 0, wxALL, 5);
m_sizerStatBox->Add(m_statMarkup);
#endif // wxUSE_MARKUP
m_sizerStatic->Add(m_sizerStatBox, 0, wxGROW);
@ -536,6 +569,8 @@ void StaticWidgetsPage::CreateStatic()
staticBox->Connect(wxEVT_LEFT_UP,
wxMouseEventHandler(StaticWidgetsPage::OnMouseEvent),
NULL, this);
SetUpWidget();
}
// ----------------------------------------------------------------------------
@ -559,6 +594,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());

View File

@ -983,6 +983,58 @@ lay them out using wxSizers, absolute positioning, everything you like!
<object class="wxStaticBoxSizer">
<orient>wxVERTICAL</orient>
<label>Internet options</label>
<object class="sizeritem">
<flag>wxALL</flag>
<border>5</border>
<object class="wxRadioButton">
<style>wxRB_GROUP</style>
<label>Use _proxy</label>
</object>
</object>
<object class="sizeritem">
<flag>wxALL</flag>
<border>5</border>
<object class="wxRadioButton">
<label>Use _VPN</label>
</object>
</object>
<object class="sizeritem">
<flag>wxALL</flag>
<border>5</border>
<object class="wxRadioButton">
<label>Use _Tor</label>
<value>1</value>
</object>
</object>
</object>
</object>
<object class="sizeritem">
<flag>wxGROW|wxALL</flag>
<border>5</border>
<object class="wxStaticBoxSizer">
<orient>wxVERTICAL</orient>
<windowlabel>
<object class="wxCheckBox">
<label>Enable quantum mechanics</label>
<checked>1</checked>
</object>
</windowlabel>
<object class="sizeritem">
<flag>wxALL</flag>
<border>5</border>
<object class="wxRadioButton">
<style>wxRB_GROUP</style>
<label>Cat is dead</label>
</object>
</object>
<object class="sizeritem">
<flag>wxALL</flag>
<border>5</border>
<object class="wxRadioButton">
<label>Cat is alive</label>
<value>1</value>
</object>
</object>
</object>
</object>
</object>

View File

@ -2582,20 +2582,7 @@ wxStaticBoxSizer::~wxStaticBoxSizer()
// previous wxWidgets versions, so ensure they are left alive.
if ( m_staticBox )
{
// Notice that we must make a copy of the list as it will be changed by
// Reparent() calls in the loop.
const wxWindowList children = m_staticBox->GetChildren();
wxWindow* const parent = m_staticBox->GetParent();
for ( wxWindowList::const_iterator i = children.begin();
i != children.end();
++i )
{
(*i)->Reparent(parent);
}
delete m_staticBox;
}
m_staticBox->WXDestroyWithoutChildren();
}
void wxStaticBoxSizer::RecalcSizes()

View File

@ -31,11 +31,90 @@ extern WXDLLEXPORT_DATA(const char) wxStaticBoxNameStr[] = "groupBox";
wxStaticBoxBase::wxStaticBoxBase()
{
m_labelWin = NULL;
m_areChildrenEnabled = true;
#ifndef __WXGTK__
m_container.DisableSelfFocus();
#endif
}
void wxStaticBoxBase::GetBordersForSizer(int *borderTop, int *borderOther) const
{
const int BORDER = FromDIP(5); // FIXME: hardcoded value
if ( m_labelWin )
{
*borderTop = m_labelWin->GetSize().y;
}
else
{
*borderTop = GetLabel().empty() ? BORDER : GetCharHeight();
}
*borderOther = BORDER;
}
void wxStaticBoxBase::WXDestroyWithoutChildren()
{
// Notice that we must make a copy of the list as it will be changed by
// Reparent() calls in the loop.
const wxWindowList children = GetChildren();
wxWindow* const parent = GetParent();
for ( wxWindowList::const_iterator i = children.begin();
i != children.end();
++i )
{
// 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 -- by not reparenting it, we ensure
// that it's destroyed when this object itself is below.
if ( *i != m_labelWin )
{
(*i)->Reparent(parent);
}
}
delete this;
}
bool wxStaticBoxBase::Enable(bool enable)
{
#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX
// We want to keep the window label enabled even if the static box is
// disabled because this label is often used to enable/disable the box
// (e.g. a checkbox or a radio button is commonly used for this purpose)
// and it would be impossible to re-enable the box back if disabling it
// also disabled the label control.
//
// Unfortunately it is _not_ enough to just disable the box and then enable
// the label window as it would still remain disabled for as long as its
// parent is disabled. So we avoid disabling the box at all in this case
// and only disable its children.
if ( m_labelWin )
{
if ( enable == m_areChildrenEnabled )
return false;
m_areChildrenEnabled = enable;
const wxWindowList& children = GetChildren();
for ( wxWindowList::const_iterator i = children.begin();
i != children.end();
++i )
{
if ( *i != m_labelWin )
(*i)->Enable(enable);
}
return true;
}
#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX
return wxNavigationEnabled<wxControl>::Enable(enable);
}
// ----------------------------------------------------------------------------
// XTI
// ----------------------------------------------------------------------------

View File

@ -1166,15 +1166,26 @@ bool wxWindowBase::IsEnabled() const
return IsThisEnabled() && (IsTopLevel() || !GetParent() || GetParent()->IsEnabled());
}
// Define this macro if the corresponding operating system handles the state
// of children windows automatically when the parent is enabled/disabled.
// Otherwise wx itself must ensure that when the parent is disabled its
// children are disabled too, and their initial state is restored when the
// parent is enabled back.
#if defined(__WXMSW__)
// must do everything ourselves
#undef wxHAS_NATIVE_ENABLED_MANAGEMENT
#elif defined(__WXOSX__)
// must do everything ourselves
#undef wxHAS_NATIVE_ENABLED_MANAGEMENT
#else
#define wxHAS_NATIVE_ENABLED_MANAGEMENT
#endif
void wxWindowBase::NotifyWindowOnEnableChange(bool enabled)
{
// Under some platforms there is no need to update the window state
// explicitly, it will become disabled when its parent is. On other ones we
// do need to disable all windows recursively though.
#ifndef wxHAS_NATIVE_ENABLED_MANAGEMENT
DoEnable(enabled);
#endif // !defined(wxHAS_NATIVE_ENABLED_MANAGEMENT)
#ifndef wxHAS_NATIVE_ENABLED_MANAGEMENT
// Disabling a top level window is typically done when showing a modal
// dialog and we don't need to disable its children in this case, they will
// be logically disabled anyhow (i.e. their IsEnabled() will return false)
@ -1190,7 +1201,6 @@ void wxWindowBase::NotifyWindowOnEnableChange(bool enabled)
// they would still show as enabled even though they wouldn't actually
// accept any input (at least under MSW where children don't accept input
// if any of the windows in their parent chain is enabled).
#ifndef wxHAS_NATIVE_ENABLED_MANAGEMENT
for ( wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
node;
node = node->GetNext() )
@ -1209,12 +1219,6 @@ bool wxWindowBase::Enable(bool enable)
m_isEnabled = enable;
// If we call DoEnable() from NotifyWindowOnEnableChange(), we don't need
// to do it from here.
#ifdef wxHAS_NATIVE_ENABLED_MANAGEMENT
DoEnable(enable);
#endif // !defined(wxHAS_NATIVE_ENABLED_MANAGEMENT)
NotifyWindowOnEnableChange(enable);
return true;

View File

@ -59,28 +59,14 @@ static gboolean expose_event(GtkWidget* widget, GdkEventExpose*, wxWindow*)
// wxStaticBox
//-----------------------------------------------------------------------------
wxStaticBox::wxStaticBox()
{
}
wxStaticBox::wxStaticBox( wxWindow *parent,
wxWindowID id,
const wxString &label,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name )
{
Create( parent, id, label, pos, size, style, name );
}
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 ))
@ -89,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 );
@ -140,12 +156,16 @@ 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);

View File

@ -54,6 +54,21 @@
#define TMT_FONT 210
namespace
{
// Offset of the first pixel of the label from the box left border.
//
// FIXME: value is hardcoded as this is what it is on my system, no idea if
// it's true everywhere
const int LABEL_HORZ_OFFSET = 9;
// Extra borders around the label on left/right and bottom sides.
const int LABEL_HORZ_BORDER = 2;
const int LABEL_VERT_BORDER = 2;
} // anonymous namespace
// ----------------------------------------------------------------------------
// wxWin macros
// ----------------------------------------------------------------------------
@ -92,6 +107,41 @@ bool wxStaticBox::Create(wxWindow *parent,
return true;
}
bool wxStaticBox::Create(wxWindow* parent,
wxWindowID id,
wxWindow* labelWin,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
wxCHECK_MSG( labelWin, false, wxS("Label window can't be null") );
if ( !Create(parent, id, wxString(), pos, size, style, name) )
return false;
m_labelWin = labelWin;
m_labelWin->Reparent(this);
PositionLabelWindow();
return true;
}
void wxStaticBox::PositionLabelWindow()
{
m_labelWin->SetSize(m_labelWin->GetBestSize());
m_labelWin->Move(FromDIP(LABEL_HORZ_OFFSET), 0);
}
wxWindowList wxStaticBox::GetCompositeWindowParts() const
{
wxWindowList parts;
if ( m_labelWin )
parts.push_back(m_labelWin);
return parts;
}
WXDWORD wxStaticBox::MSWGetStyle(long style, WXDWORD *exstyle) const
{
long styleWin = wxStaticBoxBase::MSWGetStyle(style, exstyle);
@ -152,7 +202,31 @@ void wxStaticBox::GetBordersForSizer(int *borderTop, int *borderOther) const
wxStaticBoxBase::GetBordersForSizer(borderTop, borderOther);
// need extra space, don't know how much but this seems to be enough
*borderTop += GetCharHeight()/3;
*borderTop += FromDIP(LABEL_VERT_BORDER);
}
bool wxStaticBox::SetBackgroundColour(const wxColour& colour)
{
// Do _not_ call the immediate base class method, we don't need to set the
// label window (which is the only sub-window of this composite window)
// background explicitly because it will almost always be a wxCheckBox or
// wxRadioButton which inherits its background from the box anyhow, so
// setting it would be at best useless.
return wxStaticBoxBase::SetBackgroundColour(colour);
}
bool wxStaticBox::SetFont(const wxFont& font)
{
if ( !wxCompositeWindowSettersOnly<wxStaticBoxBase>::SetFont(font) )
return false;
// We need to reposition the label as its size may depend on the font.
if ( m_labelWin )
{
PositionLabelWindow();
}
return true;
}
WXLRESULT wxStaticBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
@ -189,9 +263,10 @@ WXLRESULT wxStaticBox::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lPar
if ( !HandlePrintClient((WXHDC)wParam) )
{
// no, we don't, erase the background ourselves
// (don't use our own) - see PaintBackground for explanation
wxBrush brush(GetParent()->GetBackgroundColour());
wxFillRect(GetHwnd(), (HDC)wParam, GetHbrushOf(brush));
RECT rc;
::GetClientRect(GetHwnd(), &rc);
wxDCTemp dc((WXHDC)wParam);
PaintBackground(dc, rc);
}
return 0;
@ -253,7 +328,21 @@ void wxStaticBox::MSWGetRegionWithoutSelf(WXHRGN hRgn, int w, int h)
GetBordersForSizer(&borderTop, &border);
// top
SubtractRectFromRgn(hrgn, 0, 0, w, borderTop);
if ( m_labelWin )
{
// Don't exclude the entire rectangle at the top, we do need to paint
// the background of the gap between the label window and the box
// frame.
const wxRect labelRect = m_labelWin->GetRect();
const int gap = FromDIP(LABEL_HORZ_BORDER);
SubtractRectFromRgn(hrgn, 0, 0, labelRect.GetLeft() - gap, borderTop);
SubtractRectFromRgn(hrgn, labelRect.GetRight() + gap, 0, w, borderTop);
}
else
{
SubtractRectFromRgn(hrgn, 0, 0, w, borderTop);
}
// bottom
SubtractRectFromRgn(hrgn, 0, h - border, w, h);
@ -399,7 +488,7 @@ void wxStaticBox::PaintForeground(wxDC& dc, const RECT&)
// background mode doesn't change anything: the static box def window proc
// still draws the label in its own colours, so we need to redraw the text
// ourselves if we have a non default fg colour
if ( m_hasFgCol && wxUxThemeEngine::GetIfActive() )
if ( m_hasFgCol && wxUxThemeEngine::GetIfActive() && !m_labelWin )
{
// draw over the text in default colour in our colour
HDC hdc = GetHdcOf(*impl);
@ -443,23 +532,17 @@ void wxStaticBox::PaintForeground(wxDC& dc, const RECT&)
dc.GetTextExtent(wxStripMenuCodes(label, wxStrip_Mnemonics),
&width, &height);
int x;
int y = height;
// first we need to correctly paint the background of the label
// as Windows ignores the brush offset when doing it
//
// FIXME: value of x is hardcoded as this is what it is on my system,
// no idea if it's true everywhere
RECT dimensions = {0, 0, 0, y};
x = 9;
const int x = FromDIP(LABEL_HORZ_OFFSET);
RECT dimensions = { x, 0, 0, height };
dimensions.left = x;
dimensions.right = x + width;
// need to adjust the rectangle to cover all the label background
dimensions.left -= 2;
dimensions.right += 2;
dimensions.bottom += 2;
dimensions.left -= FromDIP(LABEL_HORZ_BORDER);
dimensions.right += FromDIP(LABEL_HORZ_BORDER);
dimensions.bottom += FromDIP(LABEL_VERT_BORDER);
if ( UseBgCol() )
{
@ -489,7 +572,7 @@ void wxStaticBox::PaintForeground(wxDC& dc, const RECT&)
}
// now draw the text
RECT rc2 = { x, 0, x + width, y };
RECT rc2 = { x, 0, x + width, height };
::DrawText(hdc, label.t_str(), label.length(), &rc2,
drawTextFlags);
}
@ -525,8 +608,31 @@ void wxStaticBox::OnPaint(wxPaintEvent& WXUNUSED(event))
GetBordersForSizer(&borderTop, &border);
// top
dc.Blit(border, 0, rc.right - border, borderTop,
&memdc, border, 0);
if ( m_labelWin )
{
// We also have to exclude the area taken by the label window,
// otherwise there would be flicker when it draws itself on top of it.
const wxRect labelRect = m_labelWin->GetRect();
// We also leave a small border around label window to make it appear
// more similarly to a plain text label.
const int gap = FromDIP(LABEL_HORZ_BORDER);
dc.Blit(border, 0,
labelRect.GetLeft() - gap - border,
borderTop,
&memdc, border, 0);
dc.Blit(labelRect.GetRight() + gap, 0,
rc.right - (labelRect.GetRight() + gap),
borderTop,
&memdc, border, 0);
}
else
{
dc.Blit(border, 0, rc.right - border, borderTop,
&memdc, border, 0);
}
// bottom
dc.Blit(border, rc.bottom - border, rc.right - border, border,
&memdc, border, rc.bottom - border);

View File

@ -320,14 +320,63 @@ wxSizer* wxSizerXmlHandler::Handle_wxBoxSizer()
#if wxUSE_STATBOX
wxSizer* wxSizerXmlHandler::Handle_wxStaticBoxSizer()
{
return new wxStaticBoxSizer(
new wxStaticBox(m_parentAsWindow,
GetID(),
GetText(wxT("label")),
wxDefaultPosition, wxDefaultSize,
0/*style*/,
GetName()),
GetStyle(wxT("orient"), wxHORIZONTAL));
wxXmlNode* nodeWindowLabel = GetParamNode(wxS("windowlabel"));
wxString const& labelText = GetText(wxS("label"));
wxStaticBox* box = NULL;
if ( nodeWindowLabel )
{
if ( !labelText.empty() )
{
ReportError("Either label or windowlabel can be used, but not both");
return NULL;
}
#ifdef wxHAS_WINDOW_LABEL_IN_STATIC_BOX
wxXmlNode* n = nodeWindowLabel->GetChildren();
if ( !n )
{
ReportError("windowlabel must have a window child");
return NULL;
}
if ( n->GetNext() )
{
ReportError("windowlabel can only have a single child");
return NULL;
}
wxObject* const item = CreateResFromNode(n, m_parent, NULL);
wxWindow* const wndLabel = wxDynamicCast(item, wxWindow);
if ( !wndLabel )
{
ReportError(n, "windowlabel child must be a window");
return NULL;
}
box = new wxStaticBox(m_parentAsWindow,
GetID(),
wndLabel,
wxDefaultPosition, wxDefaultSize,
0/*style*/,
GetName());
#else // !wxHAS_WINDOW_LABEL_IN_STATIC_BOX
ReportError("Support for using windows as wxStaticBox labels is "
"missing in this build of wxWidgets.");
return NULL;
#endif // wxHAS_WINDOW_LABEL_IN_STATIC_BOX/!wxHAS_WINDOW_LABEL_IN_STATIC_BOX
}
else // Using plain text label.
{
box = new wxStaticBox(m_parentAsWindow,
GetID(),
labelText,
wxDefaultPosition, wxDefaultSize,
0/*style*/,
GetName());
}
return new wxStaticBoxSizer(box, GetStyle(wxS("orient"), wxHORIZONTAL));
}
#endif // wxUSE_STATBOX