From b0d94659218636df6134c879355dca7e776bddd9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 31 Oct 2021 20:54:58 +0100 Subject: [PATCH] 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. --- include/wx/bmpbndl.h | 6 ++++ include/wx/msw/notebook.h | 11 ++----- include/wx/withimages.h | 59 +++++++++++++++++++++++++++++++++++ interface/wx/withimages.h | 22 +++++++++++++ samples/notebook/notebook.cpp | 19 ++++------- samples/notebook/notebook.h | 2 +- src/common/bmpbndl.cpp | 31 ++++++++++++++++-- src/msw/notebook.cpp | 16 +++++++--- 8 files changed, 138 insertions(+), 28 deletions(-) diff --git a/include/wx/bmpbndl.h b/include/wx/bmpbndl.h index 6872a142bd..646c006b72 100644 --- a/include/wx/bmpbndl.h +++ b/include/wx/bmpbndl.h @@ -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& 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& bundles); + private: typedef wxObjectDataPtr wxBitmapBundleImplPtr; diff --git a/include/wx/msw/notebook.h b/include/wx/msw/notebook.h index b6a05ce637..e2c2d31915 100644 --- a/include/wx/msw/notebook.h +++ b/include/wx/msw/notebook.h @@ -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 diff --git a/include/wx/withimages.h b/include/wx/withimages.h index 9c4d7e785c..d9a6f929ca 100644 --- a/include/wx/withimages.h +++ b/include/wx/withimages.h @@ -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 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(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; diff --git a/interface/wx/withimages.h b/interface/wx/withimages.h index 011718b22f..e5b6f4d008 100644 --- a/interface/wx/withimages.h +++ b/interface/wx/withimages.h @@ -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& 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); diff --git a/samples/notebook/notebook.cpp b/samples/notebook/notebook.cpp index b009ad5f5f..f5da371790 100644 --- a/samples/notebook/notebook.cpp +++ b/samples/notebook/notebook.cpp @@ -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 ) diff --git a/samples/notebook/notebook.h b/samples/notebook/notebook.h index 05857c7216..ac7d13197c 100644 --- a/samples/notebook/notebook.h +++ b/samples/notebook/notebook.h @@ -131,7 +131,7 @@ private: wxBoxSizer *m_sizerFrame; - wxImageList *m_imageList; + wxBookCtrlBase::Images m_images; wxDECLARE_EVENT_TABLE(); }; diff --git a/src/common/bmpbndl.cpp b/src/common/bmpbndl.cpp index 7a98fde781..567326bc57 100644 --- a/src/common/bmpbndl.cpp +++ b/src/common/bmpbndl.cpp @@ -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& 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 // ============================================================================ diff --git a/src/msw/notebook.cpp b/src/msw/notebook.cpp index 5f281aaaae..2b4f20b006 100644 --- a/src/msw/notebook.cpp +++ b/src/msw/notebook.cpp @@ -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); } // ----------------------------------------------------------------------------