Merge branch 'spinctrl-fixes'

Various improvements to wxSpinCtrl and wxGridCellNumberEditor, using it.

Closes https://github.com/wxWidgets/wxWidgets/pull/1588
This commit is contained in:
Vadim Zeitlin
2019-10-09 01:52:10 +02:00
9 changed files with 129 additions and 43 deletions

View File

@@ -115,6 +115,11 @@ public:
wxSize GetSizeFromTextSize(const wxSize& tsize) const wxSize GetSizeFromTextSize(const wxSize& tsize) const
{ return DoGetSizeFromTextSize(tsize.x, tsize.y); } { return DoGetSizeFromTextSize(tsize.x, tsize.y); }
wxSize GetSizeFromText(const wxString& text) const
{
return GetSizeFromTextSize(GetTextExtent(text).GetWidth());
}
// static utilities for mnemonics char (&) handling // static utilities for mnemonics char (&) handling
// ------------------------------------------------ // ------------------------------------------------

View File

@@ -116,6 +116,8 @@ public:
wxWindowID id, wxWindowID id,
wxEvtHandler* evtHandler) wxOVERRIDE; wxEvtHandler* evtHandler) wxOVERRIDE;
virtual void SetSize(const wxRect& rect) wxOVERRIDE;
virtual bool IsAcceptedKey(wxKeyEvent& event) wxOVERRIDE; virtual bool IsAcceptedKey(wxKeyEvent& event) wxOVERRIDE;
virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE; virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE;
virtual bool EndEdit(int row, int col, const wxGrid* grid, virtual bool EndEdit(int row, int col, const wxGrid* grid,

View File

@@ -168,6 +168,9 @@ private:
// (up-down control) and the text control (buddy window). // (up-down control) and the text control (buddy window).
int GetOverlap() const; int GetOverlap() const;
// Calculate the best size for the number with the given number of digits.
wxSize GetBestSizeFromDigitsCount(int digitsCount) const;
wxDECLARE_DYNAMIC_CLASS(wxSpinCtrl); wxDECLARE_DYNAMIC_CLASS(wxSpinCtrl);
wxDECLARE_EVENT_TABLE(); wxDECLARE_EVENT_TABLE();
wxDECLARE_NO_COPY_CLASS(wxSpinCtrl); wxDECLARE_NO_COPY_CLASS(wxSpinCtrl);

View File

@@ -143,6 +143,11 @@ namespace wxPrivate
// string containing hexadecimal representation of the given number. // string containing hexadecimal representation of the given number.
extern wxString wxSpinCtrlFormatAsHex(long val, long maxVal); extern wxString wxSpinCtrlFormatAsHex(long val, long maxVal);
// The helper function to determine the best size for the given control.
// We can't implement this function in the wxSpinCtrlBase because MSW implementation
// of wxSpinCtrl is derived from wxSpinButton but uses the same algorithm.
extern wxSize wxSpinCtrlGetBestSize(const wxControl* spin, int minVal, int maxVal, int base);
} // namespace wxPrivate } // namespace wxPrivate
// old wxEVT_COMMAND_* constants // old wxEVT_COMMAND_* constants

View File

@@ -140,6 +140,27 @@ public:
*/ */
wxSize GetSizeFromTextSize(const wxSize& tsize) const; wxSize GetSizeFromTextSize(const wxSize& tsize) const;
/**
Determine the minimum size needed by the control to display the given text.
The helper function that uses combination of GetSizeFromTextSize() and
GetTextExtent() which used together pretty often:
@code
wxSize GetSizeFromText(const wxString& text) const
{
return GetSizeFromTextSize(GetTextExtent(text).GetWidth());
}
@endcode
@param text The given text.
@return The size that the control should have to leave the area of the
specified text. May return wxDefaultSize if this method is not
implemented for this particular control under the current platform.
@since 3.1.3
*/
wxSize GetSizeFromText(const wxString& text) const;
/** /**
Sets the control's label. Sets the control's label.

View File

@@ -115,4 +115,17 @@ wxString wxPrivate::wxSpinCtrlFormatAsHex(long val, long maxVal)
return text; return text;
} }
wxSize wxPrivate::wxSpinCtrlGetBestSize(const wxControl* spin,
int minVal, int maxVal, int base)
{
const int lenMin = (base == 16 ?
wxSpinCtrlFormatAsHex(minVal, maxVal) :
wxString::Format("%d", minVal)).length();
const int lenMax = (base == 16 ?
wxSpinCtrlFormatAsHex(maxVal, maxVal) :
wxString::Format("%d", maxVal)).length();
const wxString largestString('8', wxMax(lenMin, lenMax));
return spin->GetSizeFromText(largestString);
}
#endif // wxUSE_SPINCTRL #endif // wxUSE_SPINCTRL

View File

@@ -683,10 +683,14 @@ void wxGridCellNumberEditor::Create(wxWindow* parent,
#if wxUSE_SPINCTRL #if wxUSE_SPINCTRL
if ( HasRange() ) if ( HasRange() )
{ {
long style = wxSP_ARROW_KEYS |
wxTE_PROCESS_ENTER |
wxTE_PROCESS_TAB;
// create a spin ctrl // create a spin ctrl
m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString, m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize, wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS, style,
m_min, m_max); m_min, m_max);
wxGridCellEditor::Create(parent, id, evtHandler); wxGridCellEditor::Create(parent, id, evtHandler);
@@ -703,6 +707,48 @@ void wxGridCellNumberEditor::Create(wxWindow* parent,
} }
} }
void wxGridCellNumberEditor::SetSize(const wxRect& rectCell)
{
#if wxUSE_SPINCTRL
if ( HasRange() )
{
wxASSERT_MSG(m_control, "The wxSpinCtrl must be created first!");
wxSize size = Spin()->GetBestSize();
// Extend the control to fill the entire cell horizontally.
if ( size.x < rectCell.GetWidth() )
size.x = rectCell.GetWidth();
// Ensure it uses a reasonable height even if wxSpinCtrl::GetBestSize()
// didn't return anything useful.
if ( size.y <= 0 )
size.y = rectCell.GetHeight();
wxRect rectSpin(rectCell.GetPosition(), size);
// If possible, i.e. if we're not editing the topmost or leftmost cell,
// center the control rectangle in the cell.
if ( rectCell.GetTop() > 0 )
{
rectSpin.SetTop(rectCell.GetTop() -
(rectSpin.GetHeight() - rectCell.GetHeight()) / 2);
}
if ( rectCell.GetLeft() > 0 )
{
rectSpin.SetLeft(rectCell.GetLeft() -
(rectSpin.GetWidth() - rectCell.GetWidth()) / 2);
}
wxGridCellEditor::SetSize(rectSpin);
}
else
#endif // wxUSE_SPINCTRL
{
wxGridCellTextEditor::SetSize(rectCell);
}
}
void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid) void wxGridCellNumberEditor::BeginEdit(int row, int col, wxGrid* grid)
{ {
// first get the value // first get the value

View File

@@ -267,6 +267,8 @@ void wxSpinCtrlGTKBase::DoSetRange(double minVal, double maxVal)
wxSpinCtrlEventDisabler disable(this); wxSpinCtrlEventDisabler disable(this);
gtk_spin_button_set_range( GTK_SPIN_BUTTON(m_widget), minVal, maxVal); gtk_spin_button_set_range( GTK_SPIN_BUTTON(m_widget), minVal, maxVal);
InvalidateBestSize();
} }
void wxSpinCtrlGTKBase::DoSetIncrement(double inc) void wxSpinCtrlGTKBase::DoSetIncrement(double inc)
@@ -354,15 +356,8 @@ GdkWindow *wxSpinCtrlGTKBase::GTKGetWindow(wxArrayGdkWindows& windows) const
wxSize wxSpinCtrlGTKBase::DoGetBestSize() const wxSize wxSpinCtrlGTKBase::DoGetBestSize() const
{ {
const int minVal = static_cast<int>(DoGetMin()); const int minVal = static_cast<int>(DoGetMin());
const int lenMin = wxString::Format("%d", minVal).length();
const int maxVal = static_cast<int>(DoGetMax()); const int maxVal = static_cast<int>(DoGetMax());
const int lenMax = wxString::Format("%d", maxVal).length(); return wxPrivate::wxSpinCtrlGetBestSize(this, minVal, maxVal, GetBase());
wxString longestText(wxMax(lenMin, lenMax), '9');
if ( minVal < 0 )
longestText.insert(0, "-");
return DoGetSizeFromTextSize(GetTextExtent(longestText).x, -1);
} }
wxSize wxSpinCtrlGTKBase::DoGetSizeFromTextSize(int xlen, int ylen) const wxSize wxSpinCtrlGTKBase::DoGetSizeFromTextSize(int xlen, int ylen) const
@@ -477,6 +472,11 @@ bool wxSpinCtrl::SetBase(int base)
this); this);
} }
InvalidateBestSize();
// Update the displayed text after changing the base it uses.
SetValue(GetValue());
return true; return true;
} }

View File

@@ -348,39 +348,6 @@ bool wxSpinCtrl::Create(wxWindow *parent,
if (!m_hasFont) if (!m_hasFont)
SetFont(GetDefaultAttributes().font); SetFont(GetDefaultAttributes().font);
// Finally deal with the size: notice that this can only be done now both
// windows are created and the text one is set up as buddy because
// UDM_SETBUDDY changes its size using some unknown algorithm, so setting
// the sizes earlier is useless.
const int bestSpinWidth = wxSpinButton::DoGetBestSize().x;
const int effectiveSpinWidth = bestSpinWidth - GetOverlap();
wxSize sizeCtrl(size);
if ( sizeCtrl.x <= 0 )
{
// DEFAULT_ITEM_WIDTH is the default width for the text control
sizeCtrl.x = FromDIP(DEFAULT_ITEM_WIDTH) + effectiveSpinWidth;
}
else if ( sizeCtrl.x <= effectiveSpinWidth )
{
wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ")
wxS("at least %d pixels needed."),
name, size.x, effectiveSpinWidth);
}
// adjust an invalid height for text control
if ( sizeCtrl.y <= 0 )
{
int cx, cy;
wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
sizeCtrl.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
}
// This will call our DoMoveWindow() and lay out the windows correctly.
SetInitialSize(sizeCtrl);
(void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
// If the initial text value is actually a number, it overrides the // If the initial text value is actually a number, it overrides the
// "initial" argument specified later. // "initial" argument specified later.
long initialFromText; long initialFromText;
@@ -400,6 +367,22 @@ bool wxSpinCtrl::Create(wxWindow *parent,
SetValue(value); SetValue(value);
m_blockEvent = false; m_blockEvent = false;
// Finally deal with the size: notice that this can only be done now both
// windows are created and the text one is set up as buddy because
// UDM_SETBUDDY changes its size using some unknown algorithm, so setting
// the sizes earlier is useless. Do it after setting the range and the base
// because GetBestSize() uses them.
if ( size.x > 0 && size.x < GetBestSize().x )
{
wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ")
wxS("at least %d pixels needed."),
name, size.x, GetBestSize().x);
}
SetInitialSize(size);
(void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
return true; return true;
} }
@@ -439,10 +422,16 @@ bool wxSpinCtrl::SetBase(int base)
if ( !::SendMessage(GetHwnd(), UDM_SETBASE, base, 0) ) if ( !::SendMessage(GetHwnd(), UDM_SETBASE, base, 0) )
return false; return false;
// DoGetBestSize uses the base.
InvalidateBestSize();
// Whether we need to be able enter "x" or not influences whether we should // Whether we need to be able enter "x" or not influences whether we should
// use ES_NUMBER for the buddy control. // use ES_NUMBER for the buddy control.
UpdateBuddyStyle(); UpdateBuddyStyle();
// Update the displayed text after changing the base it uses.
SetValue(GetValue());
return true; return true;
} }
@@ -559,6 +548,8 @@ void wxSpinCtrl::SetRange(int minVal, int maxVal)
wxSpinButton::SetRange(minVal, maxVal); wxSpinButton::SetRange(minVal, maxVal);
InvalidateBestSize();
UpdateBuddyStyle(); UpdateBuddyStyle();
} }
@@ -748,7 +739,7 @@ int wxSpinCtrl::GetOverlap() const
wxSize wxSpinCtrl::DoGetBestSize() const wxSize wxSpinCtrl::DoGetBestSize() const
{ {
return DoGetSizeFromTextSize(FromDIP(DEFAULT_ITEM_WIDTH)); return wxPrivate::wxSpinCtrlGetBestSize(this, GetMin(), GetMax(), GetBase());
} }
wxSize wxSpinCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const wxSize wxSpinCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const