зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1631735 Part 1: Make nsCocoaWindow animated transitions asynchronous and atomic. r=mstange
This patch attempts to make nsCocoaWindow transitions (fullscreen, windowed, minimize, zoom) atomic operations that can't be disrupted by user action or by programmatic triggers. To accomplish this, it defines a transition queue which ensures that when a transition is submitted, it is always run to completion before another transition is started. Native transitions execute asynchronously, and emulated ones execute synchronously. Additionally, this patch adds intermediary transitions to handle programmatic transitions which aren't allowed by macOS, which include: 1) Attempting to minimize a fullscreen window will first transition to windowed state. 2) Any transition on a minimized window will first transition to windowed state. A later part of this patch stack tests this behavior. Differential Revision: https://phabricator.services.mozilla.com/D166450
This commit is contained in:
Родитель
04266eba2f
Коммит
35295e5966
|
@ -16,6 +16,7 @@
|
||||||
#include "nsCocoaUtils.h"
|
#include "nsCocoaUtils.h"
|
||||||
#include "nsTouchBar.h"
|
#include "nsTouchBar.h"
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
class nsCocoaWindow;
|
class nsCocoaWindow;
|
||||||
class nsChildView;
|
class nsChildView;
|
||||||
|
@ -262,8 +263,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||||
virtual void SuppressAnimation(bool aSuppress) override;
|
virtual void SuppressAnimation(bool aSuppress) override;
|
||||||
virtual void HideWindowChrome(bool aShouldHide) override;
|
virtual void HideWindowChrome(bool aShouldHide) override;
|
||||||
|
|
||||||
void WillEnterFullScreen(bool aFullScreen);
|
|
||||||
void EnteredFullScreen(bool aFullScreen, bool aNativeMode = true);
|
|
||||||
virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
|
virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
|
||||||
virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration,
|
virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration,
|
||||||
nsISupports* aData, nsIRunnable* aCallback) override;
|
nsISupports* aData, nsIRunnable* aCallback) override;
|
||||||
|
@ -362,6 +361,25 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||||
const ScrollableLayerGuid& aGuid) override;
|
const ScrollableLayerGuid& aGuid) override;
|
||||||
void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) override;
|
void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) override;
|
||||||
|
|
||||||
|
// Class method versions of NSWindow/Delegate callbacks which need to
|
||||||
|
// access object state.
|
||||||
|
void CocoaWindowWillEnterFullscreen(bool aFullscreen);
|
||||||
|
void CocoaWindowDidFailFullscreen(bool aAttemptedFullscreen);
|
||||||
|
void CocoaWindowDidResize();
|
||||||
|
void CocoaSendToplevelActivateEvents();
|
||||||
|
void CocoaSendToplevelDeactivateEvents();
|
||||||
|
|
||||||
|
enum class TransitionType {
|
||||||
|
Windowed,
|
||||||
|
Fullscreen,
|
||||||
|
EmulatedFullscreen,
|
||||||
|
Miniaturize,
|
||||||
|
Deminiaturize,
|
||||||
|
Zoom,
|
||||||
|
};
|
||||||
|
void FinishCurrentTransition();
|
||||||
|
void FinishCurrentTransitionIfMatching(const TransitionType& aTransition);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~nsCocoaWindow();
|
virtual ~nsCocoaWindow();
|
||||||
|
|
||||||
|
@ -375,7 +393,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||||
void DoResize(double aX, double aY, double aWidth, double aHeight, bool aRepaint,
|
void DoResize(double aX, double aY, double aWidth, double aHeight, bool aRepaint,
|
||||||
bool aConstrainToCurrentScreen);
|
bool aConstrainToCurrentScreen);
|
||||||
|
|
||||||
inline bool ShouldToggleNativeFullscreen(bool aFullScreen, bool aUseSystemTransition);
|
|
||||||
void UpdateFullscreenState(bool aFullScreen, bool aNativeMode);
|
void UpdateFullscreenState(bool aFullScreen, bool aNativeMode);
|
||||||
nsresult DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition);
|
nsresult DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition);
|
||||||
|
|
||||||
|
@ -405,8 +422,27 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||||
// this is used for sibling sheet contention only
|
// this is used for sibling sheet contention only
|
||||||
nsSizeMode mSizeMode;
|
nsSizeMode mSizeMode;
|
||||||
bool mInFullScreenMode;
|
bool mInFullScreenMode;
|
||||||
bool mInFullScreenTransition; // true from the request to enter/exit fullscreen
|
// Whether we are currently using native fullscreen. It could be false because
|
||||||
// (MakeFullScreen() call) to EnteredFullScreen()
|
// we are in the emulated fullscreen where we do not use the native fullscreen.
|
||||||
|
bool mInNativeFullScreenMode;
|
||||||
|
|
||||||
|
mozilla::Maybe<TransitionType> mTransitionCurrent;
|
||||||
|
std::queue<TransitionType> mTransitionsPending;
|
||||||
|
|
||||||
|
// Whether we are treating the next resize as the start of a fullscreen transition.
|
||||||
|
// If we are, which direction are we going: Fullscreen or Windowed.
|
||||||
|
mozilla::Maybe<TransitionType> mUpdateFullscreenOnResize;
|
||||||
|
|
||||||
|
bool IsInTransition() { return mTransitionCurrent.isSome(); }
|
||||||
|
void QueueTransition(const TransitionType& aTransition);
|
||||||
|
void ProcessTransitions();
|
||||||
|
|
||||||
|
bool mInProcessTransitions = false;
|
||||||
|
|
||||||
|
// While running an emulated fullscreen transition, we want to suppress sending
|
||||||
|
// size mode events due to window resizing. We fix it up at the end when the
|
||||||
|
// transition is complete.
|
||||||
|
bool mSuppressSizeModeEvents = false;
|
||||||
|
|
||||||
// Ignore occlusion events caused by displaying the temporary fullscreen
|
// Ignore occlusion events caused by displaying the temporary fullscreen
|
||||||
// window during the fullscreen transition animation because only focused
|
// window during the fullscreen transition animation because only focused
|
||||||
|
@ -416,10 +452,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa {
|
||||||
bool mModal;
|
bool mModal;
|
||||||
bool mFakeModal;
|
bool mFakeModal;
|
||||||
|
|
||||||
// Whether we are currently using native fullscreen. It could be false because
|
|
||||||
// we are in the DOM fullscreen where we do not use the native fullscreen.
|
|
||||||
bool mInNativeFullScreenMode;
|
|
||||||
|
|
||||||
bool mIsAnimationSuppressed;
|
bool mIsAnimationSuppressed;
|
||||||
|
|
||||||
bool mInReportMoveEvent; // true if in a call to ReportMoveEvent().
|
bool mInReportMoveEvent; // true if in a call to ReportMoveEvent().
|
||||||
|
|
|
@ -139,11 +139,10 @@ nsCocoaWindow::nsCocoaWindow()
|
||||||
mSheetNeedsShow(false),
|
mSheetNeedsShow(false),
|
||||||
mSizeMode(nsSizeMode_Normal),
|
mSizeMode(nsSizeMode_Normal),
|
||||||
mInFullScreenMode(false),
|
mInFullScreenMode(false),
|
||||||
mInFullScreenTransition(false),
|
mInNativeFullScreenMode(false),
|
||||||
mIgnoreOcclusionCount(0),
|
mIgnoreOcclusionCount(0),
|
||||||
mModal(false),
|
mModal(false),
|
||||||
mFakeModal(false),
|
mFakeModal(false),
|
||||||
mInNativeFullScreenMode(false),
|
|
||||||
mIsAnimationSuppressed(false),
|
mIsAnimationSuppressed(false),
|
||||||
mInReportMoveEvent(false),
|
mInReportMoveEvent(false),
|
||||||
mInResize(false),
|
mInResize(false),
|
||||||
|
@ -1256,32 +1255,15 @@ void nsCocoaWindow::Move(double aX, double aY) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsCocoaWindow::SetSizeMode(nsSizeMode aMode) {
|
void nsCocoaWindow::SetSizeMode(nsSizeMode aMode) {
|
||||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
|
||||||
|
|
||||||
if (!mWindow) return;
|
|
||||||
|
|
||||||
// mSizeMode will be updated in DispatchSizeModeEvent, which will be called
|
|
||||||
// from a delegate method that handles the state change during one of the
|
|
||||||
// calls below.
|
|
||||||
nsSizeMode previousMode = mSizeMode;
|
|
||||||
|
|
||||||
if (aMode == nsSizeMode_Normal) {
|
if (aMode == nsSizeMode_Normal) {
|
||||||
if ([mWindow isMiniaturized])
|
QueueTransition(TransitionType::Windowed);
|
||||||
[mWindow deminiaturize:nil];
|
|
||||||
else if (previousMode == nsSizeMode_Maximized && [mWindow isZoomed])
|
|
||||||
[mWindow zoom:nil];
|
|
||||||
else if (previousMode == nsSizeMode_Fullscreen)
|
|
||||||
MakeFullScreen(false);
|
|
||||||
} else if (aMode == nsSizeMode_Minimized) {
|
} else if (aMode == nsSizeMode_Minimized) {
|
||||||
if (![mWindow isMiniaturized]) [mWindow miniaturize:nil];
|
QueueTransition(TransitionType::Miniaturize);
|
||||||
} else if (aMode == nsSizeMode_Maximized) {
|
} else if (aMode == nsSizeMode_Maximized) {
|
||||||
if ([mWindow isMiniaturized]) [mWindow deminiaturize:nil];
|
QueueTransition(TransitionType::Zoom);
|
||||||
if (![mWindow isZoomed]) [mWindow zoom:nil];
|
|
||||||
} else if (aMode == nsSizeMode_Fullscreen) {
|
} else if (aMode == nsSizeMode_Fullscreen) {
|
||||||
if (!mInFullScreenMode) MakeFullScreen(true);
|
MakeFullScreen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_OBJC_END_TRY_IGNORE_BLOCK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The (work)space switching implementation below was inspired by Phoenix:
|
// The (work)space switching implementation below was inspired by Phoenix:
|
||||||
|
@ -1639,18 +1621,33 @@ static bool AlwaysUsesNativeFullScreen() {
|
||||||
[mFullscreenTransitionAnimation startAnimation];
|
[mFullscreenTransitionAnimation startAnimation];
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsCocoaWindow::WillEnterFullScreen(bool aFullScreen) {
|
void nsCocoaWindow::CocoaWindowWillEnterFullscreen(bool aFullscreen) {
|
||||||
|
MOZ_ASSERT(mUpdateFullscreenOnResize.isNothing());
|
||||||
|
|
||||||
|
// Ensure that we update our fullscreen state as early as possible, when the resize
|
||||||
|
// happens.
|
||||||
|
mUpdateFullscreenOnResize =
|
||||||
|
Some(aFullscreen ? TransitionType::Fullscreen : TransitionType::Windowed);
|
||||||
|
|
||||||
if (mWidgetListener) {
|
if (mWidgetListener) {
|
||||||
mWidgetListener->FullscreenWillChange(aFullScreen);
|
mWidgetListener->FullscreenWillChange(aFullscreen);
|
||||||
}
|
}
|
||||||
// Update the state to full screen when we are entering, so that we switch to
|
|
||||||
// full screen view as soon as possible.
|
|
||||||
UpdateFullscreenState(aFullScreen, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsCocoaWindow::EnteredFullScreen(bool aFullScreen, bool aNativeMode) {
|
void nsCocoaWindow::CocoaWindowDidFailFullscreen(bool aAttemptedFullscreen) {
|
||||||
mInFullScreenTransition = false;
|
if (mWidgetListener) {
|
||||||
UpdateFullscreenState(aFullScreen, aNativeMode);
|
mWidgetListener->FullscreenWillChange(!aAttemptedFullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we already updated our fullscreen state due to a resize, we need to update it again.
|
||||||
|
if (mUpdateFullscreenOnResize.isNothing()) {
|
||||||
|
UpdateFullscreenState(!aAttemptedFullscreen, true);
|
||||||
|
ReportSizeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionType transition =
|
||||||
|
aAttemptedFullscreen ? TransitionType::Fullscreen : TransitionType::Windowed;
|
||||||
|
FinishCurrentTransitionIfMatching(transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nsCocoaWindow::UpdateFullscreenState(bool aFullScreen, bool aNativeMode) {
|
void nsCocoaWindow::UpdateFullscreenState(bool aFullScreen, bool aNativeMode) {
|
||||||
|
@ -1671,29 +1668,6 @@ void nsCocoaWindow::UpdateFullscreenState(bool aFullScreen, bool aNativeMode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool nsCocoaWindow::ShouldToggleNativeFullscreen(bool aFullScreen,
|
|
||||||
bool aUseSystemTransition) {
|
|
||||||
// First check if this window supports entering native fullscreen.
|
|
||||||
// This is set based on the macnativefullscreen attribute on the window's
|
|
||||||
// document element.
|
|
||||||
NSWindowCollectionBehavior colBehavior = [mWindow collectionBehavior];
|
|
||||||
if (!(colBehavior & NSWindowCollectionBehaviorFullScreenPrimary)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mInNativeFullScreenMode) {
|
|
||||||
// If we are using native fullscreen, go ahead to exit it.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!aUseSystemTransition) {
|
|
||||||
// If we do not want the system fullscreen transition,
|
|
||||||
// don't use the native fullscreen.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If we are using native fullscreen, we should have returned earlier.
|
|
||||||
return aFullScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult nsCocoaWindow::MakeFullScreen(bool aFullScreen) {
|
nsresult nsCocoaWindow::MakeFullScreen(bool aFullScreen) {
|
||||||
return DoMakeFullScreen(aFullScreen, AlwaysUsesNativeFullScreen());
|
return DoMakeFullScreen(aFullScreen, AlwaysUsesNativeFullScreen());
|
||||||
}
|
}
|
||||||
|
@ -1703,45 +1677,196 @@ nsresult nsCocoaWindow::MakeFullScreenWithNativeTransition(bool aFullScreen) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult nsCocoaWindow::DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition) {
|
nsresult nsCocoaWindow::DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition) {
|
||||||
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
|
|
||||||
|
|
||||||
if (!mWindow) {
|
if (!mWindow) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will call into MakeFullScreen redundantly when entering/exiting
|
// Figure out what type of transition is being requested.
|
||||||
// fullscreen mode via OS X controls. When that happens we should just handle
|
TransitionType transition = TransitionType::Windowed;
|
||||||
// it gracefully - no need to ASSERT.
|
if (aFullScreen) {
|
||||||
if (mInFullScreenMode == aFullScreen) {
|
// Decide whether to use fullscreen or emulated fullscreen.
|
||||||
return NS_OK;
|
transition = (aUseSystemTransition &&
|
||||||
|
(mWindow.collectionBehavior & NSWindowCollectionBehaviorFullScreenPrimary))
|
||||||
|
? TransitionType::Fullscreen
|
||||||
|
: TransitionType::EmulatedFullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInFullScreenTransition = true;
|
QueueTransition(transition);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
if (ShouldToggleNativeFullscreen(aFullScreen, aUseSystemTransition)) {
|
void nsCocoaWindow::QueueTransition(const TransitionType& aTransition) {
|
||||||
MOZ_ASSERT(mInNativeFullScreenMode != aFullScreen,
|
mTransitionsPending.push(aTransition);
|
||||||
"We shouldn't have been in native fullscreen.");
|
ProcessTransitions();
|
||||||
// Calling toggleFullScreen will result in windowDid(FailTo)?(Enter|Exit)FullScreen
|
}
|
||||||
// to be called from the OS. We will call EnteredFullScreen from those methods,
|
|
||||||
// where mInFullScreenMode will be set and a sizemode event will be dispatched.
|
void nsCocoaWindow::ProcessTransitions() {
|
||||||
|
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK
|
||||||
|
|
||||||
|
if (mInProcessTransitions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInProcessTransitions = true;
|
||||||
|
|
||||||
|
// Start a loop that will continue as long as we have transitions to process
|
||||||
|
// and we aren't waiting on an asynchronous transition to complete. Any
|
||||||
|
// transition that starts something async will `continue` this loop to exit.
|
||||||
|
while (!mTransitionsPending.empty() && !IsInTransition()) {
|
||||||
|
TransitionType nextTransition = mTransitionsPending.front();
|
||||||
|
|
||||||
|
// We have to check for some incompatible transition states, and if we find one,
|
||||||
|
// instead perform an alternative transition and leave the queue untouched. If
|
||||||
|
// we add one of these transitions, we suppress size mode events because we don't
|
||||||
|
// want to confuse listeners who are expecting to receive exactly one event when
|
||||||
|
// the requested transition has completed. The mSuppressSizeModeEvents flag is
|
||||||
|
// cleared in FinishCurrentTransitionIfMatching.
|
||||||
|
switch (nextTransition) {
|
||||||
|
case TransitionType::Fullscreen:
|
||||||
|
case TransitionType::EmulatedFullscreen:
|
||||||
|
case TransitionType::Windowed:
|
||||||
|
case TransitionType::Zoom:
|
||||||
|
// These can't handle miniaturized windows, so deminiaturize first.
|
||||||
|
if (mWindow.miniaturized) {
|
||||||
|
// Anything other than Windowed will be confused by the size mode event
|
||||||
|
// posted by deminiaturization.
|
||||||
|
if (nextTransition != TransitionType::Windowed) {
|
||||||
|
mSuppressSizeModeEvents = true;
|
||||||
|
}
|
||||||
|
mTransitionCurrent = Some(TransitionType::Deminiaturize);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TransitionType::Miniaturize:
|
||||||
|
// This can't handle fullscreen, so go to windowed first.
|
||||||
|
if (mInFullScreenMode) {
|
||||||
|
mSuppressSizeModeEvents = true;
|
||||||
|
mTransitionCurrent = Some(TransitionType::Windowed);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mTransitionCurrent is still empty, then we use the nextTransition and pop
|
||||||
|
// the queue.
|
||||||
|
if (mTransitionCurrent.isNothing()) {
|
||||||
|
mTransitionCurrent = Some(nextTransition);
|
||||||
|
mTransitionsPending.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (*mTransitionCurrent) {
|
||||||
|
case TransitionType::Fullscreen: {
|
||||||
|
if (!mInFullScreenMode) {
|
||||||
|
// This triggers an async animation, so continue.
|
||||||
[mWindow toggleFullScreen:nil];
|
[mWindow toggleFullScreen:nil];
|
||||||
} else {
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TransitionType::EmulatedFullscreen: {
|
||||||
|
if (!mInFullScreenMode) {
|
||||||
|
// This can be done synchronously.
|
||||||
if (mWidgetListener) {
|
if (mWidgetListener) {
|
||||||
mWidgetListener->FullscreenWillChange(aFullScreen);
|
mWidgetListener->FullscreenWillChange(true);
|
||||||
}
|
}
|
||||||
NSDisableScreenUpdates();
|
NSDisableScreenUpdates();
|
||||||
|
mSuppressSizeModeEvents = true;
|
||||||
// The order here matters. When we exit full screen mode, we need to show the
|
// The order here matters. When we exit full screen mode, we need to show the
|
||||||
// Dock first, otherwise the newly-created window won't have its minimize
|
// Dock first, otherwise the newly-created window won't have its minimize
|
||||||
// button enabled. See bug 526282.
|
// button enabled. See bug 526282.
|
||||||
nsCocoaUtils::HideOSChromeOnScreen(aFullScreen);
|
nsCocoaUtils::HideOSChromeOnScreen(true);
|
||||||
nsBaseWidget::InfallibleMakeFullScreen(aFullScreen);
|
nsBaseWidget::InfallibleMakeFullScreen(true);
|
||||||
|
mSuppressSizeModeEvents = false;
|
||||||
NSEnableScreenUpdates();
|
NSEnableScreenUpdates();
|
||||||
EnteredFullScreen(aFullScreen, /* aNativeMode */ false);
|
UpdateFullscreenState(true, false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
case TransitionType::Windowed: {
|
||||||
|
if (mInFullScreenMode) {
|
||||||
|
if (mInNativeFullScreenMode) {
|
||||||
|
// This triggers an async animation, so continue.
|
||||||
|
[mWindow toggleFullScreen:nil];
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// This can be done synchronously.
|
||||||
|
if (mWidgetListener) {
|
||||||
|
mWidgetListener->FullscreenWillChange(false);
|
||||||
|
}
|
||||||
|
NSDisableScreenUpdates();
|
||||||
|
mSuppressSizeModeEvents = true;
|
||||||
|
// The order here matters. When we exit full screen mode, we need to show the
|
||||||
|
// Dock first, otherwise the newly-created window won't have its minimize
|
||||||
|
// button enabled. See bug 526282.
|
||||||
|
nsCocoaUtils::HideOSChromeOnScreen(false);
|
||||||
|
nsBaseWidget::InfallibleMakeFullScreen(false);
|
||||||
|
mSuppressSizeModeEvents = false;
|
||||||
|
NSEnableScreenUpdates();
|
||||||
|
UpdateFullscreenState(false, false);
|
||||||
|
}
|
||||||
|
} else if (mWindow.zoomed) {
|
||||||
|
[mWindow zoom:nil];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
|
case TransitionType::Miniaturize:
|
||||||
|
if (!mWindow.miniaturized) {
|
||||||
|
// This triggers an async animation, so continue.
|
||||||
|
[mWindow miniaturize:nil];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TransitionType::Deminiaturize:
|
||||||
|
if (mWindow.miniaturized) {
|
||||||
|
// This triggers an async animation, so continue.
|
||||||
|
[mWindow deminiaturize:nil];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TransitionType::Zoom:
|
||||||
|
if (!mWindow.zoomed) {
|
||||||
|
[mWindow zoom:nil];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTransitionCurrent.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
mInProcessTransitions = false;
|
||||||
|
|
||||||
|
NS_OBJC_END_TRY_IGNORE_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsCocoaWindow::FinishCurrentTransition() {
|
||||||
|
mTransitionCurrent.reset();
|
||||||
|
mSuppressSizeModeEvents = false;
|
||||||
|
ProcessTransitions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsCocoaWindow::FinishCurrentTransitionIfMatching(const TransitionType& aTransition) {
|
||||||
|
// We've just finished some transition activity, and we're not sure whether it was
|
||||||
|
// triggered programmatically, or by the user. If it matches our current transition,
|
||||||
|
// then assume it was triggered programmatically and we can clean up that transition
|
||||||
|
// and start processing transitions again.
|
||||||
|
if (mTransitionCurrent.isSome() && (*mTransitionCurrent == aTransition)) {
|
||||||
|
// This matches our current transition. Since this function is called from
|
||||||
|
// nsWindowDelegate transition callbacks, we want to make sure those callbacks are
|
||||||
|
// all the way done before we start processing more transitions. To accomplish
|
||||||
|
// this, we dispatch our cleanup to happen on the next event loop. Doing this will
|
||||||
|
// ensure that any async native transition methods we call (like toggleFullscreen)
|
||||||
|
// will succeed.
|
||||||
|
NS_DispatchToCurrentThread(NewRunnableMethod("FinishCurrentTransition", this,
|
||||||
|
&nsCocoaWindow::FinishCurrentTransition));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coordinates are desktop pixels
|
// Coordinates are desktop pixels
|
||||||
|
@ -2096,12 +2221,12 @@ void nsCocoaWindow::DispatchSizeModeEvent() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsSizeMode newMode = GetWindowSizeMode(mWindow, mInFullScreenMode);
|
if (mSuppressSizeModeEvents) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't dispatch a sizemode event if:
|
nsSizeMode newMode = GetWindowSizeMode(mWindow, mInFullScreenMode);
|
||||||
// 1. the window is transitioning to fullscreen
|
if (mSizeMode == newMode) {
|
||||||
// 2. the new sizemode is the same as the current sizemode
|
|
||||||
if (mInFullScreenTransition || mSizeMode == newMode) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2138,7 +2263,6 @@ void nsCocoaWindow::ReportSizeEvent() {
|
||||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||||
|
|
||||||
UpdateBounds();
|
UpdateBounds();
|
||||||
|
|
||||||
if (mWidgetListener) {
|
if (mWidgetListener) {
|
||||||
LayoutDeviceIntRect innerBounds = GetClientBounds();
|
LayoutDeviceIntRect innerBounds = GetClientBounds();
|
||||||
mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
|
mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height);
|
||||||
|
@ -2665,19 +2789,50 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||||
|
|
||||||
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)proposedFrameSize {
|
- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)proposedFrameSize {
|
||||||
RollUpPopups();
|
RollUpPopups();
|
||||||
|
|
||||||
return proposedFrameSize;
|
return proposedFrameSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nsCocoaWindow::CocoaSendToplevelActivateEvents() {
|
||||||
|
if (mWidgetListener) {
|
||||||
|
mWidgetListener->WindowActivated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsCocoaWindow::CocoaSendToplevelDeactivateEvents() {
|
||||||
|
if (mWidgetListener) {
|
||||||
|
mWidgetListener->WindowDeactivated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsCocoaWindow::CocoaWindowDidResize() {
|
||||||
|
// It's important to update our bounds before we trigger any listeners. This
|
||||||
|
// ensures that our bounds are correct when GetScreenBounds is called.
|
||||||
|
UpdateBounds();
|
||||||
|
|
||||||
|
if (mUpdateFullscreenOnResize.isSome()) {
|
||||||
|
// Act as if the native fullscreen transition is complete, doing everything other
|
||||||
|
// than actually clearing the transition state, which will happen when one of the
|
||||||
|
// appropriate windowDid delegate methods is called.
|
||||||
|
bool toFullscreen = (*mUpdateFullscreenOnResize == TransitionType::Fullscreen);
|
||||||
|
mUpdateFullscreenOnResize.reset();
|
||||||
|
|
||||||
|
UpdateFullscreenState(toFullscreen, true);
|
||||||
|
ReportSizeEvent();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resizing might have changed our zoom state.
|
||||||
|
DispatchSizeModeEvent();
|
||||||
|
ReportSizeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
- (void)windowDidResize:(NSNotification*)aNotification {
|
- (void)windowDidResize:(NSNotification*)aNotification {
|
||||||
BaseWindow* window = [aNotification object];
|
BaseWindow* window = [aNotification object];
|
||||||
[window updateTrackingArea];
|
[window updateTrackingArea];
|
||||||
|
|
||||||
if (!mGeckoWindow) return;
|
if (!mGeckoWindow) return;
|
||||||
|
|
||||||
// Resizing might have changed our zoom state.
|
mGeckoWindow->CocoaWindowDidResize();
|
||||||
mGeckoWindow->DispatchSizeModeEvent();
|
|
||||||
mGeckoWindow->ReportSizeEvent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidChangeScreen:(NSNotification*)aNotification {
|
- (void)windowDidChangeScreen:(NSNotification*)aNotification {
|
||||||
|
@ -2714,20 +2869,13 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||||
if (!mGeckoWindow) {
|
if (!mGeckoWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mGeckoWindow->CocoaWindowWillEnterFullscreen(true);
|
||||||
mGeckoWindow->WillEnterFullScreen(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lion's full screen mode will bypass our internal fullscreen tracking, so
|
// Lion's full screen mode will bypass our internal fullscreen tracking, so
|
||||||
// we need to catch it when we transition and call our own methods, which in
|
// we need to catch it when we transition and call our own methods, which in
|
||||||
// turn will fire "fullscreen" events.
|
// turn will fire "fullscreen" events.
|
||||||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||||
if (!mGeckoWindow) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mGeckoWindow->EnteredFullScreen(true);
|
|
||||||
|
|
||||||
// On Yosemite, the NSThemeFrame class has two new properties --
|
// On Yosemite, the NSThemeFrame class has two new properties --
|
||||||
// titlebarView (an NSTitlebarView object) and titlebarContainerView (an
|
// titlebarView (an NSTitlebarView object) and titlebarContainerView (an
|
||||||
// NSTitlebarContainerView object). These are used to display the titlebar
|
// NSTitlebarContainerView object). These are used to display the titlebar
|
||||||
|
@ -2750,38 +2898,40 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||||
if ([titlebarContainerView respondsToSelector:@selector(setTransparent:)]) {
|
if ([titlebarContainerView respondsToSelector:@selector(setTransparent:)]) {
|
||||||
[titlebarContainerView setTransparent:NO];
|
[titlebarContainerView setTransparent:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mGeckoWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGeckoWindow->FinishCurrentTransitionIfMatching(nsCocoaWindow::TransitionType::Fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
||||||
if (!mGeckoWindow) {
|
if (!mGeckoWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mGeckoWindow->CocoaWindowWillEnterFullscreen(false);
|
||||||
mGeckoWindow->WillEnterFullScreen(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
||||||
if (!mGeckoWindow) {
|
if (!mGeckoWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mGeckoWindow->FinishCurrentTransitionIfMatching(nsCocoaWindow::TransitionType::Windowed);
|
||||||
mGeckoWindow->EnteredFullScreen(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
|
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
|
||||||
if (!mGeckoWindow) {
|
if (!mGeckoWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mGeckoWindow->CocoaWindowDidFailFullscreen(true);
|
||||||
mGeckoWindow->EnteredFullScreen(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidFailToExitFullScreen:(NSWindow*)window {
|
- (void)windowDidFailToExitFullScreen:(NSWindow*)window {
|
||||||
if (!mGeckoWindow) {
|
if (!mGeckoWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mGeckoWindow->CocoaWindowDidFailFullscreen(false);
|
||||||
mGeckoWindow->EnteredFullScreen(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidBecomeMain:(NSNotification*)aNotification {
|
- (void)windowDidBecomeMain:(NSNotification*)aNotification {
|
||||||
|
@ -2883,11 +3033,19 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidMiniaturize:(NSNotification*)aNotification {
|
- (void)windowDidMiniaturize:(NSNotification*)aNotification {
|
||||||
if (mGeckoWindow) mGeckoWindow->DispatchSizeModeEvent();
|
if (!mGeckoWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mGeckoWindow->DispatchSizeModeEvent();
|
||||||
|
mGeckoWindow->FinishCurrentTransitionIfMatching(nsCocoaWindow::TransitionType::Miniaturize);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)windowDidDeminiaturize:(NSNotification*)aNotification {
|
- (void)windowDidDeminiaturize:(NSNotification*)aNotification {
|
||||||
if (mGeckoWindow) mGeckoWindow->DispatchSizeModeEvent();
|
if (!mGeckoWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mGeckoWindow->DispatchSizeModeEvent();
|
||||||
|
mGeckoWindow->FinishCurrentTransitionIfMatching(nsCocoaWindow::TransitionType::Deminiaturize);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)proposedFrame {
|
- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)proposedFrame {
|
||||||
|
@ -2956,20 +3114,15 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
|
||||||
|
|
||||||
- (void)sendToplevelActivateEvents {
|
- (void)sendToplevelActivateEvents {
|
||||||
if (!mToplevelActiveState && mGeckoWindow) {
|
if (!mToplevelActiveState && mGeckoWindow) {
|
||||||
nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
|
mGeckoWindow->CocoaSendToplevelActivateEvents();
|
||||||
if (listener) {
|
|
||||||
listener->WindowActivated();
|
|
||||||
}
|
|
||||||
mToplevelActiveState = true;
|
mToplevelActiveState = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)sendToplevelDeactivateEvents {
|
- (void)sendToplevelDeactivateEvents {
|
||||||
if (mToplevelActiveState && mGeckoWindow) {
|
if (mToplevelActiveState && mGeckoWindow) {
|
||||||
nsIWidgetListener* listener = mGeckoWindow->GetWidgetListener();
|
mGeckoWindow->CocoaSendToplevelDeactivateEvents();
|
||||||
if (listener) {
|
|
||||||
listener->WindowDeactivated();
|
|
||||||
}
|
|
||||||
mToplevelActiveState = false;
|
mToplevelActiveState = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче