From 27e80f81b4bac4d6dc2a73cec5f26ec1bcb5c5dd Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 25 Aug 2023 17:58:33 +0200 Subject: [PATCH] Add high DPI support to generic wxAnimationCtrl Allow specifying multiple animation versions, for different resolutions, when setting the animation to use via wxAnimationBundle class which is a much simpler version of wxBitmapBundle used elsewhere. This is not implemented for the native GTK version yet. Update the sample, even though the difference in it is not really noticeable as the "high DPI" throbber is just a scaled up version of the existing standard DPI animation produced using gifsicle. --- include/wx/animate.h | 67 ++++++++++++++++++++++- include/wx/generic/animate.h | 2 +- include/wx/gtk/animate.h | 2 +- interface/wx/animate.h | 94 +++++++++++++++++++++++++++++++- samples/animate/Makefile.in | 2 +- samples/animate/anitest.bkl | 2 +- samples/animate/anitest.cpp | 15 ++++- samples/animate/makefile.gcc | 2 +- samples/animate/makefile.vc | 2 +- samples/animate/throbber_2x.gif | Bin 0 -> 6254 bytes src/common/animatecmn.cpp | 25 +++++++++ src/generic/animateg.cpp | 22 ++++++-- src/gtk/animate.cpp | 19 +++++-- 13 files changed, 235 insertions(+), 19 deletions(-) create mode 100644 samples/animate/throbber_2x.gif diff --git a/include/wx/animate.h b/include/wx/animate.h index 04faedb0b2..9a51e85d23 100644 --- a/include/wx/animate.h +++ b/include/wx/animate.h @@ -20,6 +20,8 @@ #include "wx/timer.h" #include "wx/bmpbndl.h" +#include + class WXDLLIMPEXP_FWD_CORE wxAnimation; class wxAnimationImpl; @@ -80,6 +82,64 @@ private: }; +// ---------------------------------------------------------------------------- +// wxAnimationBundle is one or more animations in different resolutions +// ---------------------------------------------------------------------------- + +using wxAnimations = std::vector; + +// This class is conceptually similar to wxBitmapBundle but much simpler. +class WXDLLIMPEXP_CORE wxAnimationBundle +{ +public: + // Default ctor, call Add() later. + wxAnimationBundle() = default; + + // Implicit ctor for backwards compatibility: this allows to pass a single + // wxAnimation to wxAnimationCtrl::SetAnimation(), just as before. + wxAnimationBundle(const wxAnimation& anim) + { + // Allow the animation to be invalid here, also for compatibility. + if ( anim.IsOk() ) + m_animations.push_back(anim); + } + + // Another implicit ctor for backwards compatibility: this one allows to + // create an animation from the given file. + wxAnimationBundle(const wxString& filename, + wxAnimationType type = wxANIMATION_TYPE_ANY) + { + wxAnimation anim(filename, type); + if ( anim.IsOk() ) + m_animations.push_back(anim); + } + + wxAnimationBundle(const wxAnimationBundle&) = default; + wxAnimationBundle& operator=(const wxAnimationBundle&) = default; + + ~wxAnimationBundle() = default; + + + // Add an animation in another, bigger, size. + void Add(const wxAnimation& anim); + + void Add(const wxString& filename, + wxAnimationType type = wxANIMATION_TYPE_ANY) + { + Add(wxAnimation(filename, type)); + } + + // Check if this bundle contains any animations (if it has any, they must + // be valid). + bool IsOk() const { return !m_animations.empty(); } + + // Just provide access to all the elements. + const wxAnimations& GetAll() const { return m_animations; } + +private: + wxAnimations m_animations; +}; + // ---------------------------------------------------------------------------- // wxAnimationCtrlBase // ---------------------------------------------------------------------------- @@ -102,7 +162,7 @@ public: virtual bool Load(wxInputStream& stream, wxAnimationType type = wxANIMATION_TYPE_ANY) = 0; - virtual void SetAnimation(const wxAnimation &anim) = 0; + virtual void SetAnimation(const wxAnimationBundle& animations) = 0; wxAnimation GetAnimation() const { return m_animation; } virtual bool Play() = 0; @@ -130,7 +190,10 @@ protected: wxAnimationImpl* GetAnimImpl() const { return m_animation.GetImpl(); } - // The associated animation, possibly invalid/empty. + // All animations that we have. + wxAnimations m_animations; + + // The animation being currently used, possibly invalid/empty. wxAnimation m_animation; // the inactive bitmap as it was set by the user diff --git a/include/wx/generic/animate.h b/include/wx/generic/animate.h index 6b7fc55ce9..1e2ca918fa 100644 --- a/include/wx/generic/animate.h +++ b/include/wx/generic/animate.h @@ -57,7 +57,7 @@ public: virtual bool IsPlaying() const override { return m_isPlaying; } - void SetAnimation(const wxAnimation &animation) override; + void SetAnimation(const wxAnimationBundle &animations) override; virtual void SetInactiveBitmap(const wxBitmapBundle &bmp) override; diff --git a/include/wx/gtk/animate.h b/include/wx/gtk/animate.h index 4d92c958c1..3bca130b15 100644 --- a/include/wx/gtk/animate.h +++ b/include/wx/gtk/animate.h @@ -56,7 +56,7 @@ public: // public API virtual bool LoadFile(const wxString& filename, wxAnimationType type = wxANIMATION_TYPE_ANY) override; virtual bool Load(wxInputStream& stream, wxAnimationType type = wxANIMATION_TYPE_ANY) override; - void SetAnimation(const wxAnimation &anim) override; + void SetAnimation(const wxAnimationBundle& anim) override; virtual bool Play() override; virtual void Stop() override; diff --git a/interface/wx/animate.h b/interface/wx/animate.h index c73130c9c3..e4a426f932 100644 --- a/interface/wx/animate.h +++ b/interface/wx/animate.h @@ -27,6 +27,82 @@ enum wxAnimationType #define wxAC_DEFAULT_STYLE (wxBORDER_NONE) +/** + Container for possible multiple versions of the same animation in different + resolutions. + + This class is used to pass either one or multiple animations to use in + wxAnimationCtrl. If it contains multiple elements, they must be added to it + using its Add() function in ascending size order and, in particular, the + first element defines the size of the animation in standard DPI, with the + other elements containing versions of the animation to use in higher DPI. + + Example of using this class to pass a normal and high DPI version of a + "throbbing" animation to the control and let it choose the one most + appropriate to the current resolution automatically: + + @code + auto animationCtrl = new wxAnimationCtrl(parent, wxID_ANY); + wxAnimationBundle animations; + animations.Add("throbber.gif"); + animations.Add("throbber_2x.gif"); + if ( animationCtrl->SetAnimation(animations) ) + animationCtrl->Play(); + @endcode + + @since 3.3.0 + */ +class wxAnimationBundle +{ +public: + /** + Default constructor creates an empty bundle. + + Call Add() later. + */ + wxAnimationBundle() = default; + + /** + Implicit ctor from wxAnimation for backwards compatibility. + */ + wxAnimationBundle(const wxAnimation& anim); + + /** + Implicit ctor from animation file name for backwards compatibility. + */ + wxAnimationBundle(const wxString& filename, + wxAnimationType type = wxANIMATION_TYPE_ANY); + + wxAnimationBundle(const wxAnimationBundle&) = default; + wxAnimationBundle& operator=(const wxAnimationBundle&) = default; + + ~wxAnimationBundle() = default; + + + /** + Add an animation in another, bigger, size. + */ + void Add(const wxAnimation& anim); + + /// @overload + void Add(const wxString& filename, + wxAnimationType type = wxANIMATION_TYPE_ANY); + + /** + Get vector containing all animations in this bundle. + + The vector can be empty. + */ + const std::vector& GetAll() const; + + /** + Return true if this animation bundle is not empty. + + Notice that any elements of the bundle are always valid animations. + */ + bool IsOk() const; +}; + /** @class wxAnimationCtrl @@ -175,8 +251,24 @@ public: Until Play() isn't called, a static image, the first frame of the given animation or the background colour will be shown (see SetInactiveBitmap() for more info). + + Note that while this function takes wxAnimationBundle parameter, it is + possible to pass just a single wxAnimation to it too, as it is + implicitly converted to a bundle. Moreover, is it also possible to call + this function with just the name of the animation file, e.g. + + @code + animationCtrl->SetAnimation("progress.gif"); + @endcode + + due to the existence of another implicit constructor, however this + doesn't allow to check for errors when creating the animation and is + not recommended. + + If the animation bundle contains more than one animation, the one + appropriate for the current display resolution is chosen. */ - virtual void SetAnimation(const wxAnimation& anim); + virtual void SetAnimation(const wxAnimationBundle& animations); /** Sets the bitmap to show on the control when it's not playing an animation. diff --git a/samples/animate/Makefile.in b/samples/animate/Makefile.in index 4c6e2d795e..bb57d826d8 100644 --- a/samples/animate/Makefile.in +++ b/samples/animate/Makefile.in @@ -186,7 +186,7 @@ anitest$(EXEEXT): $(ANITEST_OBJECTS) $(__anitest___win32rc) catalog: @mkdir -p . - @for f in throbber.gif hourglass.ani; do \ + @for f in throbber.gif throbber_2x.gif hourglass.ani; do \ if test ! -f ./$$f -a ! -d ./$$f ; \ then x=yep ; \ else x=`find $(srcdir)/$$f -newer ./$$f -print` ; \ diff --git a/samples/animate/anitest.bkl b/samples/animate/anitest.bkl index 12bac6ae6b..bf7e65e132 100644 --- a/samples/animate/anitest.bkl +++ b/samples/animate/anitest.bkl @@ -11,7 +11,7 @@ - throbber.gif hourglass.ani + throbber.gif throbber_2x.gif hourglass.ani diff --git a/samples/animate/anitest.cpp b/samples/animate/anitest.cpp index 36da022990..3f8a8e520f 100644 --- a/samples/animate/anitest.cpp +++ b/samples/animate/anitest.cpp @@ -174,8 +174,21 @@ MyFrame::MyFrame(wxWindow *parent, wxSizerFlags().Centre().Border()); m_animationCtrl = new wxAnimationCtrl(this, wxID_ANY); - if (m_animationCtrl->LoadFile("throbber.gif")) + + wxAnimationBundle animations; + + wxAnimation throbber("throbber.gif"); + if (throbber.IsOk()) + animations.Add(throbber); + wxAnimation throbber2x("throbber_2x.gif"); + if (throbber2x.IsOk()) + animations.Add(throbber2x); + + if (animations.IsOk()) + { + m_animationCtrl->SetAnimation(animations); m_animationCtrl->Play(); + } sz->Add(m_animationCtrl, wxSizerFlags().Centre().Border()); SetSizer(sz); diff --git a/samples/animate/makefile.gcc b/samples/animate/makefile.gcc index 89db9a5732..fc08431825 100644 --- a/samples/animate/makefile.gcc +++ b/samples/animate/makefile.gcc @@ -212,7 +212,7 @@ $(OBJS)\anitest.exe: $(ANITEST_OBJECTS) $(OBJS)\anitest_sample_rc.o catalog: if not exist $(OBJS) mkdir $(OBJS) - for %%f in (throbber.gif hourglass.ani) do if not exist $(OBJS)\%%f copy .\%%f $(OBJS) + for %%f in (throbber.gif throbber_2x.gif hourglass.ani) do if not exist $(OBJS)\%%f copy .\%%f $(OBJS) $(OBJS)\anitest_sample_rc.o: ./../../samples/sample.rc $(WINDRES) -i$< -o$@ --define __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) --include-dir $(SETUPHDIR) --include-dir ./../../include $(__CAIRO_INCLUDEDIR_p) --include-dir . $(__DLLFLAG_p_1) --define wxUSE_DPI_AWARE_MANIFEST=$(USE_DPI_AWARE_MANIFEST) --include-dir ./../../samples --define NOPCH diff --git a/samples/animate/makefile.vc b/samples/animate/makefile.vc index 927e1a9884..2e5259eed1 100644 --- a/samples/animate/makefile.vc +++ b/samples/animate/makefile.vc @@ -420,7 +420,7 @@ $(OBJS)\anitest.exe: $(ANITEST_OBJECTS) $(OBJS)\anitest_sample.res catalog: if not exist $(OBJS) mkdir $(OBJS) - for %f in (throbber.gif hourglass.ani) do if not exist $(OBJS)\%f copy .\%f $(OBJS) + for %f in (throbber.gif throbber_2x.gif hourglass.ani) do if not exist $(OBJS)\%f copy .\%f $(OBJS) $(OBJS)\anitest_sample.res: .\..\..\samples\sample.rc rc /fo$@ /d WIN32 $(____DEBUGRUNTIME_3_p_1) /d _CRT_SECURE_NO_DEPRECATE=1 /d _CRT_NON_CONFORMING_SWPRINTFS=1 /d _SCL_SECURE_NO_WARNINGS=1 $(__NO_VC_CRTDBG_p_1) $(__TARGET_CPU_COMPFLAG_p_1) /d __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__NDEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) /i $(SETUPHDIR) /i .\..\..\include $(____CAIRO_INCLUDEDIR_FILENAMES_1_p) /i . $(__DLLFLAG_p_1) /d _WINDOWS /i .\..\..\samples /d NOPCH .\..\..\samples\sample.rc diff --git a/samples/animate/throbber_2x.gif b/samples/animate/throbber_2x.gif new file mode 100644 index 0000000000000000000000000000000000000000..ff3ee8f20186660d18f05e91ff6cf412129253bc GIT binary patch literal 6254 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, type); } +// ---------------------------------------------------------------------------- +// wxAnimationBundle +// ---------------------------------------------------------------------------- + +void wxAnimationBundle::Add(const wxAnimation& anim) +{ + // It's ok to have empty animation bundle, but any animations added to + // it should be valid. + wxCHECK_RET( anim.IsOk(), "shouldn't add invalid animations" ); + + if ( !m_animations.empty() ) + { + // They also should be added in increasing size. + const wxSize thisSize = anim.GetSize(); + const wxSize lastSize = m_animations.back().GetSize(); + + wxCHECK_RET( thisSize != lastSize, + "shouldn't have multiple animations of the same size" ); + + wxCHECK_RET( thisSize.IsAtLeast(lastSize), + "should be added in order of increasing size" ); + } + + m_animations.push_back(anim); +} // ---------------------------------------------------------------------------- // wxAnimationCtrlBase diff --git a/src/generic/animateg.cpp b/src/generic/animateg.cpp index 9eb7b2d866..976fca8ffd 100644 --- a/src/generic/animateg.cpp +++ b/src/generic/animateg.cpp @@ -241,20 +241,32 @@ wxSize wxGenericAnimationCtrl::DoGetBestSize() const return FromDIP(wxSize(100, 100)); } -void wxGenericAnimationCtrl::SetAnimation(const wxAnimation& animation) +void wxGenericAnimationCtrl::SetAnimation(const wxAnimationBundle& animations) { if (IsPlaying()) Stop(); - // set new animation even if it's wxNullAnimation - m_animation = animation; - if (!m_animation.IsOk()) + m_animations = animations.GetAll(); + + // Reset animation if we don't have any valid ones. + if ( m_animations.empty() ) { + m_animation.UnRef(); DisplayStaticImage(); return; } - wxCHECK_RET(animation.IsCompatibleWith(GetClassInfo()), + // Otherwise choose the animation of the size most appropriate for the + // current resolution. + const wxSize wantedSize = m_animations[0].GetSize()*GetDPIScaleFactor(); + for ( const auto& anim: m_animations ) + { + m_animation = anim; + if ( m_animation.GetSize().IsAtLeast(wantedSize) ) + break; + } + + wxCHECK_RET(m_animation.IsCompatibleWith(GetClassInfo()), wxT("incompatible animation") ); if (AnimationImplGetBackgroundColour() == wxNullColour) diff --git a/src/gtk/animate.cpp b/src/gtk/animate.cpp index df118eca40..c6625b0d03 100644 --- a/src/gtk/animate.cpp +++ b/src/gtk/animate.cpp @@ -275,7 +275,7 @@ wxAnimationImpl* wxAnimationCtrl::DoCreateAnimationImpl() const return new wxAnimationGTKImpl(); } -void wxAnimationCtrl::SetAnimation(const wxAnimation &anim) +void wxAnimationCtrl::SetAnimation(const wxAnimationBundle& animations) { if (IsPlaying()) Stop(); @@ -283,15 +283,26 @@ void wxAnimationCtrl::SetAnimation(const wxAnimation &anim) ResetAnim(); ResetIter(); - m_animation = anim; - if (!m_animation.IsOk()) + m_animations = animations.GetAll(); + + // Reset animation if we don't have any valid ones. + if ( m_animations.empty() ) { + m_animation.UnRef(); m_anim = nullptr; DisplayStaticImage(); return; } - wxCHECK_RET(anim.IsCompatibleWith(GetClassInfo()), + // TODO: Support higher resolution animations. Currently we always take the + // animation for the standard resolution, which will be scaled if + // necessary, we would need to create the pixbuf using the correct + // animation when it's actually needed (by which time we will also have the + // correct scale factor, which is not the case yet if we're called before + // the control is shown, as is usually the case). + m_animation = m_animations[0]; + + wxCHECK_RET(m_animation.IsCompatibleWith(GetClassInfo()), wxT("incompatible animation") ); // copy underlying GdkPixbuf object