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
{ return DoGetSizeFromTextSize(tsize.x, tsize.y); }
wxSize GetSizeFromText(const wxString& text) const
{
return GetSizeFromTextSize(GetTextExtent(text).GetWidth());
}
// static utilities for mnemonics char (&) handling
// ------------------------------------------------

View File

@@ -116,6 +116,8 @@ public:
wxWindowID id,
wxEvtHandler* evtHandler) wxOVERRIDE;
virtual void SetSize(const wxRect& rect) wxOVERRIDE;
virtual bool IsAcceptedKey(wxKeyEvent& event) wxOVERRIDE;
virtual void BeginEdit(int row, int col, wxGrid* grid) wxOVERRIDE;
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).
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_EVENT_TABLE();
wxDECLARE_NO_COPY_CLASS(wxSpinCtrl);

View File

@@ -143,6 +143,11 @@ namespace wxPrivate
// string containing hexadecimal representation of the given number.
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
// old wxEVT_COMMAND_* constants

View File

@@ -140,6 +140,27 @@ public:
*/
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.

View File

@@ -115,4 +115,17 @@ wxString wxPrivate::wxSpinCtrlFormatAsHex(long val, long maxVal)
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

View File

@@ -683,10 +683,14 @@ void wxGridCellNumberEditor::Create(wxWindow* parent,
#if wxUSE_SPINCTRL
if ( HasRange() )
{
long style = wxSP_ARROW_KEYS |
wxTE_PROCESS_ENTER |
wxTE_PROCESS_TAB;
// create a spin ctrl
m_control = new wxSpinCtrl(parent, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS,
style,
m_min, m_max);
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)
{
// first get the value

View File

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

View File

@@ -348,39 +348,6 @@ bool wxSpinCtrl::Create(wxWindow *parent,
if (!m_hasFont)
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
// "initial" argument specified later.
long initialFromText;
@@ -400,6 +367,22 @@ bool wxSpinCtrl::Create(wxWindow *parent,
SetValue(value);
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;
}
@@ -439,10 +422,16 @@ bool wxSpinCtrl::SetBase(int base)
if ( !::SendMessage(GetHwnd(), UDM_SETBASE, base, 0) )
return false;
// DoGetBestSize uses the base.
InvalidateBestSize();
// Whether we need to be able enter "x" or not influences whether we should
// use ES_NUMBER for the buddy control.
UpdateBuddyStyle();
// Update the displayed text after changing the base it uses.
SetValue(GetValue());
return true;
}
@@ -559,6 +548,8 @@ void wxSpinCtrl::SetRange(int minVal, int maxVal)
wxSpinButton::SetRange(minVal, maxVal);
InvalidateBestSize();
UpdateBuddyStyle();
}
@@ -748,7 +739,7 @@ int wxSpinCtrl::GetOverlap() 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