Merge branch 'animation-hidpi'

Add support for specifying multiple animations to allow automatically
showing a higher resolution version when using high DPI, just as it was
already possible with the bitmaps.

See #23817.
This commit is contained in:
Vadim Zeitlin 2023-08-29 01:53:36 +02:00
commit 00366cbaae
22 changed files with 383 additions and 85 deletions

View file

@ -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)

View file

@ -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

View file

@ -20,6 +20,8 @@
#include "wx/timer.h"
#include "wx/bmpbndl.h"
#include <vector>
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<wxAnimation>;
// 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

View file

@ -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)

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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<wxAnimation>& 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.

View file

@ -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

View file

@ -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` ; \

View file

@ -11,7 +11,7 @@
</exe>
<wx-data id="catalog">
<files>throbber.gif hourglass.ani</files>
<files>throbber.gif throbber_2x.gif hourglass.ani</files>
</wx-data>
</makefile>

View file

@ -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);

View file

@ -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

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -58,9 +58,9 @@ wxObject *wxAnimationCtrlXmlHandler::DoCreateResource()
if ( GetBool("hidden", 0) == 1 )
ctrl->Hide();
std::unique_ptr<wxAnimation> 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<wxAnimation> 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

View file

@ -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

View file

@ -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()) );
}