diff --git a/include/wx/private/window.h b/include/wx/private/window.h new file mode 100644 index 0000000000..3084665206 --- /dev/null +++ b/include/wx/private/window.h @@ -0,0 +1,39 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/private/window.h +// Purpose: misc wxWindow helpers +// Author: Vaclav Slavik +// Created: 2010-01-21 +// RCS-ID: $Id$ +// Copyright: (c) 2010 Vaclav Slavik +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_PRIVATE_WINDOW_H_ +#define _WX_PRIVATE_WINDOW_H_ + +#include "wx/gdicmn.h" + +namespace wxPrivate +{ + +// Windows' computes dialog units using average character width over upper- +// and lower-case ASCII alphabet and not using the average character width +// metadata stored in the font; see +// http://support.microsoft.com/default.aspx/kb/145994 for detailed discussion. +// +// This helper function computes font dimensions in the same way. It works with +// either wxDC or wxWindow argument. +template +inline wxSize GetAverageASCIILetterSize(const T& of_what) +{ + const wxStringCharType *TEXT_TO_MEASURE = + wxS("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + + wxSize s = of_what.GetTextExtent(TEXT_TO_MEASURE); + s.x = (s.x / 26 + 1) / 2; + return s; +} + +} // namespace wxPrivate + +#endif // _WX_PRIVATE_WINDOW_H_ diff --git a/include/wx/window.h b/include/wx/window.h index e4f15868b4..9218362735 100644 --- a/include/wx/window.h +++ b/include/wx/window.h @@ -1736,6 +1736,8 @@ private: // explicitly disabled with SetAutoLayout(false) void InternalOnSize(wxSizeEvent& event); + // base for dialog unit conversion, i.e. average character size + wxSize GetDlgUnitBase() const; // the stack of windows which have captured the mouse static struct WXDLLIMPEXP_FWD_CORE wxWindowNext *ms_winCaptureNext; diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index a9c2b44505..e67ee6a67b 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -73,6 +73,11 @@ #endif #include "wx/platinfo.h" +#include "wx/private/window.h" + +#ifdef __WXMSW__ + #include "wx/msw/wrapwin.h" +#endif // Windows List WXDLLIMPEXP_DATA_CORE(wxWindowList) wxTopLevelWindows; @@ -2461,24 +2466,57 @@ void wxWindowBase::DoUpdateWindowUI(wxUpdateUIEvent& event) // dialog units translations // ---------------------------------------------------------------------------- +// Windows' computes dialog units using average character width over upper- +// and lower-case ASCII alphabet and not using the average character width +// metadata stored in the font; see +// http://support.microsoft.com/default.aspx/kb/145994 for detailed discussion. +// It's important that we perform the conversion in identical way, because +// dialog units natively exist only on Windows and Windows HIG is expressed +// using them. +wxSize wxWindowBase::GetDlgUnitBase() const +{ + const wxWindow *parent = wxGetTopLevelParent((wxWindow*)this); + + if ( !parent->m_font.IsOk() ) + { + // Default GUI font is used. This is the most common case, so + // cache the results. + static wxSize s_defFontSize; + if ( s_defFontSize.x == 0 ) + s_defFontSize = wxPrivate::GetAverageASCIILetterSize(*parent); + return s_defFontSize; + } + else + { + // Custom font, we always need to compute the result + return wxPrivate::GetAverageASCIILetterSize(*parent); + } +} + wxPoint wxWindowBase::ConvertPixelsToDialog(const wxPoint& pt) const { + const wxSize base = GetDlgUnitBase(); + + // NB: wxMulDivInt32() is used, because it correctly rounds the result + wxPoint pt2 = wxDefaultPosition; if (pt.x != wxDefaultCoord) - pt2.x = (int) ((pt.x * 4) / GetCharWidth()); + pt2.x = wxMulDivInt32(pt.x, 4, base.x); if (pt.y != wxDefaultCoord) - pt2.y = (int) ((pt.y * 8) / GetCharHeight()); + pt2.y = wxMulDivInt32(pt.y, 8, base.y); return pt2; } wxPoint wxWindowBase::ConvertDialogToPixels(const wxPoint& pt) const { + const wxSize base = GetDlgUnitBase(); + wxPoint pt2 = wxDefaultPosition; if (pt.x != wxDefaultCoord) - pt2.x = (int) ((pt.x * GetCharWidth()) / 4); + pt2.x = wxMulDivInt32(pt.x, base.x, 4); if (pt.y != wxDefaultCoord) - pt2.y = (int) ((pt.y * GetCharHeight()) / 8); + pt2.y = wxMulDivInt32(pt.y, base.y, 8); return pt2; } diff --git a/src/msw/button.cpp b/src/msw/button.cpp index ad3f9a452f..2f68d8b362 100644 --- a/src/msw/button.cpp +++ b/src/msw/button.cpp @@ -44,6 +44,7 @@ #include "wx/msw/private.h" #include "wx/msw/private/button.h" #include "wx/msw/private/dc.h" +#include "wx/private/window.h" using namespace wxMSWImpl; @@ -662,16 +663,20 @@ wxSize wxButtonBase::GetDefaultSize() wxScreenDC dc; dc.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - // the size of a standard button in the dialog units is 50x14, - // translate this to pixels - // NB1: the multipliers come from the Windows convention - // NB2: the extra +1/+2 were needed to get the size be the same as the - // size of the buttons in the standard dialog - I don't know how - // this happens, but on my system this size is 75x23 in pixels and - // 23*8 isn't even divisible by 14... Would be nice to understand - // why these constants are needed though! - s_sizeBtn.x = (50 * (dc.GetCharWidth() + 1))/4; - s_sizeBtn.y = ((14 * dc.GetCharHeight()) + 2)/8; + // The size of a standard button in the dialog units is 50x14, + // translate this to pixels. + // + // Windows' computes dialog units using average character width over + // upper- and lower-case ASCII alphabet and not using the average + // character width metadata stored in the font; see + // http://support.microsoft.com/default.aspx/kb/145994 for detailed + // discussion. + // + // NB: wxMulDivInt32() is used, because it correctly rounds the result + + const wxSize base = wxPrivate::GetAverageASCIILetterSize(dc); + s_sizeBtn.x = wxMulDivInt32(50, base.x, 4); + s_sizeBtn.y = wxMulDivInt32(14, base.y, 8); } return s_sizeBtn;