diff --git a/build/cmake/samples/CMakeLists.txt b/build/cmake/samples/CMakeLists.txt index 0519177e75..362cd9a111 100644 --- a/build/cmake/samples/CMakeLists.txt +++ b/build/cmake/samples/CMakeLists.txt @@ -8,7 +8,7 @@ ############################################################################# wx_add_sample(access accesstest.cpp DEPENDS wxUSE_ACCESSIBILITY) -wx_add_sample(animate anitest.cpp anitest.h DATA throbber.gif hourglass.ani DEPENDS wxUSE_ANIMATIONCTRL) +wx_add_sample(animate anitest.cpp anitest.h DATA throbber.gif throbber_2x.gif hourglass.ani DEPENDS wxUSE_ANIMATIONCTRL) wx_add_sample(archive CONSOLE) wx_add_sample(artprov arttest.cpp artbrows.cpp artbrows.h) wx_add_sample(aui auidemo.cpp LIBRARIES wxaui wxhtml NAME auidemo DEPENDS wxUSE_AUI) diff --git a/docs/doxygen/overviews/xrc_format.h b/docs/doxygen/overviews/xrc_format.h index 235dcd927d..acbdfa6724 100644 --- a/docs/doxygen/overviews/xrc_format.h +++ b/docs/doxygen/overviews/xrc_format.h @@ -653,7 +653,9 @@ controls cannot have children. @beginTable @hdr3col{property, type, description} @row3col{animation, @ref overview_xrcformat_type_url, - Animation file to load into the control (default: none).} + Animation file to load into the control or, since wxWindow 3.3.0, multiple + semicolon-separated files in order of increasing size, corresponding to + multiple versions of the animation for different resolutions (default: none).} @row3col{inactive-bitmap, @ref overview_xrcformat_type_bitmap, Bitmap to use when not playing the animation (default: the default).} @endTable 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/gdicmn.h b/include/wx/gdicmn.h index 236ba350ee..5f7b881cca 100644 --- a/include/wx/gdicmn.h +++ b/include/wx/gdicmn.h @@ -332,6 +332,10 @@ public: bool IsFullySpecified() const { return x != wxDefaultCoord && y != wxDefaultCoord; } + // Check that this size object is at least as big as the other one in both + // directions. + bool IsAtLeast(const wxSize& sz) const { return x >= sz.x && y >= sz.y; } + // combine this size with the other one replacing the default (i.e. equal // to wxDefaultCoord) components of this object with those of the other void SetDefaults(const wxSize& size) diff --git a/include/wx/generic/animate.h b/include/wx/generic/animate.h index 6b7fc55ce9..e673fa9928 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; @@ -140,6 +140,9 @@ protected: // on the screen private: + // True if we need to show the next frame after painting the current one. + bool m_needToShowNextFrame = false; + typedef wxAnimationCtrlBase base_type; wxDECLARE_DYNAMIC_CLASS(wxGenericAnimationCtrl); wxDECLARE_EVENT_TABLE(); 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/include/wx/xrc/xmlres.h b/include/wx/xrc/xmlres.h index 8cc356b9cc..868c5eb216 100644 --- a/include/wx/xrc/xmlres.h +++ b/include/wx/xrc/xmlres.h @@ -621,11 +621,19 @@ public: wxImageList *GetImageList(const wxString& param = wxT("imagelist")) override; #if wxUSE_ANIMATIONCTRL + // Get all the animations defined in the given parameter which may contain + // more than one semicolon-separated paths. + wxAnimationBundle GetAnimations(const wxString& param = wxT("animation"), + wxAnimationCtrlBase* ctrl = nullptr) override; + +#if WXWIN_COMPATIBILITY_3_2 + wxDEPRECATED_MSG("Use GetAnimations() instead") // Gets an animation creating it using the provided control (so that it // will be compatible with it) if any. wxAnimation* GetAnimation(const wxString& param = wxT("animation"), wxAnimationCtrlBase* ctrl = nullptr) override; -#endif +#endif // WXWIN_COMPATIBILITY_3_2 +#endif // wxUSE_ANIMATIONCTRL // Gets a font. wxFont GetFont(const wxString& param = wxT("font"), wxWindow* parent = nullptr) override; diff --git a/include/wx/xrc/xmlreshandler.h b/include/wx/xrc/xmlreshandler.h index be71ad7af9..7c0dc4773d 100644 --- a/include/wx/xrc/xmlreshandler.h +++ b/include/wx/xrc/xmlreshandler.h @@ -22,6 +22,7 @@ #include "wx/window.h" class WXDLLIMPEXP_FWD_CORE wxAnimation; +class WXDLLIMPEXP_FWD_CORE wxAnimationBundle; class WXDLLIMPEXP_FWD_CORE wxAnimationCtrlBase; class WXDLLIMPEXP_FWD_XML wxXmlNode; @@ -109,9 +110,15 @@ public: virtual wxImageList *GetImageList(const wxString& param = wxT("imagelist")) = 0; #if wxUSE_ANIMATIONCTRL + virtual wxAnimationBundle GetAnimations(const wxString& param = wxT("animation"), + wxAnimationCtrlBase* ctrl = nullptr) = 0; + +#if WXWIN_COMPATIBILITY_3_2 + wxDEPRECATED_BUT_USED_INTERNALLY_MSG("Use GetAnimations() instead") virtual wxAnimation* GetAnimation(const wxString& param = wxT("animation"), wxAnimationCtrlBase* ctrl = nullptr) = 0; -#endif +#endif // WXWIN_COMPATIBILITY_3_2 +#endif // wxUSE_ANIMATIONCTRL virtual wxFont GetFont(const wxString& param = wxT("font"), wxWindow* parent = nullptr) = 0; virtual bool GetBoolAttr(const wxString& attr, bool defaultv) = 0; @@ -377,12 +384,14 @@ protected: } #if wxUSE_ANIMATIONCTRL + wxAnimationBundle GetAnimations(const wxString& param = wxT("animation"), + wxAnimationCtrlBase* ctrl = nullptr); + +#if WXWIN_COMPATIBILITY_3_2 wxAnimation* GetAnimation(const wxString& param = wxT("animation"), - wxAnimationCtrlBase* ctrl = nullptr) - { - return GetImpl()->GetAnimation(param, ctrl); - } -#endif + wxAnimationCtrlBase* ctrl = nullptr); +#endif // WXWIN_COMPATIBILITY_3_2 +#endif // wxUSE_ANIMATIONCTRL wxFont GetFont(const wxString& param = wxT("font"), wxWindow* parent = nullptr) 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/interface/wx/gdicmn.h b/interface/wx/gdicmn.h index 822c338a67..c512a99a3b 100644 --- a/interface/wx/gdicmn.h +++ b/interface/wx/gdicmn.h @@ -1037,6 +1037,14 @@ public: */ void IncTo(const wxSize& size); + /** + Returns @true if this size is at least as big as the other one in both + directions. + + @since 3.3.0 + */ + bool IsAtLeast(const wxSize& size) const; + /** Returns @true if neither of the size object components is equal to -1, which is used as default for the size values in wxWidgets (hence the 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 0000000000..ff3ee8f201 Binary files /dev/null and b/samples/animate/throbber_2x.gif differ diff --git a/src/common/animatecmn.cpp b/src/common/animatecmn.cpp index 1f6361530d..9a80ce3add 100644 --- a/src/common/animatecmn.cpp +++ b/src/common/animatecmn.cpp @@ -126,6 +126,31 @@ bool wxAnimation::Load(wxInputStream& stream, wxAnimationType type) return GetImpl()->Load(stream, 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..365ffd0579 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) @@ -325,19 +337,9 @@ bool wxGenericAnimationCtrl::Play(bool looped) m_isPlaying = true; - // do a ClearBackground() to avoid that e.g. the custom static bitmap which - // was eventually shown previously remains partially drawn - ClearBackground(); + m_needToShowNextFrame = true; - // DrawCurrentFrame() will use our updated backing store - wxClientDC clientDC(this); - DrawCurrentFrame(clientDC); - - // start the timer - int delay = m_animation.GetDelay(0); - if (delay == 0) - delay = 1; // 0 is invalid timeout for wxTimer. - m_timer.Start(delay, true); + Refresh(); return true; } @@ -385,7 +387,6 @@ bool wxGenericAnimationCtrl::RebuildBackingStoreUpToFrame(unsigned int frame) // finally draw this frame DrawFrame(dc, frame); - dc.SelectObject(wxNullBitmap); return true; } @@ -439,7 +440,6 @@ void wxGenericAnimationCtrl::IncrementalUpdateBackingStore() // now just draw the current frame on the top of the backing store DrawFrame(dc, m_currentFrame); - dc.SelectObject(wxNullBitmap); } void wxGenericAnimationCtrl::DisplayStaticImage() @@ -552,6 +552,17 @@ void wxGenericAnimationCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) // clear then our area to the background colour DisposeToBackground(dc); } + + if ( m_needToShowNextFrame ) + { + m_needToShowNextFrame = false; + + // Set the timer for the next frame + int delay = m_animation.GetDelay(m_currentFrame); + if (delay == 0) + delay = 1; // 0 is invalid timeout for wxTimer. + m_timer.StartOnce(delay); + } } void wxGenericAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event)) @@ -571,19 +582,9 @@ void wxGenericAnimationCtrl::OnTimer(wxTimerEvent &WXUNUSED(event)) IncrementalUpdateBackingStore(); - wxClientDC dc(this); - DrawCurrentFrame(dc); + m_needToShowNextFrame = true; -#ifdef __WXMAC__ - // without this, the animation currently doesn't redraw under Mac Refresh(); -#endif // __WXMAC__ - - // Set the timer for the next frame - int delay = m_animation.GetDelay(m_currentFrame); - if (delay == 0) - delay = 1; // 0 is invalid timeout for wxTimer. - m_timer.Start(delay, true); } void wxGenericAnimationCtrl::OnSize(wxSizeEvent &WXUNUSED(event)) 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 diff --git a/src/xrc/xh_animatctrl.cpp b/src/xrc/xh_animatctrl.cpp index 59f82559d1..0e65a2b630 100644 --- a/src/xrc/xh_animatctrl.cpp +++ b/src/xrc/xh_animatctrl.cpp @@ -58,9 +58,9 @@ wxObject *wxAnimationCtrlXmlHandler::DoCreateResource() if ( GetBool("hidden", 0) == 1 ) ctrl->Hide(); - std::unique_ptr animation(GetAnimation("animation", ctrl)); - if ( animation ) - ctrl->SetAnimation(*animation); + const auto animations = GetAnimations("animation", ctrl); + if ( animations.IsOk() ) + ctrl->SetAnimation(animations); // if no inactive-bitmap has been provided, GetBitmapBundle() will return // an empty bundle, which just tells wxAnimationCtrl to use the default @@ -78,39 +78,61 @@ bool wxAnimationCtrlXmlHandler::CanHandle(wxXmlNode *node) IsOfClass(node, wxT("wxGenericAnimationCtrl")); } +wxAnimationBundle +wxXmlResourceHandlerImpl::GetAnimations(const wxString& param, + wxAnimationCtrlBase* ctrl) +{ + wxString paths = GetFilePath(GetParamNode(param)); + if ( paths.empty() ) + return {}; + + wxAnimationBundle animations; + for ( const auto& name: wxSplit(paths, ';', '\0') ) + { + // create compatible animation object + wxAnimation ani; + if ( ctrl ) + ani = ctrl->CreateAnimation(); + + // load the animation from file +#if wxUSE_FILESYSTEM + wxFSFile * const + fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE); + if ( fsfile ) + { + ani.Load(*fsfile->GetStream()); + delete fsfile; + } +#else + ani.LoadFile(name); +#endif + + if ( !ani.IsOk() ) + { + ReportParamError + ( + param, + wxString::Format("cannot create animation from \"%s\"", name) + ); + return {}; + } + + animations.Add(ani); + } + + return animations; +} + +#if WXWIN_COMPATIBILITY_3_2 + wxAnimation* wxXmlResourceHandlerImpl::GetAnimation(const wxString& param, wxAnimationCtrlBase* ctrl) { - wxString name = GetFilePath(GetParamNode(param)); - if ( name.empty() ) - return nullptr; + const auto animations = GetAnimations(param, ctrl); - // load the animation from file - std::unique_ptr ani(ctrl ? new wxAnimation(ctrl->CreateAnimation()) - : new wxAnimation); -#if wxUSE_FILESYSTEM - wxFSFile * const - fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE); - if ( fsfile ) - { - ani->Load(*fsfile->GetStream()); - delete fsfile; - } -#else - ani->LoadFile(name); -#endif - - if ( !ani->IsOk() ) - { - ReportParamError - ( - param, - wxString::Format("cannot create animation from \"%s\"", name) - ); - return nullptr; - } - - return ani.release(); + return animations.IsOk() ? new wxAnimation(animations.GetAll()[0]) : nullptr; } +#endif // WXWIN_COMPATIBILITY_3_2 + #endif // wxUSE_XRC && wxUSE_ANIMATIONCTRL diff --git a/src/xrc/xmlreshandler.cpp b/src/xrc/xmlreshandler.cpp index 6362a4acff..f50127c230 100644 --- a/src/xrc/xmlreshandler.cpp +++ b/src/xrc/xmlreshandler.cpp @@ -14,6 +14,10 @@ #include "wx/xrc/xmlreshandler.h" +#if wxUSE_ANIMATIONCTRL + #include "wx/animate.h" +#endif + wxIMPLEMENT_ABSTRACT_CLASS(wxXmlResourceHandler, wxObject); wxXmlResourceHandlerImplBase* wxXmlResourceHandler::GetImpl() const @@ -65,4 +69,24 @@ void wxXmlResourceHandler::AddWindowStyles() XRC_ADD_STYLE(wxWS_EX_PROCESS_UI_UPDATES); } +#if wxUSE_ANIMATIONCTRL + +wxAnimationBundle +wxXmlResourceHandler::GetAnimations(const wxString& param, + wxAnimationCtrlBase* ctrl) +{ + return GetImpl()->GetAnimations(param, ctrl); +} + +#if WXWIN_COMPATIBILITY_3_2 +wxAnimation* +wxXmlResourceHandler::GetAnimation(const wxString& param, + wxAnimationCtrlBase* ctrl) +{ + return GetImpl()->GetAnimation(param, ctrl); +} +#endif // WXWIN_COMPATIBILITY_3_2 + +#endif // wxUSE_ANIMATIONCTRL + #endif // wxUSE_XRC diff --git a/tests/geometry/size.cpp b/tests/geometry/size.cpp index 15ede6e524..a9c2f7d8a6 100644 --- a/tests/geometry/size.cpp +++ b/tests/geometry/size.cpp @@ -51,3 +51,16 @@ TEST_CASE("wxSize::Operators", "[size]") CHECK( wxSize(6, 9) / 1.5 == wxSize(4, 6) ); } + +TEST_CASE("wxSize::Functions", "[size]") +{ + CHECK( wxSize(10, 10).IsAtLeast(wxDefaultSize) ); + CHECK( wxSize(10, 10).IsAtLeast(wxSize()) ); + CHECK( wxSize(10, 10).IsAtLeast(wxSize(10, 5)) ); + CHECK( wxSize(10, 10).IsAtLeast(wxSize(10, 10)) ); + + CHECK_FALSE( wxSize(10, 10).IsAtLeast(wxSize(11, 10)) ); + CHECK_FALSE( wxSize(10, 10).IsAtLeast(wxSize(10, 11)) ); + CHECK_FALSE( wxSize(10, 10).IsAtLeast(wxSize(11, 11)) ); + CHECK_FALSE( wxDefaultSize.IsAtLeast(wxSize()) ); +}