diff --git a/docs/changes.txt b/docs/changes.txt index f058577236..c723c17e1d 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -471,6 +471,7 @@ All (GUI): - Fix multiple item selection in generic wxTreeCtrl (Igor Korot). - Implement wxMenuBar::IsEnabledTop() for all major ports (Igor Korot). - Implement best size calculation for report mode wxListCtrl. +- Fix setting of the frame icon when using non-standard icon sizes (vid). GTK: diff --git a/include/wx/iconbndl.h b/include/wx/iconbndl.h index e80edb4d9f..0955e71654 100644 --- a/include/wx/iconbndl.h +++ b/include/wx/iconbndl.h @@ -28,6 +28,23 @@ WX_DECLARE_EXPORTED_OBJARRAY(wxIcon, wxIconArray); class WXDLLIMPEXP_CORE wxIconBundle : public wxGDIObject { public: + // Flags that determine what happens if GetIcon() doesn't find the icon of + // exactly the requested size. + enum + { + // Return invalid icon if exact size is not found. + FALLBACK_NONE = 0, + + // Return the icon of the system icon size if exact size is not found. + // May be combined with other non-NONE enum elements to determine what + // happens if the system icon size is not found neither. + FALLBACK_SYSTEM = 1, + + // Return the icon of closest larger size or, if there is no icon of + // larger size in the bundle, the closest icon of smaller size. + FALLBACK_NEAREST_LARGER = 2 + }; + // default constructor wxIconBundle(); @@ -60,13 +77,13 @@ public: void AddIcon(const wxIcon& icon); // returns the icon with the given size; if no such icon exists, - // returns the icon with size wxSYS_ICON_[XY]; if no such icon exists, - // returns the first icon in the bundle - wxIcon GetIcon(const wxSize& size) const; + // behavior is specified by the flags. + wxIcon GetIcon(const wxSize& size, int flags = FALLBACK_SYSTEM) const; // equivalent to GetIcon(wxSize(size, size)) - wxIcon GetIcon(wxCoord size = wxDefaultCoord) const - { return GetIcon(wxSize(size, size)); } + wxIcon GetIcon(wxCoord size = wxDefaultCoord, + int flags = FALLBACK_SYSTEM) const + { return GetIcon(wxSize(size, size), flags); } // returns the icon exactly of the specified size or wxNullIcon if no icon // of exactly given size are available diff --git a/interface/wx/iconbndl.h b/interface/wx/iconbndl.h index 28c54ee38c..c33009d0ef 100644 --- a/interface/wx/iconbndl.h +++ b/interface/wx/iconbndl.h @@ -21,6 +21,28 @@ class wxIconBundle : public wxGDIObject { public: + /** + The elements of this enum determine what happens if GetIcon() doesn't + find the icon of exactly the requested size. + + @since 2.9.4 + */ + enum + { + /// Return invalid icon if exact size is not found. + FALLBACK_NONE = 0, + + /// Return the icon of the system icon size if exact size is not found. + /// May be combined with other non-NONE enum elements to determine what + /// happens if the system icon size is not found neither. + FALLBACK_SYSTEM = 1, + + /// Return the icon of closest larger size or, if there is no icon of + /// larger size in the bundle, the closest icon of smaller size. + FALLBACK_NEAREST_LARGER = 2 + }; + + /** Default ctor. */ @@ -85,19 +107,31 @@ public: void AddIcon(const wxIcon& icon); /** - Returns the icon with the given size; if no such icon exists, returns - the icon with size @c wxSYS_ICON_X and @c wxSYS_ICON_Y; if no such icon - exists, returns the first icon in the bundle. + Returns the icon with the given size. - If size = wxDefaultSize, returns the icon with size @c wxSYS_ICON_X and - @c wxSYS_ICON_Y. + If @a size is ::wxDefaultSize, it is interpreted as the standard system + icon size, i.e. the size returned by wxSystemSettings::GetMetric() for + @c wxSYS_ICON_X and @c wxSYS_ICON_Y. + + If the bundle contains an icon with exactly the requested size, it's + always returned. Otherwise, the behaviour depends on the flags. If only + ::FALLBACK_NONE is given, the function returns an invalid icon. If + ::FALLBACK_SYSTEM is given, it tries to find the icon of standard + system size, regardless of the size passed as parameter. Otherwise, or + if the icon system size is not found neither, but + ::FALLBACK_NEAREST_LARGER flag is specified, the function returns the + smallest icon of the size larger than the requested one or, if this + fails too, just the icon closest to the specified size. + + The @a flags parameter is available only since wxWidgets 2.9.4. */ - wxIcon GetIcon(const wxSize& size) const; + wxIcon GetIcon(const wxSize& size, int flags = FALLBACK_SYSTEM) const; /** Same as @code GetIcon( wxSize( size, size ) ) @endcode. */ - wxIcon GetIcon(wxCoord size = wxDefaultCoord) const; + wxIcon GetIcon(wxCoord size = wxDefaultCoord, + int flags = FALLBACK_SYSTEM) const; /** Returns the icon with exactly the given size or ::wxNullIcon if this diff --git a/src/common/iconbndl.cpp b/src/common/iconbndl.cpp index 186d4df0cc..d589943453 100644 --- a/src/common/iconbndl.cpp +++ b/src/common/iconbndl.cpp @@ -193,49 +193,91 @@ void wxIconBundle::AddIcon(wxInputStream& stream, wxBitmapType type) #endif // wxUSE_STREAMS && wxUSE_IMAGE -wxIcon wxIconBundle::GetIcon(const wxSize& size) const +wxIcon wxIconBundle::GetIcon(const wxSize& size, int flags) const { + wxASSERT( size == wxDefaultSize || (size.x >= 0 && size.y > 0) ); + + // We need the standard system icon size when using FALLBACK_SYSTEM. + wxCoord sysX = 0, + sysY = 0; + if ( flags & FALLBACK_SYSTEM ) + { + sysX = wxSystemSettings::GetMetric(wxSYS_ICON_X); + sysY = wxSystemSettings::GetMetric(wxSYS_ICON_Y); + } + + // If size == wxDefaultSize, we use system default icon size by convention. + wxCoord sizeX = size.x; + wxCoord sizeY = size.y; + if ( size == wxDefaultSize ) + { + wxASSERT_MSG( flags == FALLBACK_SYSTEM, + wxS("Must have valid size if not using FALLBACK_SYSTEM") ); + + sizeX = sysX; + sizeY = sysY; + } + + // Iterate over all icons searching for the exact match or the closest icon + // for FALLBACK_NEAREST_LARGER. + wxIcon iconBest; + int bestDiff = 0; + bool bestIsLarger = false; + bool bestIsSystem = false; + const size_t count = GetIconCount(); - // optimize for the common case of icon bundles containing one icon only - wxIcon iconBest; - switch ( count ) + const wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons; + for ( size_t i = 0; i < count; i++ ) { - case 0: - // nothing to do, iconBest is already invalid + const wxIcon& icon = iconArray[i]; + if ( !icon.IsOk() ) + continue; + wxCoord sx = icon.GetWidth(), + sy = icon.GetHeight(); + + // Exact match ends search immediately in any case. + if ( sx == sizeX && sy == sizeY ) + { + iconBest = icon; break; + } - case 1: - iconBest = M_ICONBUNDLEDATA->m_icons[0]; - break; - - default: - // there is more than one icon, find the best match: - wxCoord sysX = wxSystemSettings::GetMetric( wxSYS_ICON_X ), - sysY = wxSystemSettings::GetMetric( wxSYS_ICON_Y ); - - const wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons; - for ( size_t i = 0; i < count; i++ ) + if ( flags & FALLBACK_SYSTEM ) + { + if ( sx == sysX && sy == sysY ) { - const wxIcon& icon = iconArray[i]; - wxCoord sx = icon.GetWidth(), - sy = icon.GetHeight(); - - // if we got an icon of exactly the requested size, we're done - if ( sx == size.x && sy == size.y ) - { - iconBest = icon; - break; - } - - // the best icon is by default (arbitrarily) the first one but - // if we find a system-sized icon, take it instead - if ((sx == sysX && sy == sysY) || !iconBest.IsOk()) - iconBest = icon; + iconBest = icon; + bestIsSystem = true; + continue; } + } + + if ( !bestIsSystem && (flags & FALLBACK_NEAREST_LARGER) ) + { + bool iconLarger = (sx >= sizeX) && (sy >= sizeY); + int iconDiff = abs(sx - sizeX) + abs(sy - sizeY); + + // Use current icon as candidate for the best icon, if either: + // - we have no candidate yet + // - we have no candidate larger than desired size and current icon is + // - current icon is closer to desired size than candidate + if ( !iconBest.IsOk() || + (!bestIsLarger && iconLarger) || + (iconLarger && (iconDiff < bestDiff)) ) + { + iconBest = icon; + bestIsLarger = iconLarger; + bestDiff = iconDiff; + continue; + } + } } #if defined( __WXMAC__ ) && wxOSX_USE_CARBON + if (!iconBest.IsOk()) + return wxNullIcon; + return wxIcon(iconBest.GetHICON(), size); #else return iconBest; @@ -244,14 +286,7 @@ wxIcon wxIconBundle::GetIcon(const wxSize& size) const wxIcon wxIconBundle::GetIconOfExactSize(const wxSize& size) const { - wxIcon icon = GetIcon(size); - if ( icon.IsOk() && - (icon.GetWidth() != size.x || icon.GetHeight() != size.y) ) - { - icon = wxNullIcon; - } - - return icon; + return GetIcon(size, FALLBACK_NONE); } void wxIconBundle::AddIcon(const wxIcon& icon) diff --git a/src/msw/toplevel.cpp b/src/msw/toplevel.cpp index 8219f12a43..7adf8c6925 100644 --- a/src/msw/toplevel.cpp +++ b/src/msw/toplevel.cpp @@ -1100,18 +1100,7 @@ bool wxTopLevelWindowMSW::DoSelectAndSetIcon(const wxIconBundle& icons, { const wxSize size(::GetSystemMetrics(smX), ::GetSystemMetrics(smY)); - // Try the exact size first. - wxIcon icon = icons.GetIconOfExactSize(size); - - if ( !icon.IsOk() ) - { - // If we didn't find any, set at least some icon: it will look scaled - // and ugly but in practice it's impossible to prevent this because not - // everyone can provide the icons in all sizes used by all versions of - // Windows in all DPIs (this would include creating them in at least - // 14, 16, 22, 32, 48, 64 and 128 pixel sizes). - icon = icons.GetIcon(size); - } + wxIcon icon = icons.GetIcon(size, wxIconBundle::FALLBACK_NEAREST_LARGER); if ( !icon.IsOk() ) return false;