summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authormtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-17 04:30:56 +0000
committermtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-17 04:30:56 +0000
commit6b58b286091525e14c29f79a00c2682b2f71462f (patch)
treed1dcbcd599be4d3eec49d526e66c4ee840826910 /ash
parentf30e3dac09ae32a4d985b6018ef4f0a71257b776 (diff)
downloadchromium_src-6b58b286091525e14c29f79a00c2682b2f71462f.zip
chromium_src-6b58b286091525e14c29f79a00c2682b2f71462f.tar.gz
chromium_src-6b58b286091525e14c29f79a00c2682b2f71462f.tar.bz2
Yet another approach. Not pretty, but simple and works.
BUG=156772 Review URL: https://chromiumcodereview.appspot.com/11451002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173417 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/ash.gyp1
-rw-r--r--ash/launcher/launcher.cc27
-rw-r--r--ash/launcher/launcher.h13
-rw-r--r--ash/launcher/launcher_unittest.cc15
-rw-r--r--ash/wm/ash_activation_controller.cc37
-rw-r--r--ash/wm/ash_activation_controller.h8
-rw-r--r--ash/wm/ash_activation_controller_unittest.cc126
7 files changed, 221 insertions, 6 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 886a963..cac4596 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -580,6 +580,7 @@
'test/ash_unittests.cc',
'tooltips/tooltip_controller_unittest.cc',
'wm/activation_controller_unittest.cc',
+ 'wm/ash_activation_controller_unittest.cc',
'wm/base_layout_manager_unittest.cc',
'wm/cursor_manager_unittest.cc',
'wm/custom_frame_view_ash_unittest.cc',
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 8668879..a73f402 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -19,6 +19,7 @@
#include "ash/wm/shelf_layout_manager.h"
#include "ash/wm/window_properties.h"
#include "grit/ash_resources.h"
+#include "ui/aura/client/activation_client.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
@@ -68,9 +69,14 @@ class Launcher::DelegateView : public views::WidgetDelegate,
return View::GetWidget();
}
virtual bool CanActivate() const OVERRIDE {
- // We don't want mouse clicks to activate us, but we need to allow
- // activation when the user is using the keyboard (FocusCycler).
- return focus_cycler_ && focus_cycler_->widget_activating() == GetWidget();
+ // Allow to activate as fallback.
+ if (launcher_->activating_as_fallback_)
+ return true;
+ // Allow to activate from the focus cycler.
+ if (focus_cycler_ && focus_cycler_->widget_activating() == GetWidget())
+ return true;
+ // Disallow activating in other cases, especially when using mouse.
+ return false;
}
// BackgroundAnimatorDelegate overrides:
@@ -209,7 +215,8 @@ Launcher::Launcher(LauncherModel* launcher_model,
launcher_view_(NULL),
alignment_(SHELF_ALIGNMENT_BOTTOM),
delegate_(launcher_delegate),
- background_animator_(delegate_view_, 0, kLauncherBackgroundAlpha) {
+ background_animator_(delegate_view_, 0, kLauncherBackgroundAlpha),
+ activating_as_fallback_(false) {
widget_.reset(new views::Widget);
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
@@ -236,9 +243,12 @@ Launcher::Launcher(LauncherModel* launcher_model,
gfx::Size pref =
static_cast<views::View*>(launcher_view_)->GetPreferredSize();
widget_->SetBounds(gfx::Rect(pref));
+
+ widget_->AddObserver(this);
}
Launcher::~Launcher() {
+ widget_->RemoveObserver(this);
}
// static
@@ -406,6 +416,15 @@ void Launcher::SwitchToWindow(int window_index) {
}
}
+void Launcher::OnWidgetActivationChanged(views::Widget* widget, bool active) {
+ activating_as_fallback_ = false;
+ if (active) {
+ delegate_view_->SetPaneFocusAndFocusDefault();
+ } else {
+ delegate_view_->GetFocusManager()->ClearFocus();
+ }
+}
+
internal::LauncherView* Launcher::GetLauncherViewForTest() {
return launcher_view_;
}
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index 03de4ec..a934a5b 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -12,6 +12,7 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "ui/gfx/size.h"
+#include "ui/views/widget/widget_observer.h"
namespace aura {
class Window;
@@ -38,7 +39,7 @@ class LauncherIconObserver;
class LauncherDelegate;
class LauncherModel;
-class ASH_EXPORT Launcher {
+class ASH_EXPORT Launcher: public views::WidgetObserver {
public:
Launcher(LauncherModel* launcher_model,
LauncherDelegate* launcher_delegate,
@@ -124,6 +125,13 @@ class ASH_EXPORT Launcher {
aura::Window* window_container() { return window_container_; }
+ // Called by the activation delegate, before the launcher is activated
+ // when no other windows are visible.
+ void WillActivateAsFallback() { activating_as_fallback_ = true; }
+
+ // Overridden from views::WidgetObserver:
+ void OnWidgetActivationChanged(views::Widget* widget, bool active) OVERRIDE;
+
private:
class DelegateView;
@@ -149,6 +157,9 @@ class ASH_EXPORT Launcher {
// Used to animate the background.
internal::BackgroundAnimator background_animator_;
+ // Used then activation is forced from the activation delegate.
+ bool activating_as_fallback_;
+
DISALLOW_COPY_AND_ASSIGN(Launcher);
};
diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc
index 2c3f8a8..ea9135b 100644
--- a/ash/launcher/launcher_unittest.cc
+++ b/ash/launcher/launcher_unittest.cc
@@ -10,6 +10,7 @@
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/launcher_view_test_api.h"
+#include "ash/wm/window_util.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
@@ -77,4 +78,18 @@ TEST_F(LauncherTest, OpenBrowser) {
ASSERT_EQ(--button_count, test.GetButtonCount());
}
+// Launcher can't be activated on mouse click, but it is activable from
+// the focus cycler or as fallback.
+TEST_F(LauncherTest, ActivateAsFallback) {
+ Launcher* launcher = Launcher::ForPrimaryDisplay();
+ views::Widget* launcher_widget = launcher->widget();
+ EXPECT_FALSE(launcher_widget->CanActivate());
+
+ launcher->WillActivateAsFallback();
+ EXPECT_TRUE(launcher_widget->CanActivate());
+
+ wm::ActivateWindow(launcher_widget->GetNativeWindow());
+ EXPECT_FALSE(launcher_widget->CanActivate());
+}
+
} // namespace ash
diff --git a/ash/wm/ash_activation_controller.cc b/ash/wm/ash_activation_controller.cc
index f9cc006..261bd62 100644
--- a/ash/wm/ash_activation_controller.cc
+++ b/ash/wm/ash_activation_controller.cc
@@ -4,13 +4,16 @@
#include "ash/wm/ash_activation_controller.h"
+#include "ash/launcher/launcher.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
+#include "ash/shell_delegate.h"
#include "ash/wm/activation_controller.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace_controller.h"
#include "ui/views/corewm/window_modality_controller.h"
+#include "ui/views/widget/widget.h"
namespace ash {
namespace internal {
@@ -34,6 +37,10 @@ aura::Window* AshActivationController::WillActivateWindow(
if (window_modal_transient)
return window_modal_transient;
+ // Fallback to launcher
+ if (!window)
+ window = PrepareToActivateLauncher();
+
// Make sure the workspace manager switches to the workspace for window.
// Without this CanReceiveEvents() below returns false and activation never
// changes. CanReceiveEvents() returns false if |window| isn't in the active
@@ -77,5 +84,35 @@ aura::Window* AshActivationController::WillFocusWindow(
return window;
}
+aura::Window* AshActivationController::PrepareToActivateLauncher() {
+ // If workspace controller is not available, then it means that the root
+ // window is being destroyed. We can't activate any window then.
+ if (!GetRootWindowController(
+ Shell::GetActiveRootWindow())->workspace_controller()) {
+ return NULL;
+ }
+ // Fallback to a launcher only when Spoken feedback is enabled.
+ if (!Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled())
+ return NULL;
+ Launcher* launcher;
+ if (Shell::IsLauncherPerDisplayEnabled()) {
+ launcher = GetRootWindowController(
+ Shell::GetActiveRootWindow())->launcher();
+ } else {
+ launcher = Launcher::ForPrimaryDisplay();
+ }
+ // Launcher is not always available, eg. not in the login screen.
+ if (!launcher)
+ return NULL;
+ views::Widget* launcher_widget = launcher->widget();
+ // Launcher's window may be already destroyed in shutting down process.
+ if (!launcher_widget)
+ return NULL;
+ aura::Window* launcher_window = launcher_widget->GetNativeWindow();
+ // Notify launcher to allow activation via CanActivate().
+ launcher->WillActivateAsFallback();
+ return launcher_window;
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/wm/ash_activation_controller.h b/ash/wm/ash_activation_controller.h
index 0e9be46..c3961f9 100644
--- a/ash/wm/ash_activation_controller.h
+++ b/ash/wm/ash_activation_controller.h
@@ -5,6 +5,7 @@
#ifndef ASH_WM_ASH_ACTIVATION_CONTROLLER_H_
#define ASH_WM_ASH_ACTIVATION_CONTROLLER_H_
+#include "ash/ash_export.h"
#include "ash/wm/activation_controller_delegate.h"
#include "base/compiler_specific.h"
#include "base/basictypes.h"
@@ -12,7 +13,7 @@
namespace ash {
namespace internal {
-class AshActivationController : public ActivationControllerDelegate {
+class ASH_EXPORT AshActivationController : public ActivationControllerDelegate {
public:
AshActivationController();
virtual ~AshActivationController();
@@ -22,6 +23,11 @@ class AshActivationController : public ActivationControllerDelegate {
virtual aura::Window* WillActivateWindow(aura::Window* window) OVERRIDE;
virtual aura::Window* WillFocusWindow(aura::Window* window) OVERRIDE;
+ // Returns a handle to the launcher on the active root window which will
+ // be activated as fallback. Also notifies the launcher, so it can return
+ // true from Launcher::CanActivate().
+ aura::Window* PrepareToActivateLauncher();
+
DISALLOW_COPY_AND_ASSIGN(AshActivationController);
};
diff --git a/ash/wm/ash_activation_controller_unittest.cc b/ash/wm/ash_activation_controller_unittest.cc
new file mode 100644
index 0000000..3fc31df
--- /dev/null
+++ b/ash/wm/ash_activation_controller_unittest.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/ash_activation_controller.h"
+
+#include "ash/launcher/launcher.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell_delegate.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+
+namespace wm {
+
+namespace {
+
+class AshActivationControllerTest : public test::AshTestBase {
+ public:
+ AshActivationControllerTest()
+ : launcher_(NULL), launcher_widget_(NULL), launcher_window_(NULL) {}
+ virtual ~AshActivationControllerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ test::AshTestBase::SetUp();
+ ash_activation_controller_.reset(new internal::AshActivationController());
+ launcher_ = Launcher::ForPrimaryDisplay();
+ ASSERT_TRUE(launcher_);
+ launcher_widget_ = launcher_->widget();
+ ASSERT_TRUE(launcher_widget_);
+ launcher_window_ = launcher_widget_->GetNativeWindow();
+ ASSERT_TRUE(launcher_window_);
+ }
+
+ void SetSpokenFeedbackState(bool enabled) {
+ if (Shell::GetInstance()->delegate()->IsSpokenFeedbackEnabled() !=
+ enabled) {
+ Shell::GetInstance()->delegate()->ToggleSpokenFeedback(
+ A11Y_NOTIFICATION_NONE);
+ }
+ }
+
+ protected:
+ scoped_ptr<internal::ActivationControllerDelegate> ash_activation_controller_;
+ ash::Launcher* launcher_;
+ views::Widget* launcher_widget_;
+ aura::Window* launcher_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(AshActivationControllerTest);
+};
+
+TEST_F(AshActivationControllerTest, LauncherFallback) {
+ // When spoken feedback is disabled, then fallback should not occur.
+ {
+ SetSpokenFeedbackState(false);
+ aura::Window* result = ash_activation_controller_->WillActivateWindow(NULL);
+ EXPECT_EQ(NULL, result);
+ }
+
+ // When spoken feedback is enabled, then fallback should occur.
+ {
+ SetSpokenFeedbackState(true);
+ aura::Window* result = ash_activation_controller_->WillActivateWindow(NULL);
+ EXPECT_EQ(launcher_window_, result);
+ }
+
+ // No fallback when activating another window.
+ {
+ aura::Window* test_window = CreateTestWindowInShellWithId(0);
+ aura::Window* result = ash_activation_controller_->
+ WillActivateWindow(test_window);
+ EXPECT_EQ(test_window, result);
+ }
+}
+
+TEST_F(AshActivationControllerTest, LauncherFallbackOnShutdown) {
+ SetSpokenFeedbackState(true);
+ // While shutting down a root window controller, activation controller
+ // is notified about destroyed windows and therefore will try to activate
+ // a launcher as fallback, which would result in segmentation faults since
+ // the launcher's window or the workspace's controller may be already
+ // destroyed.
+ GetRootWindowController(Shell::GetActiveRootWindow())->CloseChildWindows();
+
+ aura::Window* result = ash_activation_controller_->WillActivateWindow(NULL);
+ EXPECT_EQ(NULL, result);
+}
+
+TEST_F(AshActivationControllerTest, LauncherEndToEndFallbackOnDestroyTest) {
+ // This test checks the whole fallback activation flow.
+ SetSpokenFeedbackState(true);
+
+ scoped_ptr<aura::Window> test_window(CreateTestWindowInShellWithId(0));
+ ActivateWindow(test_window.get());
+ ASSERT_EQ(test_window.get(), GetActiveWindow());
+
+ // Close the window.
+ test_window.reset();
+
+ // Verify if the launcher got activated as fallback.
+ ASSERT_EQ(launcher_window_, GetActiveWindow());
+}
+
+TEST_F(AshActivationControllerTest, LauncherEndToEndFallbackOnMinimizeTest) {
+ // This test checks the whole fallback activation flow.
+ SetSpokenFeedbackState(true);
+
+ scoped_ptr<aura::Window> test_window(CreateTestWindowInShellWithId(0));
+ ActivateWindow(test_window.get());
+ ASSERT_EQ(test_window.get(), GetActiveWindow());
+
+ // Minimize the window.
+ MinimizeWindow(test_window.get());
+
+ // Verify if the launcher got activated as fallback.
+ ASSERT_EQ(launcher_window_, GetActiveWindow());
+}
+
+} // namespace
+
+} // namespace wm
+
+} // namespace ash