Fix wxMSW wxSpinCtrl appearance: show arrows inside the control

As recommended in the "Spin Controls" MSDN documentation (see
https://msdn.microsoft.com/en-us/library/windows/desktop/dn742439.aspx), put
the spin control inside the associated "buddy" edit control and not near it.

Closes #12297.

Closes https://github.com/wxWidgets/wxWidgets/pull/410
This commit is contained in:
Cătălin Răceanu 2017-02-11 23:37:35 +01:00 committed by Vadim Zeitlin
parent 8e47b3ca97
commit 05b980aba1
4 changed files with 64 additions and 55 deletions

View File

@ -141,6 +141,7 @@ wxMSW:
- Add support for building with Microsoft Visual Studio 2017 (Tobias Taschner).
- Enable wxStackWalker in MinGW64 builds.
- Fix crash when using wxCHMHelpController() in 64 bit builds (Xlord2).
- Fix wxSpinCtrl appearance: show arrows inside the control (Catalin Raceanu).
- Fix MDI menu display after failure to create a child frame (troelsk).
- Fix wxScreenDC::GetSize() with multiple monitors (iwbnwif).
- Fix background colour returned by wxTextCtrl::GetStyle() (Andreas Falkenhahn).

View File

@ -159,6 +159,9 @@ private:
// hexadecimal prefix, ...) in it.
void UpdateBuddyStyle();
// Determine the (horizontal) pixel overlap between the spin button
// (up-down control) and the text control (buddy window).
int GetOverlap() const;
wxDECLARE_DYNAMIC_CLASS(wxSpinCtrl);
wxDECLARE_EVENT_TABLE();

View File

@ -94,7 +94,8 @@ bool wxSpinButton::Create(wxWindow *parent,
// translate the styles
DWORD wstyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | /* WS_CLIPSIBLINGS | */
UDS_NOTHOUSANDS | // never useful, sometimes harmful
UDS_SETBUDDYINT; // it doesn't harm if we don't have buddy
UDS_ALIGNRIGHT | // these styles are effectively used only
UDS_SETBUDDYINT; // by wxSpinCtrl but do no harm otherwise
if ( m_windowStyle & wxCLIP_SIBLINGS )
wstyle |= WS_CLIPSIBLINGS;

View File

@ -55,15 +55,6 @@ wxEND_EVENT_TABLE()
#define GetBuddyHwnd() (HWND)(m_hwndBuddy)
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// the margin between the up-down control and its buddy (can be arbitrary,
// choose what you like - or may be decide during run-time depending on the
// font size?)
static const int MARGIN_BETWEEN = 1;
// ---------------------------------------------------------------------------
// global vars
@ -278,9 +269,7 @@ bool wxSpinCtrl::Create(wxWindow *parent,
int min, int max, int initial,
const wxString& name)
{
// before using DoGetBestSize(), have to set style to let the base class
// know whether this is a horizontal or vertical control (we're always
// vertical)
// set style for the base class
style |= wxSP_VERTICAL;
if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT )
@ -301,27 +290,6 @@ bool wxSpinCtrl::Create(wxWindow *parent,
else if ( style & wxALIGN_CENTER )
msStyle |= ES_CENTER;
// calculate the sizes: the size given is the total size for both controls
// and we need to fit them both in the given width (height is the same)
wxSize sizeText(size), sizeBtn(size);
sizeBtn.x = wxSpinButton::DoGetBestSize().x;
if ( sizeText.x <= 0 )
{
// DEFAULT_ITEM_WIDTH is the default width for the text control
sizeText.x = FromDIP(DEFAULT_ITEM_WIDTH) + MARGIN_BETWEEN + sizeBtn.x;
}
sizeText.x -= sizeBtn.x + MARGIN_BETWEEN;
if ( sizeText.x <= 0 )
{
wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ")
wxS("at least %d pixels needed."),
name, size.x, sizeBtn.x + MARGIN_BETWEEN + 1);
}
wxPoint posBtn(pos);
posBtn.x += sizeText.x + MARGIN_BETWEEN;
// we must create the text control before the spin button for the purpose
// of the dialog navigation: if there is a static text just before the spin
// control, activating it by Alt-letter should give focus to the text
@ -353,7 +321,7 @@ bool wxSpinCtrl::Create(wxWindow *parent,
// create the spin button
if ( !wxSpinButton::Create(parent, id, posBtn, sizeBtn, style, name) )
if ( !wxSpinButton::Create(parent, id, wxPoint(0, 0), wxSize(0, 0), style, name) )
{
return false;
}
@ -366,28 +334,46 @@ bool wxSpinCtrl::Create(wxWindow *parent,
m_wndProcBuddy = (WXFARPROC)wxSetWindowProc(GetBuddyHwnd(),
wxBuddyTextWndProc);
// associate the text window with the spin button
(void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)m_hwndBuddy, 0);
// set up fonts and colours (This is nomally done in MSWCreateControl)
InheritAttributes();
if (!m_hasFont)
SetFont(GetDefaultAttributes().font);
// set the size of the text window - can do it only now, because we
// couldn't call DoGetBestSize() before as font wasn't set
if ( sizeText.y <= 0 )
// finally deal with the size, now that both windows are created and the
// font is set
const wxSize sizeBtn = wxSpinButton::DoGetBestSize();
const int effectiveBtnWidth = sizeBtn.x - 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) + effectiveBtnWidth;
}
else if ( sizeCtrl.x <= effectiveBtnWidth )
{
wxLogDebug(wxS("wxSpinCtrl \"%s\": initial width %d is too small, ")
wxS("at least %d pixels needed."),
name, size.x, effectiveBtnWidth);
}
{
}
// adjust an invalid height for text control
if ( sizeCtrl.y <= 0 )
{
int cx, cy;
wxGetCharSize(GetHWND(), &cx, &cy, GetFont());
sizeText.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
sizeCtrl.y = EDIT_HEIGHT_FROM_CHAR_HEIGHT(cy);
}
SetInitialSize(size);
SetInitialSize(sizeCtrl);
(void)::ShowWindow(GetBuddyHwnd(), SW_SHOW);
// associate the text window with the spin button
(void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)m_hwndBuddy, 0);
// If the initial text value is actually a number, it overrides the
// "initial" argument specified later.
long initialFromText;
@ -632,7 +618,7 @@ bool wxSpinCtrl::Reparent(wxWindowBase *newParent)
// create and initialize the new one
if ( !wxSpinButton::Create(GetParent(), GetId(),
rect.GetPosition(), rect.GetSize(),
wxPoint(0, 0), wxSize(0, 0), // it will have a buddy
GetWindowStyle(), GetName()) )
return false;
@ -640,14 +626,14 @@ bool wxSpinCtrl::Reparent(wxWindowBase *newParent)
wxSpinButton::SetValue(GetValue());
SetRange(m_min, m_max);
// also set the size again with wxSIZE_ALLOW_MINUS_ONE flag: this is
// necessary if our original position used -1 for either x or y
SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
// associate it with the buddy control again
::SetParent(GetBuddyHwnd(), GetHwndOf(GetParent()));
(void)::SendMessage(GetHwnd(), UDM_SETBUDDY, (WPARAM)GetBuddyHwnd(), 0);
// also set the size again with wxSIZE_ALLOW_MINUS_ONE flag: this is
// necessary if our original position used -1 for either x or y
SetSize(rect, wxSIZE_ALLOW_MINUS_ONE);
return true;
}
@ -732,6 +718,21 @@ bool wxSpinCtrl::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM *re
// size calculations
// ----------------------------------------------------------------------------
int wxSpinCtrl::GetOverlap() const
{
if ( !GetHwnd() )
{
// We can be called from GetSizeFromTextSize() before the window is
// created and still need to return something reasonable in this case,
// so return the overlap equal to the default border size.
return FromDIP(2);
}
// The sign here is correct because the button is positioned inside its
// buddy window.
return wxGetWindowRect(m_hwndBuddy).right - wxGetWindowRect(GetHwnd()).left;
}
wxSize wxSpinCtrl::DoGetBestSize() const
{
return DoGetSizeFromTextSize(DEFAULT_ITEM_WIDTH);
@ -748,7 +749,7 @@ wxSize wxSpinCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const
// that's too big. So never use the height calculated
// from wxSpinButton::DoGetBestSize().
wxSize tsize(xlen + sizeBtn.x + MARGIN_BETWEEN + 3*y/10 + 10,
wxSize tsize(xlen + sizeBtn.x - GetOverlap(),
EDIT_HEIGHT_FROM_CHAR_HEIGHT(y));
// Check if the user requested a non-standard height.
@ -760,8 +761,11 @@ wxSize wxSpinCtrl::DoGetSizeFromTextSize(int xlen, int ylen) const
void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
{
// make sure the given width will be the total of both controls
const int overlap = GetOverlap();
int widthBtn = wxSpinButton::DoGetBestSize().x;
int widthText = width - widthBtn - MARGIN_BETWEEN;
int widthText = width - widthBtn + overlap;
if ( widthText < 0 )
{
// This can happen during the initial window layout when it's total
@ -775,6 +779,9 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
widthText = 0;
}
if ( widthBtn > width )
widthBtn = width;
// Because both subcontrols are positioned relatively
// to the parent which can have different layout direction
// then our control, we need to mirror their positions manually.
@ -785,9 +792,7 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
DoMoveSibling(m_hwndBuddy, x, y, widthText, height);
// 2) The button window
if ( widthText > 0 )
x += widthText + MARGIN_BETWEEN;
wxSpinButton::DoMoveWindow(x, y, widthBtn, height);
wxSpinButton::DoMoveWindow(x + widthText - overlap, y, widthBtn, height);
}
else
{
@ -796,8 +801,7 @@ void wxSpinCtrl::DoMoveWindow(int x, int y, int width, int height)
wxSpinButton::DoMoveWindow(x, y, widthBtn, height);
// 2) The buddy window
x += widthBtn + MARGIN_BETWEEN;
DoMoveSibling(m_hwndBuddy, x, y, widthText, height);
DoMoveSibling(m_hwndBuddy, x + widthBtn - overlap, y, widthText, height);
}
}