зеркало из https://github.com/mozilla/pjs.git
Bug 373266 - base support for animated theme widgets to nsnativetheme. r=roc
This commit is contained in:
Родитель
47b51fb48b
Коммит
3a836e3f4a
|
@ -67,8 +67,7 @@
|
|||
#include "gfxPlatformGtk.h"
|
||||
#include "gfxGdkNativeRenderer.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsNativeThemeGTK, nsNativeTheme, nsITheme,
|
||||
nsIObserver)
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsNativeThemeGTK, nsNativeTheme, nsITheme)
|
||||
|
||||
static int gLastGdkError;
|
||||
|
||||
|
@ -82,7 +81,7 @@ nsNativeThemeGTK::nsNativeThemeGTK()
|
|||
// We have to call moz_gtk_shutdown before the event loop stops running.
|
||||
nsCOMPtr<nsIObserverService> obsServ =
|
||||
mozilla::services::GetObserverService();
|
||||
obsServ->AddObserver(this, "xpcom-shutdown", false);
|
||||
obsServ->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
|
||||
memset(mDisabledWidgetTypes, 0, sizeof(mDisabledWidgetTypes));
|
||||
memset(mSafeWidgetStates, 0, sizeof(mSafeWidgetStates));
|
||||
|
@ -95,14 +94,10 @@ NS_IMETHODIMP
|
|||
nsNativeThemeGTK::Observe(nsISupports *aSubject, const char *aTopic,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
|
||||
if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
moz_gtk_shutdown();
|
||||
} else {
|
||||
NS_NOTREACHED("unexpected topic");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return nsNativeTheme::Observe(aSubject, aTopic, aData);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -46,12 +46,12 @@
|
|||
#include "gtkdrawing.h"
|
||||
|
||||
class nsNativeThemeGTK: private nsNativeTheme,
|
||||
public nsITheme,
|
||||
public nsIObserver {
|
||||
public nsITheme {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
// Also implemented by nsNativeTheme
|
||||
NS_SCRIPTABLE NS_IMETHOD Observe(nsISupports *aSubject, const char * aTopic,
|
||||
const PRUnichar * aData);
|
||||
|
||||
// The nsITheme interface.
|
||||
NS_IMETHOD DrawWidgetBackground(nsRenderingContext* aContext,
|
||||
|
|
|
@ -54,13 +54,15 @@
|
|||
#include "nsProgressFrame.h"
|
||||
#include "nsMenuFrame.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
nsNativeTheme::nsNativeTheme()
|
||||
: mAnimatedContentTimeout(PR_UINT32_MAX)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsNativeTheme, nsITimerCallback)
|
||||
NS_IMPL_ISUPPORTS2(nsNativeTheme, nsITimerCallback, nsIObserver)
|
||||
|
||||
nsIPresShell *
|
||||
nsNativeTheme::GetPresShell(nsIFrame* aFrame)
|
||||
|
@ -565,6 +567,150 @@ nsNativeTheme::QueueAnimatedContentForRefresh(nsIContent* aContent,
|
|||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsFadeIn(nsNativeTheme::FadeState aState)
|
||||
{
|
||||
return (aState == nsNativeTheme::FADE_IN ||
|
||||
aState == nsNativeTheme::FADE_IN_FINISHED);
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsFadeOut(nsNativeTheme::FadeState aState)
|
||||
{
|
||||
return (aState == nsNativeTheme::FADE_OUT);
|
||||
}
|
||||
|
||||
bool
|
||||
nsNativeTheme::QueueAnimatedContentRefreshForFade(nsIContent* aContent,
|
||||
FadeState aFadeDirection,
|
||||
PRUint32 aMinimumFrameRate,
|
||||
PRUint32 aMilliseconds,
|
||||
PRUint32 aUserData)
|
||||
{
|
||||
NS_ASSERTION(aContent, "Null pointer!");
|
||||
NS_ASSERTION((aFadeDirection == FADE_IN ||
|
||||
aFadeDirection == FADE_OUT), "Bad initial fade direction.");
|
||||
|
||||
// Initialize our hash table and setup an observer for freeing its contents
|
||||
// on shutdown.
|
||||
if (NS_FAILED(InitFadeList()))
|
||||
return false;
|
||||
|
||||
// Note, QueueAnimatedContentForRefresh failures in here can result in
|
||||
// content getting stuck in mAnimatedFadesList until shutdown, so we
|
||||
// warn loudly. Generally this should never happen.
|
||||
|
||||
FadeData* pFade = mAnimatedFadesList.Get(aContent);
|
||||
if (pFade) {
|
||||
// Update the user data
|
||||
pFade->SetUserData(aUserData);
|
||||
|
||||
// Check for direction changes and update our fade data accordingly.
|
||||
if (IsFadeIn(pFade->GetState()) != IsFadeIn(aFadeDirection)) {
|
||||
if (pFade->GetState() != FADE_IN_FINISHED) {
|
||||
// The amount of time we spent getting here equals the amount of
|
||||
// time we spend getting back out.
|
||||
pFade->Reset(pFade->TimeoutUsed(), aFadeDirection);
|
||||
} else {
|
||||
// Reset to transition timeout passed in.
|
||||
//PRUint32 timeout =
|
||||
// PR_IntervalToMilliseconds(PR_IntervalNow()) + aMilliseconds;
|
||||
//pFade->Reset(timeout, aFadeDirection);
|
||||
pFade->Reset(TimeDuration::FromMilliseconds(aMilliseconds),
|
||||
aFadeDirection);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a timeout
|
||||
if (pFade->GetTimeout() < TimeStamp::Now()) {
|
||||
// If timed out and it's a fade up, set state to finished. We keep the
|
||||
// fade data around until a corresponding fade out completes or the
|
||||
// underlying frame is destroyed.
|
||||
if (IsFadeIn(pFade->GetState())) {
|
||||
pFade->FadeInFinished();
|
||||
// Create a heartbeat (1 sec) animation timer so if the underlying
|
||||
// frame is destroyed, Notify will free the content.
|
||||
if (!QueueAnimatedContentForRefresh(aContent, 1)) {
|
||||
NS_WARNING("QueueAnimatedContentForRefresh failed???");
|
||||
return false;
|
||||
}
|
||||
} else if (IsFadeOut(pFade->GetState())) {
|
||||
// If timed out and it's a fade out, clear it, we're done.
|
||||
mAnimatedFadesList.Remove(aContent);
|
||||
// Fire one last time to get the base graphic painted.
|
||||
if (!QueueAnimatedContentForRefresh(aContent, aMinimumFrameRate)) {
|
||||
NS_WARNING("QueueAnimatedContentForRefresh failed???");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// fading..
|
||||
if (!QueueAnimatedContentForRefresh(aContent, aMinimumFrameRate)) {
|
||||
NS_WARNING("QueueAnimatedContentForRefresh failed???");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we don't have a fade put together a FadeData, store it in
|
||||
// mAnimatedFadesList, and kick things off.
|
||||
TimeStamp timeout = TimeStamp::Now() +
|
||||
TimeDuration::FromMilliseconds(aMilliseconds);
|
||||
nsAutoPtr<FadeData> newFade(new FadeData(timeout, aFadeDirection, aUserData));
|
||||
if (!newFade) {
|
||||
NS_WARNING("Out of memory!");
|
||||
return false;
|
||||
}
|
||||
// Call QueueAnimatedContentForRefresh to kick off the fade animation.
|
||||
if (!QueueAnimatedContentForRefresh(aContent, aMinimumFrameRate)) {
|
||||
NS_WARNING("QueueAnimatedContentForRefresh failed???");
|
||||
return false;
|
||||
}
|
||||
mAnimatedFadesList.Put(aContent, newFade);
|
||||
newFade.forget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// mAnimatedFadesList management
|
||||
|
||||
nsresult
|
||||
nsNativeTheme::InitFadeList()
|
||||
{
|
||||
if (mAnimatedFadesList.IsInitialized())
|
||||
return NS_OK;
|
||||
if (!mAnimatedFadesList.Init())
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
mozilla::services::GetObserverService();
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
if (obsSvc) {
|
||||
rv = obsSvc->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNativeTheme::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const PRUnichar* aData)
|
||||
{
|
||||
if (strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) == 0) {
|
||||
mAnimatedFadesList.Clear();
|
||||
nsCOMPtr<nsIObserverService> obsSvc =
|
||||
mozilla::services::GetObserverService();
|
||||
nsresult rv = NS_ERROR_UNEXPECTED;
|
||||
if (obsSvc) {
|
||||
rv = obsSvc->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
|
||||
}
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"nsNativeTheme RemoveObserver failed, this may cause a leak.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// mAnimatedContentTimer callback for QueueAnimatedContentForRefresh
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNativeTheme::Notify(nsITimer* aTimer)
|
||||
{
|
||||
|
@ -578,14 +724,90 @@ nsNativeTheme::Notify(nsITimer* aTimer)
|
|||
nsIFrame* frame = mAnimatedContentList[index]->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
frame->InvalidateOverflowRect();
|
||||
} else {
|
||||
// If this content has fade data associated with it, and the
|
||||
// frame has gone away, free the data and cancel the fade.
|
||||
if (mAnimatedFadesList.IsInitialized()) {
|
||||
mAnimatedFadesList.Remove(mAnimatedContentList[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mAnimatedContentList.Clear();
|
||||
mAnimatedContentTimeout = PR_UINT32_MAX;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Fade helpers
|
||||
|
||||
nsNativeTheme::FadeData*
|
||||
nsNativeTheme::GetFade(nsIContent* aContent)
|
||||
{
|
||||
if (!aContent || !mAnimatedFadesList.IsInitialized())
|
||||
return nsnull;
|
||||
return mAnimatedFadesList.Get(reinterpret_cast<nsISupports*>(aContent));
|
||||
}
|
||||
|
||||
nsNativeTheme::FadeState
|
||||
nsNativeTheme::GetFadeState(nsIContent* aContent)
|
||||
{
|
||||
FadeData* pFade = GetFade(aContent);
|
||||
if (!pFade)
|
||||
return FADE_NOTACTIVE;
|
||||
return pFade->GetState();
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsNativeTheme::GetFadeTicks(nsIContent* aContent)
|
||||
{
|
||||
FadeData* pFade = GetFade(aContent);
|
||||
if (!pFade)
|
||||
return 0;
|
||||
return pFade->GetTicks();
|
||||
}
|
||||
|
||||
double
|
||||
nsNativeTheme::GetFadeAlpha(nsIContent* aContent)
|
||||
{
|
||||
return ((double)GetFadeTicks(aContent))/TICK_MAX;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsNativeTheme::GetFadeUserData(nsIContent* aContent)
|
||||
{
|
||||
FadeData* pFade = GetFade(aContent);
|
||||
if (!pFade)
|
||||
return 0;
|
||||
return pFade->GetUserData();
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeTheme::SetFadeUserData(nsIContent* aContent, PRUint32 aUserData)
|
||||
{
|
||||
FadeData* pFade = GetFade(aContent);
|
||||
if (pFade) {
|
||||
pFade->SetUserData(aUserData);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeTheme::CancelFade(nsIContent* aContent)
|
||||
{
|
||||
if (aContent && mAnimatedFadesList.IsInitialized()) {
|
||||
mAnimatedFadesList.Remove(reinterpret_cast<nsISupports*>(aContent));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsNativeTheme::FinishFadeIn(nsIContent* aContent)
|
||||
{
|
||||
FadeData* pFade = GetFade(aContent);
|
||||
if (pFade) {
|
||||
pFade->FadeInFinished();
|
||||
}
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
|
||||
bool aNextSibling)
|
||||
|
@ -607,3 +829,4 @@ nsNativeTheme::GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
|
|||
return nsnull;
|
||||
return sibling;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,18 +49,27 @@
|
|||
#include "nsEventStates.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsIFrame;
|
||||
class nsIPresShell;
|
||||
class nsPresContext;
|
||||
|
||||
class nsNativeTheme : public nsITimerCallback
|
||||
class nsNativeTheme :
|
||||
public nsITimerCallback,
|
||||
public nsIObserver
|
||||
{
|
||||
typedef mozilla::TimeStamp TimeStamp;
|
||||
typedef mozilla::TimeDuration TimeDuration;
|
||||
|
||||
protected:
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
enum ScrollbarButtonType {
|
||||
eScrollbarButton_UpTop = 0,
|
||||
|
@ -189,14 +198,172 @@ class nsNativeTheme : public nsITimerCallback
|
|||
bool GetCheckedOrSelected(nsIFrame* aFrame, bool aCheckSelected);
|
||||
bool GetIndeterminate(nsIFrame* aFrame);
|
||||
|
||||
bool QueueAnimatedContentForRefresh(nsIContent* aContent,
|
||||
PRUint32 aMinimumFrameRate);
|
||||
|
||||
nsIFrame* GetAdjacentSiblingFrameWithSameAppearance(nsIFrame* aFrame,
|
||||
bool aNextSibling);
|
||||
|
||||
// Queue a themed element for a redraw after a set interval.
|
||||
bool QueueAnimatedContentForRefresh(nsIContent* aContent,
|
||||
PRUint32 aMinimumFrameRate);
|
||||
|
||||
/*
|
||||
* Simple two phase animations on themed widgets - 'Fades' transition from
|
||||
* a base state to a highlighted state and back to the base state, at which
|
||||
* point data associated with the fade is freed.
|
||||
*
|
||||
* Important notes:
|
||||
*
|
||||
* Consumers are responsible for triggering refresh calls by calling
|
||||
* QueueAnimatedContentRefreshForFade on each redraw.
|
||||
*
|
||||
* Consumers are also responsible for switching fade transitions from
|
||||
* FADE_IN/FADE_IN_FINISHED to FADE_OUT through calls to QACRFF. Failing
|
||||
* to do so keeps content / fade data stored in mAnimatedFadesList until
|
||||
* the content's underlying frame is destroyed or the application closes.
|
||||
*/
|
||||
|
||||
// Fade states
|
||||
typedef enum FadeState {
|
||||
FADE_NOTACTIVE = 0, // Fade state not found, fade complete
|
||||
FADE_IN = 1, // Fading in
|
||||
FADE_IN_FINISHED = 2, // Fade-in is finished, waiting for fade-out
|
||||
FADE_OUT = 3, // Fading out
|
||||
};
|
||||
|
||||
/*
|
||||
* QueueAnimatedContentRefreshForFade - creates a new fade or requests a
|
||||
* refresh on an existing fade in progress.
|
||||
*
|
||||
* aContent The themed content element the animation is associated
|
||||
* with.
|
||||
* aFadeDirection The current direction of the fade. Valid values are
|
||||
* FADE_IN or FADE_OUT.
|
||||
* aMinimumFrameRate The minimum frame rate requested (30 is typical). Value
|
||||
* is passed to QueueAnimatedContentForRefresh to trigger a
|
||||
* refresh.
|
||||
* aMilliseconds Duration of the fade-in or fade-out transition.
|
||||
* aUserData Generic consumer data storage for state across rendering
|
||||
* of individual frames. Updated on every call.
|
||||
*/
|
||||
bool
|
||||
QueueAnimatedContentRefreshForFade(nsIContent* aContent,
|
||||
FadeState aFadeDirection,
|
||||
PRUint32 aMinimumFrameRate,
|
||||
PRUint32 aMilliseconds,
|
||||
PRUint32 aUserData = 0);
|
||||
|
||||
// Max ticks returned by FadeData->GetTicks().
|
||||
#define TICK_MAX 100.0
|
||||
|
||||
// Internal data structure for storing fade data
|
||||
class FadeData
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* FadeData()
|
||||
* aTimeout now + duration
|
||||
* aState FADE_IN or FADE_OUT
|
||||
* aUserData intial value for user data
|
||||
*/
|
||||
FadeData(TimeStamp aTimeout, FadeState aState, PRUint32 aUserData) :
|
||||
mTimeout(aTimeout),
|
||||
mStartTime(TimeStamp::Now()),
|
||||
mState(aState),
|
||||
mUserData(aUserData) {
|
||||
}
|
||||
~FadeData() {}
|
||||
|
||||
/*
|
||||
* Reset - resets the to a new timeout value and direction.
|
||||
* aTimeout msec(now) + duration
|
||||
* aState FADE_IN or FADE_OUT
|
||||
*/
|
||||
void Reset(TimeDuration aTimeout, FadeState aState) {
|
||||
NS_ASSERTION((aState == FADE_IN || aState == FADE_OUT),
|
||||
"Bad fade direction.");
|
||||
mStartTime = TimeStamp::Now();
|
||||
mTimeout = TimeStamp::Now() + aTimeout;
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetTicks - returns the number of ticks in this animation where
|
||||
* ticks >= 0 && ticks <= TICK_MAX. FADE_IN has increasing ticks,
|
||||
* FADE_OUT decreasing.
|
||||
*/
|
||||
PRUint32 GetTicks() {
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
if (now >= mTimeout) {
|
||||
return (mState == FADE_OUT ? 0 : (PRUint32)TICK_MAX);
|
||||
}
|
||||
TimeDuration diff = now - mStartTime;
|
||||
PRUint32 tick =
|
||||
(PRUint32)ceil((diff / (mTimeout - mStartTime)) * TICK_MAX);
|
||||
// we want ticks to ascend and descend according to the direction.
|
||||
if (mState == FADE_OUT) {
|
||||
tick = (PRUint32)abs(tick - TICK_MAX);
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
/*
|
||||
* TimeoutUsed - for fades that have not completes, returns the
|
||||
* amount of time used thus far in the current transition in msec.
|
||||
*/
|
||||
TimeDuration TimeoutUsed() {
|
||||
TimeDuration used = TimeStamp::Now() - mStartTime;
|
||||
TimeDuration totalTime = mTimeout - mStartTime;
|
||||
return NS_MIN(used, totalTime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Misc. data getters/setters
|
||||
*/
|
||||
TimeStamp GetTimeout() { return mTimeout; }
|
||||
FadeState GetState() { return mState; }
|
||||
void FadeInFinished() { mState = FADE_IN_FINISHED; }
|
||||
PRUint32 GetUserData() { return mUserData; }
|
||||
void SetUserData(PRUint32 aUserData) { mUserData = aUserData; }
|
||||
|
||||
private:
|
||||
TimeStamp mTimeout;
|
||||
TimeStamp mStartTime;
|
||||
FadeState mState;
|
||||
PRUint32 mUserData;
|
||||
};
|
||||
|
||||
/*
|
||||
* nsNativeTheme fade data helpers
|
||||
*/
|
||||
|
||||
// Retrieves the FadeData object associated with this content, or null.
|
||||
FadeData* GetFade(nsIContent* aContent);
|
||||
// Retrieves the current fade state or FADE_NOTACTIVE.
|
||||
FadeState GetFadeState(nsIContent* aContent);
|
||||
// Retrieves the current tick count for a fade transition or 0. Ticks
|
||||
// range from 0 -> TICK_MAX. For FADE_IN transitions ticks increase,
|
||||
// for FADE_OUT transitions ticks decrease.
|
||||
PRUint32 GetFadeTicks(nsIContent* aContent);
|
||||
// Retrieves the alpha value (0->1) corresponding to the current tick
|
||||
// count for a fade transition, or 0.
|
||||
double GetFadeAlpha(nsIContent* aContent);
|
||||
// Get/set consumer data. Valid across each call to QACRFF.
|
||||
PRUint32 GetFadeUserData(nsIContent* aContent);
|
||||
void SetFadeUserData(nsIContent* aContent, PRUint32 aUserData);
|
||||
// Cancel an active fade and free its resources.
|
||||
void CancelFade(nsIContent* aContent);
|
||||
// Mark a fade as FADE_IN_FINISHED.
|
||||
void FinishFadeIn(nsIContent* aContent);
|
||||
|
||||
private:
|
||||
nsresult InitFadeList();
|
||||
|
||||
PRUint32 mAnimatedContentTimeout;
|
||||
nsCOMPtr<nsITimer> mAnimatedContentTimer;
|
||||
// Render refresh list - nsIContent contains the content
|
||||
// that will be invalidated when mAnimatedContentTimer fires.
|
||||
// Cleared on every call to mAnimatedContentTimer Notify.
|
||||
nsAutoTArray<nsCOMPtr<nsIContent>, 20> mAnimatedContentList;
|
||||
// Fade list data - nsISupportsHashKey contains the nsIContent
|
||||
// associated with an active fade.
|
||||
nsClassHashtable<nsISupportsHashKey, FadeData> mAnimatedFadesList;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче