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.
This commit is contained in:
parent
15f19d5d0f
commit
27e80f81b4
13 changed files with 235 additions and 19 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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` ; \
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
BIN
samples/animate/throbber_2x.gif
Normal file
BIN
samples/animate/throbber_2x.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue