Add wxWithImages::SetImages() and update wxMSW wxNotebook for it

This new function will allow selecting the bitmap of the most suitable
size and automatically react to DPI scale changes (although this hasn't
been implemented yet) in all controls using image lists.

For now, only wxNotebook in wxMSW has been updated to work with it, the
other classes and ports will be updated to override OnImagesChanged()
instead of SetImageList() later.

Also update the notebook sample to use SetImages() rather than
SetImageList() -- which means that it doesn't show the icons any longer
in non-MSW ports, which haven't been updated yet.
This commit is contained in:
Vadim Zeitlin 2021-10-31 20:54:58 +01:00
parent 55819925dc
commit b0d9465921
8 changed files with 138 additions and 28 deletions

View File

@ -15,6 +15,7 @@
#include "wx/vector.h"
class wxBitmapBundleImpl;
class WXDLLIMPEXP_FWD_CORE wxImageList;
class WXDLLIMPEXP_FWD_CORE wxWindow;
// It should be possible to implement SVG rasterizing without raw bitmap
@ -132,6 +133,11 @@ public:
const wxVector<wxBitmapBundle>& bundles,
const wxSize& sizeDefault);
// Create wxImageList and fill it with the images from the given bundles in
// the sizes appropriate for the DPI scaling used for the specified window.
static wxImageList*
CreateImageList(wxWindow* win, const wxVector<wxBitmapBundle>& bundles);
private:
typedef wxObjectDataPtr<wxBitmapBundleImpl> wxBitmapBundleImplPtr;

View File

@ -62,14 +62,6 @@ public:
bool SetPageText(size_t nPage, const wxString& strText) wxOVERRIDE;
wxString GetPageText(size_t nPage) const wxOVERRIDE;
// image list stuff: each page may have an image associated with it. All
// the images belong to an image list, so you have to
// 1) create an image list
// 2) associate it with the notebook
// 3) set for each page it's image
// associate image list with a control
void SetImageList(wxImageList* imageList) wxOVERRIDE;
// sets/returns item's image index in the current image list
int GetPageImage(size_t nPage) const wxOVERRIDE;
bool SetPageImage(size_t nPage, int nImage) wxOVERRIDE;
@ -161,6 +153,9 @@ protected:
// remove one page from the notebook, without deleting
virtual wxNotebookPage *DoRemovePage(size_t nPage) wxOVERRIDE;
// update the image list used by the native control
virtual void OnImagesChanged() wxOVERRIDE;
// get the page rectangle for the current notebook size
//
// returns empty rectangle if an error occurs, do test for it

View File

@ -11,8 +11,12 @@
#define _WX_WITHIMAGES_H_
#include "wx/defs.h"
#include "wx/bmpbndl.h"
#include "wx/icon.h"
#include "wx/imaglist.h"
#include "wx/vector.h"
class WXDLLIMPEXP_FWD_CORE wxWindow;
// ----------------------------------------------------------------------------
// wxWithImages: mix-in for classes using indices for image access
@ -26,6 +30,8 @@ public:
NO_IMAGE = -1
};
typedef wxVector<wxBitmapBundle> Images;
wxWithImages()
{
m_imageList = NULL;
@ -40,6 +46,12 @@ public:
// Return the number of images, possibly 0.
int GetImageCount() const
{
if ( !m_images.empty() )
{
// Cast is safe, we don't risk having more than INT_MAX images.
return static_cast<int>(m_images.size());
}
return m_imageList ? m_imageList->GetImageCount() : 0;
}
@ -49,11 +61,28 @@ public:
return GetImageCount() != 0;
}
// Sets the images to use.
//
// Override OnImagesChanged() in the derived class to update the actually
// shown images.
void SetImages(const Images& images)
{
m_images = images;
OnImagesChanged();
}
// Sets the image list to use, it is *not* deleted by the control.
//
// This function is virtual for compatibility, as it could be overridden in
// the existing application code, however it should not be overridden in wx
// itself, where OnImagesChanged() should be overridden instead.
virtual void SetImageList(wxImageList* imageList)
{
FreeIfNeeded();
m_imageList = imageList;
OnImagesChanged();
}
// As SetImageList() but we will delete the image list ourselves.
@ -67,6 +96,33 @@ public:
wxImageList* GetImageList() const { return m_imageList; }
protected:
// This function is called when the images associated with the control
// change, due to either SetImages() or SetImageList() being called.
//
// It ought to be pure virtual, but isn't because there could be existing
// application code inheriting from this class and not overriding it
// (because this function hadn't existed when this code was written).
virtual void OnImagesChanged() { }
// This helper function can be used from OnImagesChanged() if the derived
// class actually needs to use wxImageList: it ensures that m_imageList is
// updated from m_images, if the latter is not empty, using the images of
// the appropriate size for the given window.
void UpdateImageListIfNecessary(wxWindow* win)
{
if ( m_images.empty() )
return;
// Note that we can't just call AssignImageList() here to avoid
// infinite recursion.
FreeIfNeeded();
m_imageList = wxBitmapBundle::CreateImageList(win, m_images);
// We always own it as we created it ourselves.
m_ownsImageList = true;
}
// Return true if we have a valid image list.
bool HasImageList() const { return m_imageList != NULL; }
@ -96,6 +152,9 @@ private:
}
// The images we use: if this vector is not empty, m_imageList is not used.
Images m_images;
// The associated image list or NULL.
wxImageList* m_imageList;

View File

@ -39,9 +39,28 @@ public:
*/
bool HasImages() const;
/**
Set the images to use for the items in the control.
This function allows to specify the images to use in multiple different
resolutions, letting the control to select the appropriate one for its
DPI scaling. For this reason, it should be preferred to using the
functions taking wxImageList, which has a fixed size, in the new code.
@param images Non empty vector of bitmap bundles. Valid image indexes
for the items in this control are determined by the size of this
vector.
@since 3.1.6
*/
void SetImages(const wxVector<wxBitmapBundle>& images);
/**
Sets the image list for the page control and takes ownership of the list.
This function exists for compatibility only, please use SetImages() in
the new code.
@see wxImageList, SetImageList()
*/
void AssignImageList(wxImageList* imageList);
@ -50,6 +69,9 @@ public:
Sets the image list to use. It does not take ownership of the image
list, you must delete it yourself.
This function exists for compatibility only, please use SetImages() in
the new code.
@see wxImageList, AssignImageList()
*/
virtual void SetImageList(wxImageList* imageList);

View File

@ -397,18 +397,13 @@ MyFrame::MyFrame()
m_panel = NULL;
m_bookCtrl = NULL;
// create a dummy image list with a few icons
// use some random images for the book control pages
const wxSize imageSize(32, 32);
m_imageList = new wxImageList(imageSize.GetWidth(), imageSize.GetHeight());
m_imageList->
Add(wxArtProvider::GetIcon(wxART_INFORMATION, wxART_OTHER, imageSize));
m_imageList->
Add(wxArtProvider::GetIcon(wxART_QUESTION, wxART_OTHER, imageSize));
m_imageList->
Add(wxArtProvider::GetIcon(wxART_WARNING, wxART_OTHER, imageSize));
m_imageList->
Add(wxArtProvider::GetIcon(wxART_ERROR, wxART_OTHER, imageSize));
m_images.push_back(wxArtProvider::GetBitmapBundle(wxART_INFORMATION, wxART_OTHER, imageSize));
m_images.push_back(wxArtProvider::GetBitmapBundle(wxART_QUESTION, wxART_OTHER, imageSize));
m_images.push_back(wxArtProvider::GetBitmapBundle(wxART_WARNING, wxART_OTHER, imageSize));
m_images.push_back(wxArtProvider::GetBitmapBundle(wxART_ERROR, wxART_OTHER, imageSize));
m_panel = new wxPanel(this);
@ -442,8 +437,6 @@ MyFrame::~MyFrame()
#if USE_LOG
delete wxLog::SetActiveTarget(m_logTargetOld);
#endif // USE_LOG
delete m_imageList;
}
// DISPATCH_ON_TYPE() macro is an ugly way to write the "same" code for
@ -565,7 +558,7 @@ void MyFrame::RecreateBook()
// wxToolbook doesn't work without icons so always use them for it.
if ( m_chkShowImages || m_type == Type_Toolbook )
{
m_bookCtrl->SetImageList(m_imageList);
m_bookCtrl->SetImages(m_images);
}
if ( oldBook )

View File

@ -131,7 +131,7 @@ private:
wxBoxSizer *m_sizerFrame;
wxImageList *m_imageList;
wxBookCtrlBase::Images m_images;
wxDECLARE_EVENT_TABLE();
};

View File

@ -22,9 +22,10 @@
#endif // WX_PRECOMP
#include "wx/bmpbndl.h"
#include "wx/icon.h"
#include "wx/window.h"
#include "wx/filename.h"
#include "wx/icon.h"
#include "wx/imaglist.h"
#include "wx/window.h"
#include "wx/private/bmpbndl.h"
@ -556,6 +557,32 @@ wxBitmapBundle::GetConsensusSizeFor(wxWindow* win,
return sizePreferred;
}
/* static */
wxImageList*
wxBitmapBundle::CreateImageList(wxWindow* win,
const wxVector<wxBitmapBundle>& bundles)
{
wxCHECK_MSG( win, NULL, "must have a valid window" );
wxCHECK_MSG( !bundles.empty(), NULL, "should have some images" );
// We arbitrarily choose the default size of the first bundle as the
// default size for the image list too, as it's not clear what else could
// we do here. Note that this size is only used to break the tie in case
// the same number of bundles prefer two different sizes, so it's not going
// to matter at all in most cases.
const wxSize
size = GetConsensusSizeFor(win, bundles, bundles[0].GetDefaultSize());
wxImageList* const iml = new wxImageList(size.x, size.y);
for ( size_t n = 0; n < bundles.size(); ++n )
{
iml->Add(bundles[n].GetBitmap(size));
}
return iml;
}
// ============================================================================
// wxBitmapBundleImpl implementation
// ============================================================================

View File

@ -447,14 +447,22 @@ bool wxNotebook::SetPageImage(size_t nPage, int nImage)
return TabCtrl_SetItem(GetHwnd(), nPage, &tcItem) != 0;
}
void wxNotebook::SetImageList(wxImageList* imageList)
void wxNotebook::OnImagesChanged()
{
wxNotebookBase::SetImageList(imageList);
HIMAGELIST himl;
if ( imageList )
if ( HasImages() )
{
(void) TabCtrl_SetImageList(GetHwnd(), GetHimagelistOf(imageList));
UpdateImageListIfNecessary(this);
himl = GetHimagelistOf(GetImageList());
}
else
{
himl = 0;
}
(void) TabCtrl_SetImageList(GetHwnd(), himl);
}
// ----------------------------------------------------------------------------