Commit 50d05e11 authored by tapted's avatar tapted Committed by Commit bot

MacViews: NativeWidget -> Widget notifications: fullscreen, activation.

Fullscreen is asynchronous on Mac, so NativeWidgetMac needs to
coordinate with the ViewsNSWindowDelegate to behave in the manner views
code expects. Part of this is maintaining the restored bounds, which
Cocoa doesn't provide access to.

Activation also goes through the ViewsNSWindowDelegate. It's simpler but
needs an interactive_ui_test, which is added. Other NSWindowDelegate
messages are deferred to a follow-up.

Maximize is "implemented" as a no-op on Mac, since it's
indistinguishable from a large window (whereas views code typically
expects maximize to remove window borders).

This CL gets the following views_unittests passing:
  WidgetTest.GetRestoredBounds
  WidgetTest.ExitFullscreenRestoreState
  WidgetTest.FullscreenFrameLayout

Many Cocoa-native tests are also added to ensure user-initiated
operations are correctly observed.

BUG=403679, 378134

Review URL: https://codereview.chromium.org/388453002

Cr-Commit-Position: refs/heads/master@{#299263}
parent 1f00ac61
...@@ -207,6 +207,9 @@ enum { ...@@ -207,6 +207,9 @@ enum {
@end @end
BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification; BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowWillExitFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowDidEnterFullScreenNotification;
BASE_EXPORT extern "C" NSString* const NSWindowDidExitFullScreenNotification;
#endif // MAC_OS_X_VERSION_10_7 #endif // MAC_OS_X_VERSION_10_7
......
...@@ -11,6 +11,15 @@ ...@@ -11,6 +11,15 @@
NSString* const NSWindowWillEnterFullScreenNotification = NSString* const NSWindowWillEnterFullScreenNotification =
@"NSWindowWillEnterFullScreenNotification"; @"NSWindowWillEnterFullScreenNotification";
NSString* const NSWindowWillExitFullScreenNotification =
@"NSWindowWillExitFullScreenNotification";
NSString* const NSWindowDidEnterFullScreenNotification =
@"NSWindowDidEnterFullScreenNotification";
NSString* const NSWindowDidExitFullScreenNotification =
@"NSWindowDidExitFullScreenNotification";
#endif // MAC_OS_X_VERSION_10_7 #endif // MAC_OS_X_VERSION_10_7
// Replicate specific 10.10 SDK declarations for building with prior SDKs. // Replicate specific 10.10 SDK declarations for building with prior SDKs.
......
...@@ -115,18 +115,6 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) { ...@@ -115,18 +115,6 @@ IN_PROC_BROWSER_TEST_F(NativeAppWindowCocoaBrowserTest, HideShowWithApp) {
EXPECT_FALSE([other_ns_window isVisible]); EXPECT_FALSE([other_ns_window isVisible]);
} }
// Only test fullscreen for 10.7 and above.
// Replicate specific 10.7 SDK declarations for building with prior SDKs.
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
NSString* const NSWindowDidEnterFullScreenNotification =
@"NSWindowDidEnterFullScreenNotification";
NSString* const NSWindowDidExitFullScreenNotification =
@"NSWindowDidExitFullScreenNotification";
#endif // MAC_OS_X_VERSION_10_7
@interface ScopedNotificationWatcher : NSObject { @interface ScopedNotificationWatcher : NSObject {
@private @private
BOOL received_; BOOL received_;
......
...@@ -52,14 +52,33 @@ class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate, ...@@ -52,14 +52,33 @@ class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate,
// Called internally by the NSWindowDelegate when the window is closing. // Called internally by the NSWindowDelegate when the window is closing.
void OnWindowWillClose(); void OnWindowWillClose();
// Called by the NSWindowDelegate when a fullscreen operation begins. If
// |target_fullscreen_state| is true, the target state is fullscreen.
// Otherwise, a transition has begun to come out of fullscreen.
void OnFullscreenTransitionStart(bool target_fullscreen_state);
// Called when a fullscreen transition completes. If target_fullscreen_state()
// does not match |actual_fullscreen_state|, a new transition will begin.
void OnFullscreenTransitionComplete(bool actual_fullscreen_state);
// Transition the window into or out of fullscreen. This will immediately
// invert the value of target_fullscreen_state().
void ToggleDesiredFullscreenState();
// See widget.h for documentation. // See widget.h for documentation.
InputMethod* CreateInputMethod(); InputMethod* CreateInputMethod();
ui::InputMethod* GetHostInputMethod(); ui::InputMethod* GetHostInputMethod();
// The restored bounds will be derived from the current NSWindow frame unless
// fullscreen or transitioning between fullscreen states.
gfx::Rect GetRestoredBounds() const;
NativeWidgetMac* native_widget_mac() { return native_widget_mac_; } NativeWidgetMac* native_widget_mac() { return native_widget_mac_; }
BridgedContentView* ns_view() { return bridged_view_; } BridgedContentView* ns_view() { return bridged_view_; }
NSWindow* ns_window() { return window_; } NSWindow* ns_window() { return window_; }
bool target_fullscreen_state() const { return target_fullscreen_state_; }
// Overridden from internal::InputMethodDelegate: // Overridden from internal::InputMethodDelegate:
virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) override; virtual void DispatchKeyEventPostIME(const ui::KeyEvent& key) override;
...@@ -74,6 +93,19 @@ class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate, ...@@ -74,6 +93,19 @@ class VIEWS_EXPORT BridgedNativeWidget : public internal::InputMethodDelegate,
scoped_ptr<ui::InputMethod> input_method_; scoped_ptr<ui::InputMethod> input_method_;
FocusManager* focus_manager_; // Weak. Owned by our Widget. FocusManager* focus_manager_; // Weak. Owned by our Widget.
// Tracks the bounds when the window last started entering fullscreen. Used to
// provide an answer for GetRestoredBounds(), but not ever sent to Cocoa (it
// has its own copy, but doesn't provide access to it).
gfx::Rect bounds_before_fullscreen_;
// Whether this window wants to be fullscreen. If a fullscreen animation is in
// progress then it might not be actually fullscreen.
bool target_fullscreen_state_;
// Whether this window is in a fullscreen transition, and the fullscreen state
// can not currently be changed.
bool in_fullscreen_transition_;
// Overridden from FocusChangeListener: // Overridden from FocusChangeListener:
virtual void OnWillChangeFocus(View* focused_before, virtual void OnWillChangeFocus(View* focused_before,
View* focused_now) override; View* focused_now) override;
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/bridged_native_widget.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mac_util.h"
#import "base/mac/sdk_forward_declarations.h"
#include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method.h"
#include "ui/base/ime/input_method_factory.h" #include "ui/base/ime/input_method_factory.h"
#include "ui/base/ui_base_switches_util.h" #include "ui/base/ui_base_switches_util.h"
#import "ui/gfx/mac/coordinate_conversion.h"
#import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/views_nswindow_delegate.h" #import "ui/views/cocoa/views_nswindow_delegate.h"
#include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/native_widget_mac.h"
...@@ -19,7 +22,10 @@ ...@@ -19,7 +22,10 @@
namespace views { namespace views {
BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent) BridgedNativeWidget::BridgedNativeWidget(NativeWidgetMac* parent)
: native_widget_mac_(parent), focus_manager_(NULL) { : native_widget_mac_(parent),
focus_manager_(NULL),
target_fullscreen_state_(false),
in_fullscreen_transition_(false) {
DCHECK(parent); DCHECK(parent);
window_delegate_.reset( window_delegate_.reset(
[[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]);
...@@ -44,6 +50,11 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window, ...@@ -44,6 +50,11 @@ void BridgedNativeWidget::Init(base::scoped_nsobject<NSWindow> window,
window_.swap(window); window_.swap(window);
[window_ setDelegate:window_delegate_]; [window_ setDelegate:window_delegate_];
// Validate the window's initial state, otherwise the bridge's initial
// tracking state will be incorrect.
DCHECK(![window_ isVisible]);
DCHECK_EQ(0u, [window_ styleMask] & NSFullScreenWindowMask);
if (params.parent) { if (params.parent) {
// Use NSWindow to manage child windows. This won't automatically close them // Use NSWindow to manage child windows. This won't automatically close them
// but it will maintain relative positioning of the window layer and origin. // but it will maintain relative positioning of the window layer and origin.
...@@ -89,6 +100,55 @@ void BridgedNativeWidget::OnWindowWillClose() { ...@@ -89,6 +100,55 @@ void BridgedNativeWidget::OnWindowWillClose() {
native_widget_mac_->OnWindowWillClose(); native_widget_mac_->OnWindowWillClose();
} }
void BridgedNativeWidget::OnFullscreenTransitionStart(
bool target_fullscreen_state) {
DCHECK_NE(target_fullscreen_state, target_fullscreen_state_);
target_fullscreen_state_ = target_fullscreen_state;
in_fullscreen_transition_ = true;
// If going into fullscreen, store an answer for GetRestoredBounds().
if (target_fullscreen_state)
bounds_before_fullscreen_ = gfx::ScreenRectFromNSRect([window_ frame]);
}
void BridgedNativeWidget::OnFullscreenTransitionComplete(
bool actual_fullscreen_state) {
in_fullscreen_transition_ = false;
if (target_fullscreen_state_ == actual_fullscreen_state)
return;
// First update to reflect reality so that OnTargetFullscreenStateChanged()
// expects the change.
target_fullscreen_state_ = actual_fullscreen_state;
ToggleDesiredFullscreenState();
DCHECK_NE(target_fullscreen_state_, actual_fullscreen_state);
}
void BridgedNativeWidget::ToggleDesiredFullscreenState() {
if (base::mac::IsOSSnowLeopard()) {
NOTIMPLEMENTED();
return; // TODO(tapted): Implement this for Snow Leopard.
}
// If there is currently an animation into or out of fullscreen, then AppKit
// emits the string "not in fullscreen state" to stdio and does nothing. For
// this case, schedule a transition back into the desired state when the
// animation completes.
if (in_fullscreen_transition_) {
target_fullscreen_state_ = !target_fullscreen_state_;
return;
}
// Since fullscreen requests are ignored if the collection behavior does not
// allow it, save the collection behavior and restore it after.
NSWindowCollectionBehavior behavior = [window_ collectionBehavior];
[window_ setCollectionBehavior:behavior |
NSWindowCollectionBehaviorFullScreenPrimary];
[window_ toggleFullScreen:nil];
[window_ setCollectionBehavior:behavior];
DCHECK(in_fullscreen_transition_);
}
InputMethod* BridgedNativeWidget::CreateInputMethod() { InputMethod* BridgedNativeWidget::CreateInputMethod() {
if (switches::IsTextInputFocusManagerEnabled()) if (switches::IsTextInputFocusManagerEnabled())
return new NullInputMethod(); return new NullInputMethod();
...@@ -105,6 +165,13 @@ ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() { ...@@ -105,6 +165,13 @@ ui::InputMethod* BridgedNativeWidget::GetHostInputMethod() {
return input_method_.get(); return input_method_.get();
} }
gfx::Rect BridgedNativeWidget::GetRestoredBounds() const {
if (target_fullscreen_state_ || in_fullscreen_transition_)
return bounds_before_fullscreen_;
return gfx::ScreenRectFromNSRect([window_ frame]);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// BridgedNativeWidget, internal::InputMethodDelegate: // BridgedNativeWidget, internal::InputMethodDelegate:
......
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import "base/mac/sdk_forward_declarations.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#import "testing/gtest_mac.h" #import "testing/gtest_mac.h"
...@@ -37,6 +40,91 @@ NSRange EmptyRange() { ...@@ -37,6 +40,91 @@ NSRange EmptyRange() {
} // namespace } // namespace
@interface NativeWidgetMacNotificationWaiter : NSObject {
@private
scoped_ptr<base::RunLoop> runLoop_;
base::scoped_nsobject<NSWindow> window_;
int enterCount_;
int exitCount_;
int targetEnterCount_;
int targetExitCount_;
}
@property(readonly, nonatomic) int enterCount;
@property(readonly, nonatomic) int exitCount;
// Initialize for the given window and start tracking notifications.
- (id)initWithWindow:(NSWindow*)window;
// Keep spinning a run loop until the enter and exit counts match.
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount;
// private:
// Exit the RunLoop if there is one and the counts being tracked match.
- (void)maybeQuitForChangedArg:(int*)changedArg;
- (void)onEnter:(NSNotification*)notification;
- (void)onExit:(NSNotification*)notification;
@end
@implementation NativeWidgetMacNotificationWaiter
@synthesize enterCount = enterCount_;
@synthesize exitCount = exitCount_;
- (id)initWithWindow:(NSWindow*)window {
if ((self = [super init])) {
window_.reset([window retain]);
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(onEnter:)
name:NSWindowDidEnterFullScreenNotification
object:window];
[defaultCenter addObserver:self
selector:@selector(onExit:)
name:NSWindowDidExitFullScreenNotification
object:window];
}
return self;
}
- (void)dealloc {
DCHECK(!runLoop_);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
- (void)waitForEnterCount:(int)enterCount exitCount:(int)exitCount {
if (enterCount_ >= enterCount && exitCount_ >= exitCount)
return;
targetEnterCount_ = enterCount;
targetExitCount_ = exitCount;
runLoop_.reset(new base::RunLoop);
runLoop_->Run();
runLoop_.reset();
}
- (void)maybeQuitForChangedArg:(int*)changedArg {
++*changedArg;
if (!runLoop_)
return;
if (enterCount_ >= targetEnterCount_ && exitCount_ >= targetExitCount_)
runLoop_->Quit();
}
- (void)onEnter:(NSNotification*)notification {
[self maybeQuitForChangedArg:&enterCount_];
}
- (void)onExit:(NSNotification*)notification {
[self maybeQuitForChangedArg:&exitCount_];
}
@end
namespace views { namespace views {
namespace test { namespace test {
...@@ -148,6 +236,9 @@ void BridgedNativeWidgetTest::SetUp() { ...@@ -148,6 +236,9 @@ void BridgedNativeWidgetTest::SetUp() {
view_.reset(new views::View); view_.reset(new views::View);
base::scoped_nsobject<NSWindow> window([test_window() retain]); base::scoped_nsobject<NSWindow> window([test_window() retain]);
// BridgedNativeWidget expects to be initialized with a hidden (deferred)
// window.
[window orderOut:nil];
EXPECT_FALSE([window delegate]); EXPECT_FALSE([window delegate]);
bridge()->Init(window, Widget::InitParams()); bridge()->Init(window, Widget::InitParams());
...@@ -156,6 +247,8 @@ void BridgedNativeWidgetTest::SetUp() { ...@@ -156,6 +247,8 @@ void BridgedNativeWidgetTest::SetUp() {
bridge()->SetRootView(view_.get()); bridge()->SetRootView(view_.get());
ns_view_ = bridge()->ns_view(); ns_view_ = bridge()->ns_view();
// Pretend it has been shown via NativeWidgetMac::Show().
[window orderFront:nil];
[test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()]; [test_window() makePretendKeyWindowAndSetFirstResponder:bridge()->ns_view()];
} }
...@@ -246,7 +339,7 @@ TEST_F(BridgedNativeWidgetInitTest, ParentWindowNotNativeWidgetMac) { ...@@ -246,7 +339,7 @@ TEST_F(BridgedNativeWidgetInitTest, ParentWindowNotNativeWidgetMac) {
[[NSWindow alloc] initWithContentRect:NSMakeRect(50, 50, 400, 300) [[NSWindow alloc] initWithContentRect:NSMakeRect(50, 50, 400, 300)
styleMask:NSBorderlessWindowMask styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered backing:NSBackingStoreBuffered
defer:NO]); defer:YES]);
[child_window setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. [child_window setReleasedWhenClosed:NO]; // Owned by scoped_nsobject.
EXPECT_FALSE([child_window parentWindow]); EXPECT_FALSE([child_window parentWindow]);
...@@ -415,5 +508,92 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) { ...@@ -415,5 +508,92 @@ TEST_F(BridgedNativeWidgetTest, TextInput_DeleteForward) {
EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
} }
// Tests for correct fullscreen tracking, regardless of whether it is initiated
// by the Widget code or elsewhere (e.g. by the user).
TEST_F(BridgedNativeWidgetTest, FullscreenSynchronousState) {
EXPECT_FALSE(widget_->IsFullscreen());
// Allow user-initiated fullscreen changes on the Window.
[test_window()
setCollectionBehavior:[test_window() collectionBehavior] |
NSWindowCollectionBehaviorFullScreenPrimary];
base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
[[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
// Simualate a user-initiated fullscreen. Note trying to to this again before
// spinning a runloop will cause Cocoa to emit text to stdio and ignore it.
[test_window() toggleFullScreen:nil];
EXPECT_TRUE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
// Note there's now an animation running. While that's happening, toggling the
// state should work as expected, but do "nothing".
widget_->SetFullscreen(false);
EXPECT_FALSE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
widget_->SetFullscreen(false); // Same request - should no-op.
EXPECT_FALSE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
widget_->SetFullscreen(true);
EXPECT_TRUE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
// Always finish out of fullscreen. Otherwise there are 4 NSWindow objects
// that Cocoa creates which don't close themselves and will be seen by the Mac
// test harness on teardown. Note that the test harness will be waiting until
// all animations complete, since these temporary animation windows will not
// be removed from the window list until they do.
widget_->SetFullscreen(false);
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
// Now we must wait for the notifications. Since, if the widget is torn down,
// the NSWindowDelegate is removed, and the pending request to take out of
// fullscreen is lost. Since a message loop has not yet spun up in this test
// we can reliably say there will be one enter and one exit, despite all the
// toggling above.
base::MessageLoopForUI message_loop;
[waiter waitForEnterCount:1 exitCount:1];
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
}
// Test fullscreen without overlapping calls and without changing collection
// behaviour on the test window.
TEST_F(BridgedNativeWidgetTest, FullscreenEnterAndExit) {
base::MessageLoopForUI message_loop;
base::scoped_nsobject<NativeWidgetMacNotificationWaiter> waiter(
[[NativeWidgetMacNotificationWaiter alloc] initWithWindow:test_window()]);
EXPECT_FALSE(widget_->IsFullscreen());
const gfx::Rect restored_bounds = widget_->GetRestoredBounds();
EXPECT_FALSE(restored_bounds.IsEmpty());
// Ensure this works without having to change collection behavior as for the
// test above.
widget_->SetFullscreen(true);
EXPECT_TRUE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
// Should be zero until the runloop spins.
EXPECT_EQ(0, [waiter enterCount]);
[waiter waitForEnterCount:1 exitCount:0];
// Verify it hasn't exceeded.
EXPECT_EQ(1, [waiter enterCount]);
EXPECT_EQ(0, [waiter exitCount]);
EXPECT_TRUE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
widget_->SetFullscreen(false);
EXPECT_FALSE(widget_->IsFullscreen());
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
[waiter waitForEnterCount:1 exitCount:1];
EXPECT_EQ(1, [waiter enterCount]);
EXPECT_EQ(1, [waiter exitCount]);
EXPECT_EQ(restored_bounds, widget_->GetRestoredBounds());
}
} // namespace test } // namespace test
} // namespace views } // namespace views
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/logging.h" #include "base/logging.h"
#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/widget/native_widget_mac.h"
@implementation ViewsNSWindowDelegate @implementation ViewsNSWindowDelegate
...@@ -23,9 +24,44 @@ ...@@ -23,9 +24,44 @@
// NSWindowDelegate implementation. // NSWindowDelegate implementation.
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
// TODO(tapted): Handle these failure notifications, and simulate in a test.
NOTREACHED();
}
- (void)windowDidFailToExitFullScreen:(NSWindow*)window {
NOTREACHED();
}
- (void)windowDidBecomeKey:(NSNotification*)notification {
parent_->native_widget_mac()->GetWidget()->OnNativeWidgetActivationChanged(
true);
}
- (void)windowDidResignKey:(NSNotification*)notification {
parent_->native_widget_mac()->GetWidget()->OnNativeWidgetActivationChanged(
false);
}
- (void)windowWillClose:(NSNotification*)notification { - (void)windowWillClose:(NSNotification*)notification {
DCHECK([parent_->ns_window() isEqual:[notification object]]); DCHECK([parent_->ns_window() isEqual:[notification object]]);
parent_->OnWindowWillClose(); parent_->OnWindowWillClose();
} }
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
parent_->OnFullscreenTransitionStart(true);
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
parent_->OnFullscreenTransitionComplete(true);
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
parent_->OnFullscreenTransitionStart(false);
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
parent_->OnFullscreenTransitionComplete(false);
}
@end @end
...@@ -269,8 +269,7 @@ gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const { ...@@ -269,8 +269,7 @@ gfx::Rect NativeWidgetMac::GetClientAreaBoundsInScreen() const {
} }
gfx::Rect NativeWidgetMac::GetRestoredBounds() const { gfx::Rect NativeWidgetMac::GetRestoredBounds() const {
NOTIMPLEMENTED(); return bridge_ ? bridge_->GetRestoredBounds() : gfx::Rect();
return gfx::Rect();
} }
void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) { void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) {
...@@ -405,7 +404,7 @@ void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) { ...@@ -405,7 +404,7 @@ void NativeWidgetMac::SetVisibleOnAllWorkspaces(bool always_visible) {
} }
void NativeWidgetMac::Maximize() { void NativeWidgetMac::Maximize() {
NOTIMPLEMENTED(); NOTIMPLEMENTED(); // See IsMaximized().
} }
void NativeWidgetMac::Minimize() { void NativeWidgetMac::Minimize() {
...@@ -413,7 +412,8 @@ void NativeWidgetMac::Minimize() { ...@@ -413,7 +412,8 @@ void NativeWidgetMac::Minimize() {
} }
bool NativeWidgetMac::IsMaximized() const { bool NativeWidgetMac::IsMaximized() const {
NOTIMPLEMENTED(); // The window frame isn't altered on Mac unless going fullscreen. The green
// "+" button just makes the window bigger. So, always false.
return false; return false;
} }
...@@ -427,12 +427,14 @@ void NativeWidgetMac::Restore() { ...@@ -427,12 +427,14 @@ void NativeWidgetMac::Restore() {
} }
void NativeWidgetMac::SetFullscreen(bool fullscreen) { void NativeWidgetMac::SetFullscreen(bool fullscreen) {
NOTIMPLEMENTED(); if (!bridge_ || fullscreen == IsFullscreen())
return;
bridge_->ToggleDesiredFullscreenState();
} }
bool NativeWidgetMac::IsFullscreen() const { bool NativeWidgetMac::IsFullscreen() const {
NOTIMPLEMENTED(); return bridge_ && bridge_->target_fullscreen_state();
return false;
} }
void NativeWidgetMac::SetOpacity(unsigned char opacity) { void NativeWidgetMac::SetOpacity(unsigned char opacity) {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include "ui/views/test/widget_test.h" #include "ui/views/test/widget_test.h"
#include "ui/views/test/test_widget_observer.h"
namespace views { namespace views {
namespace test { namespace test {
...@@ -17,7 +18,10 @@ class NativeWidgetMacInteractiveUITest ...@@ -17,7 +18,10 @@ class NativeWidgetMacInteractiveUITest
: public WidgetTest, : public WidgetTest,
public ::testing::WithParamInterface<bool> { public ::testing::WithParamInterface<bool> {
public: public:
NativeWidgetMacInteractiveUITest() {} class Observer;
NativeWidgetMacInteractiveUITest()
: activationCount_(0), deactivationCount_(0) {}
Widget* MakeWidget() { Widget* MakeWidget() {
return GetParam() ? CreateTopLevelFramelessPlatformWidget() return GetParam() ? CreateTopLevelFramelessPlatformWidget()
...@@ -33,21 +37,70 @@ class NativeWidgetMacInteractiveUITest ...@@ -33,21 +37,70 @@ class NativeWidgetMacInteractiveUITest
WidgetTest::SetUp(); WidgetTest::SetUp();
} }
protected:
scoped_ptr<Observer> observer_;
int activationCount_;
int deactivationCount_;
private: private:
DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacInteractiveUITest); DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacInteractiveUITest);
}; };
class NativeWidgetMacInteractiveUITest::Observer : public TestWidgetObserver {
public:
Observer(NativeWidgetMacInteractiveUITest* parent, Widget* widget)
: TestWidgetObserver(widget), parent_(parent) {}
virtual void OnWidgetActivationChanged(Widget* widget, bool active) OVERRIDE {
if (active)
parent_->activationCount_++;
else
parent_->deactivationCount_++;
}
private:
NativeWidgetMacInteractiveUITest* parent_;
DISALLOW_COPY_AND_ASSIGN(Observer);
};
// Test that showing a window causes it to attain global keyWindow status. // Test that showing a window causes it to attain global keyWindow status.
TEST_P(NativeWidgetMacInteractiveUITest, ShowAttainsKeyStatus) { TEST_P(NativeWidgetMacInteractiveUITest, ShowAttainsKeyStatus) {
Widget* widget = MakeWidget(); Widget* widget = MakeWidget();
observer_.reset(new Observer(this, widget));
EXPECT_FALSE(widget->IsActive()); EXPECT_FALSE(widget->IsActive());
EXPECT_EQ(0, activationCount_);
widget->Show(); widget->Show();
EXPECT_TRUE(widget->IsActive()); EXPECT_TRUE(widget->IsActive());
RunPendingMessages(); RunPendingMessages();
EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]); EXPECT_TRUE([widget->GetNativeWindow() isKeyWindow]);
EXPECT_EQ(1, activationCount_);
EXPECT_EQ(0, deactivationCount_);
// Now check that losing and gaining key status due events outside of Widget
// works correctly.
Widget* widget2 = MakeWidget(); // Note: not observed.
EXPECT_EQ(0, deactivationCount_);
widget2->Show();
EXPECT_EQ(1, deactivationCount_);
RunPendingMessages();
EXPECT_FALSE(widget->IsActive());
EXPECT_EQ(1, deactivationCount_);
EXPECT_EQ(1, activationCount_);
[widget->GetNativeWindow() makeKeyAndOrderFront:nil];
RunPendingMessages();
EXPECT_TRUE(widget->IsActive());
EXPECT_EQ(1, deactivationCount_);
EXPECT_EQ(2, activationCount_);
widget2->CloseNow();
widget->CloseNow(); widget->CloseNow();
EXPECT_EQ(1, deactivationCount_);
EXPECT_EQ(2, activationCount_);
} }
// Test that ShowInactive does not take keyWindow status from an active window. // Test that ShowInactive does not take keyWindow status from an active window.
......
...@@ -928,7 +928,9 @@ TEST_F(WidgetTest, GetWindowBoundsInScreen) { ...@@ -928,7 +928,9 @@ TEST_F(WidgetTest, GetWindowBoundsInScreen) {
widget->CloseNow(); widget->CloseNow();
} }
#if defined(false) // Before being enabled on Mac, this was #ifdef(false).
// TODO(tapted): Fix this for DesktopNativeWidgets on other platforms.
#if defined(OS_MACOSX)
// Aura needs shell to maximize/fullscreen window. // Aura needs shell to maximize/fullscreen window.
// NativeWidgetGtk doesn't implement GetRestoredBounds. // NativeWidgetGtk doesn't implement GetRestoredBounds.
TEST_F(WidgetTest, GetRestoredBounds) { TEST_F(WidgetTest, GetRestoredBounds) {
...@@ -938,8 +940,14 @@ TEST_F(WidgetTest, GetRestoredBounds) { ...@@ -938,8 +940,14 @@ TEST_F(WidgetTest, GetRestoredBounds) {
toplevel->Show(); toplevel->Show();
toplevel->Maximize(); toplevel->Maximize();
RunPendingMessages(); RunPendingMessages();
#if defined(OS_MACOSX)
// Current expectation on Mac is to do nothing on Maximize.
EXPECT_EQ(toplevel->GetWindowBoundsInScreen().ToString(),
toplevel->GetRestoredBounds().ToString());
#else
EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(), EXPECT_NE(toplevel->GetWindowBoundsInScreen().ToString(),
toplevel->GetRestoredBounds().ToString()); toplevel->GetRestoredBounds().ToString());
#endif
EXPECT_GT(toplevel->GetRestoredBounds().width(), 0); EXPECT_GT(toplevel->GetRestoredBounds().width(), 0);
EXPECT_GT(toplevel->GetRestoredBounds().height(), 0); EXPECT_GT(toplevel->GetRestoredBounds().height(), 0);
...@@ -975,6 +983,9 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) { ...@@ -975,6 +983,9 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) {
// And it should still be in normal state after getting out of full screen. // And it should still be in normal state after getting out of full screen.
EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel)); EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetWidgetShowState(toplevel));
// On Mac, a "maximized" state is indistinguishable from a window that just
// fills the screen, so nothing to check there.
#if !defined(OS_MACOSX)
// Now, make it maximized. // Now, make it maximized.
toplevel->Maximize(); toplevel->Maximize();
EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
...@@ -986,6 +997,7 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) { ...@@ -986,6 +997,7 @@ TEST_F(WidgetTest, ExitFullscreenRestoreState) {
// And it stays maximized after getting out of full screen. // And it stays maximized after getting out of full screen.
EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel)); EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetWidgetShowState(toplevel));
#endif
// Clean up. // Clean up.
toplevel->Close(); toplevel->Close();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment