From 9ef1b1529d68ffbdeee01eb91d77ef1b6ada0f27 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Mon, 7 Oct 2019 17:54:27 +0700 Subject: [PATCH 1/5] Add wxControl::GetSizeFromText() helper function Add the helper function that combines GetSizeFromTextSize() and GetTextExtent() as they are often used together. --- include/wx/control.h | 5 +++++ interface/wx/control.h | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/wx/control.h b/include/wx/control.h index 0e646b2faa..3500aa5253 100644 --- a/include/wx/control.h +++ b/include/wx/control.h @@ -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 // ------------------------------------------------ diff --git a/interface/wx/control.h b/interface/wx/control.h index 04670c9e04..0441883ef9 100644 --- a/interface/wx/control.h +++ b/interface/wx/control.h @@ -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. From 1be43ed67b7c7e75e6909e56d52b73f6a19fb68a Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Thu, 3 Oct 2019 00:39:12 +0700 Subject: [PATCH 2/5] Improve best size determination for wxSpinCtrl Use the range and the base to determine the widest value string and use it to calculate the best size. --- include/wx/msw/spinctrl.h | 3 ++ include/wx/spinctrl.h | 5 ++++ src/common/spinctrlcmn.cpp | 13 +++++++++ src/msw/spinctrl.cpp | 59 ++++++++++++++++---------------------- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/include/wx/msw/spinctrl.h b/include/wx/msw/spinctrl.h index 2152ca828d..0e3f05c132 100644 --- a/include/wx/msw/spinctrl.h +++ b/include/wx/msw/spinctrl.h @@ -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); diff --git a/include/wx/spinctrl.h b/include/wx/spinctrl.h index 4df89ff178..0051737fbc 100644 --- a/include/wx/spinctrl.h +++ b/include/wx/spinctrl.h @@ -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 diff --git a/src/common/spinctrlcmn.cpp b/src/common/spinctrlcmn.cpp index 87b9f64776..9598ef829d 100644 --- a/src/common/spinctrlcmn.cpp +++ b/src/common/spinctrlcmn.cpp @@ -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 diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index f76ad61968..11fa10ee83 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -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 From 8dadc2e68c115fd9f072f45a91884a306905de34 Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Thu, 3 Oct 2019 00:42:43 +0700 Subject: [PATCH 3/5] Improve wxGridCellNumberEditor placement in the grid Use the best height and don't let the editor be smaller then min size. Also align center vertically. --- include/wx/generic/grideditors.h | 2 ++ src/generic/grideditors.cpp | 42 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/wx/generic/grideditors.h b/include/wx/generic/grideditors.h index db5dd425db..0abe429f88 100644 --- a/include/wx/generic/grideditors.h +++ b/include/wx/generic/grideditors.h @@ -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, diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 8b1226a879..37d5d48621 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -703,6 +703,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 From f2286fc200ff329dd75a13597c13506d4d3bbbad Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Thu, 3 Oct 2019 00:56:40 +0700 Subject: [PATCH 4/5] Allow processing Enter and Tab in wxGridCellNumberEditor Add style flags to allow Tab and Enter works properly for the grid number efitor with ranges (which uses wxSpinCtrl). --- src/generic/grideditors.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/generic/grideditors.cpp b/src/generic/grideditors.cpp index 37d5d48621..c773f68dce 100644 --- a/src/generic/grideditors.cpp +++ b/src/generic/grideditors.cpp @@ -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); From d2776b2fba0917abd0c45de337773bd93822febc Mon Sep 17 00:00:00 2001 From: Ilya Sinitsyn Date: Mon, 7 Oct 2019 16:40:04 +0700 Subject: [PATCH 5/5] Invalidate wxSpinCtrl best size when needed in wxGTK Also fix the initial min and best size. --- src/gtk/spinctrl.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/gtk/spinctrl.cpp b/src/gtk/spinctrl.cpp index 932bc4623d..eaeeea8b3c 100644 --- a/src/gtk/spinctrl.cpp +++ b/src/gtk/spinctrl.cpp @@ -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(DoGetMin()); - const int lenMin = wxString::Format("%d", minVal).length(); - const int maxVal = static_cast(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; }