summaryrefslogtreecommitdiff
path: root/ash
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-12-18 16:25:09 +0000
committerTorne (Richard Coles) <torne@google.com>2013-12-18 16:25:09 +0000
commita3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7 (patch)
treedafc1c6417406a7fbd422ad0bb890e96909ef564 /ash
parentd5f893c0bc79db3066bb5ae5d3d972ba1be7dd5f (diff)
downloadchromium_org-a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7.tar.gz
Merge from Chromium at DEPS revision 240154
This commit was generated by merge_to_master.py. Change-Id: I8f2ba858cf0e7f413dddedc2ae91dc37f7136c2e
Diffstat (limited to 'ash')
-rw-r--r--ash/OWNERS3
-rw-r--r--ash/accelerators/accelerator_commands.cc5
-rw-r--r--ash/accelerators/accelerator_controller.cc837
-rw-r--r--ash/accelerators/accelerator_controller_unittest.cc4
-rw-r--r--ash/accelerators/accelerator_table.cc4
-rw-r--r--ash/accelerators/accelerator_table.h1
-rw-r--r--ash/ash.gyp65
-rw-r--r--ash/ash_constants.cc2
-rw-r--r--ash/ash_constants.h5
-rw-r--r--ash/ash_switches.cc9
-rw-r--r--ash/display/display_error_observer_chromeos.cc4
-rw-r--r--ash/display/resolution_notification_controller.cc3
-rw-r--r--ash/drag_drop/drag_drop_controller.cc8
-rw-r--r--ash/launcher/launcher.cc18
-rw-r--r--ash/launcher/launcher.h8
-rw-r--r--ash/launcher/launcher_item_delegate_manager.cc74
-rw-r--r--ash/launcher/launcher_item_delegate_manager.h69
-rw-r--r--ash/launcher/launcher_types.cc13
-rw-r--r--ash/launcher/launcher_types.h25
-rw-r--r--ash/launcher/launcher_unittest.cc20
-rw-r--r--ash/multi_profile_uma.cc18
-rw-r--r--ash/multi_profile_uma.h20
-rw-r--r--ash/popup_message.h1
-rw-r--r--ash/resources/ash_resources.grd1
-rw-r--r--ash/resources/default_100_percent/common/launcher/launcher_corner.pngbin0 -> 1064 bytes
-rw-r--r--ash/resources/default_200_percent/common/launcher/launcher_corner.pngbin0 -> 1073 bytes
-rw-r--r--ash/root_window_controller.cc22
-rw-r--r--ash/root_window_controller.h7
-rw-r--r--ash/root_window_controller_unittest.cc40
-rw-r--r--ash/scoped_target_root_window.h3
-rw-r--r--ash/session_state_delegate.h12
-rw-r--r--ash/session_state_delegate_stub.cc2
-rw-r--r--ash/session_state_delegate_stub.h2
-rw-r--r--ash/shelf/alternate_app_list_button.cc7
-rw-r--r--ash/shelf/app_list_button.cc4
-rw-r--r--ash/shelf/app_list_shelf_item_delegate.cc4
-rw-r--r--ash/shelf/app_list_shelf_item_delegate.h13
-rw-r--r--ash/shelf/background_animator.cc5
-rw-r--r--ash/shelf/background_animator.h15
-rw-r--r--ash/shelf/shelf_button.cc10
-rw-r--r--ash/shelf/shelf_button.h1
-rw-r--r--ash/shelf/shelf_delegate.h (renamed from ash/launcher/launcher_delegate.h)16
-rw-r--r--ash/shelf/shelf_item_delegate.h (renamed from ash/launcher/launcher_item_delegate.h)31
-rw-r--r--ash/shelf/shelf_item_delegate_manager.cc72
-rw-r--r--ash/shelf/shelf_item_delegate_manager.h67
-rw-r--r--ash/shelf/shelf_layout_manager.cc42
-rw-r--r--ash/shelf/shelf_layout_manager.h11
-rw-r--r--ash/shelf/shelf_layout_manager_observer.h6
-rw-r--r--ash/shelf/shelf_layout_manager_unittest.cc62
-rw-r--r--ash/shelf/shelf_menu_model.h29
-rw-r--r--ash/shelf/shelf_tooltip_manager.cc2
-rw-r--r--ash/shelf/shelf_util.cc26
-rw-r--r--ash/shelf/shelf_util.h19
-rw-r--r--ash/shelf/shelf_view.cc225
-rw-r--r--ash/shelf/shelf_view.h25
-rw-r--r--ash/shelf/shelf_view_unittest.cc300
-rw-r--r--ash/shelf/shelf_widget.cc71
-rw-r--r--ash/shelf/shelf_widget.h7
-rw-r--r--ash/shelf/shelf_window_watcher.cc214
-rw-r--r--ash/shelf/shelf_window_watcher.h114
-rw-r--r--ash/shelf/shelf_window_watcher_unittest.cc175
-rw-r--r--ash/shell.cc84
-rw-r--r--ash/shell.h19
-rw-r--r--ash/shell/app_list.cc6
-rw-r--r--ash/shell/launcher_delegate_impl.cc54
-rw-r--r--ash/shell/shelf_delegate_impl.cc53
-rw-r--r--ash/shell/shelf_delegate_impl.h (renamed from ash/shell/launcher_delegate_impl.h)20
-rw-r--r--ash/shell/shell_delegate_impl.cc14
-rw-r--r--ash/shell/shell_delegate_impl.h6
-rw-r--r--ash/shell/window_type_launcher.cc3
-rw-r--r--ash/shell/window_watcher.cc14
-rw-r--r--ash/shell/window_watcher_launcher_item_delegate.cc59
-rw-r--r--ash/shell/window_watcher_launcher_item_delegate.h44
-rw-r--r--ash/shell/window_watcher_shelf_item_delegate.cc57
-rw-r--r--ash/shell/window_watcher_shelf_item_delegate.h42
-rw-r--r--ash/shell_delegate.h9
-rw-r--r--ash/system/bluetooth/tray_bluetooth.cc2
-rw-r--r--ash/system/chromeos/audio/tray_audio.cc2
-rw-r--r--ash/system/chromeos/label_tray_view.cc1
-rw-r--r--ash/system/chromeos/managed/tray_locally_managed_user.cc21
-rw-r--r--ash/system/chromeos/network/network_connect.cc2
-rw-r--r--ash/system/chromeos/network/network_state_list_detailed_view.cc3
-rw-r--r--ash/system/chromeos/network/network_state_notifier.cc4
-rw-r--r--ash/system/chromeos/network/tray_sms.cc2
-rw-r--r--ash/system/chromeos/power/tray_power.cc4
-rw-r--r--ash/system/chromeos/screen_security/screen_capture_tray_item.cc4
-rw-r--r--ash/system/chromeos/screen_security/screen_share_tray_item.cc4
-rw-r--r--ash/system/chromeos/tray_display.cc26
-rw-r--r--ash/system/date/date_view.cc1
-rw-r--r--ash/system/drive/tray_drive.cc2
-rw-r--r--ash/system/ime/tray_ime.cc6
-rw-r--r--ash/system/locale/locale_notification_controller.cc4
-rw-r--r--ash/system/session_length_limit/tray_session_length_limit.cc3
-rw-r--r--ash/system/system_notifier.cc68
-rw-r--r--ash/system/system_notifier.h40
-rw-r--r--ash/system/tray/actionable_view.cc39
-rw-r--r--ash/system/tray/actionable_view.h13
-rw-r--r--ash/system/tray/fixed_sized_scroll_view.cc5
-rw-r--r--ash/system/tray/fixed_sized_scroll_view.h1
-rw-r--r--ash/system/tray/hover_highlight_view.cc1
-rw-r--r--ash/system/tray/special_popup_row.cc1
-rw-r--r--ash/system/tray/system_tray.cc20
-rw-r--r--ash/system/tray/system_tray_bubble.cc9
-rw-r--r--ash/system/tray/system_tray_bubble.h4
-rw-r--r--ash/system/tray/system_tray_item.cc3
-rw-r--r--ash/system/tray/system_tray_item.h6
-rw-r--r--ash/system/tray/tray_background_view.cc34
-rw-r--r--ash/system/tray/tray_background_view.h7
-rw-r--r--ash/system/tray/tray_details_view.cc12
-rw-r--r--ash/system/tray/tray_details_view.h8
-rw-r--r--ash/system/tray/tray_details_view_unittest.cc149
-rw-r--r--ash/system/tray/tray_empty.cc1
-rw-r--r--ash/system/tray/tray_notification_view.cc1
-rw-r--r--ash/system/tray/tray_popup_header_button.cc12
-rw-r--r--ash/system/tray/tray_popup_header_button.h1
-rw-r--r--ash/system/tray/tray_popup_label_button.cc11
-rw-r--r--ash/system/tray/tray_popup_label_button.h3
-rw-r--r--ash/system/tray_accessibility.cc2
-rw-r--r--ash/system/user/tray_user.cc4
-rw-r--r--ash/system/web_notification/web_notification_tray.cc3
-rw-r--r--ash/system/web_notification/web_notification_tray_unittest.cc10
-rw-r--r--ash/test/ash_test_base.cc3
-rw-r--r--ash/test/ash_unittests.cc10
-rw-r--r--ash/test/launcher_item_delegate_manager_test_api.h34
-rw-r--r--ash/test/launcher_test_api.cc2
-rw-r--r--ash/test/launcher_test_api.h6
-rw-r--r--ash/test/shelf_item_delegate_manager_test_api.cc (renamed from ash/test/launcher_item_delegate_manager_test_api.cc)13
-rw-r--r--ash/test/shelf_item_delegate_manager_test_api.h33
-rw-r--r--ash/test/shelf_view_test_api.cc10
-rw-r--r--ash/test/shelf_view_test_api.h12
-rw-r--r--ash/test/shell_test_api.cc6
-rw-r--r--ash/test/shell_test_api.h6
-rw-r--r--ash/test/test_launcher_delegate.cc111
-rw-r--r--ash/test/test_launcher_item_delegate.cc53
-rw-r--r--ash/test/test_launcher_item_delegate.h44
-rw-r--r--ash/test/test_metro_viewer_process_host.cc4
-rw-r--r--ash/test/test_metro_viewer_process_host.h1
-rw-r--r--ash/test/test_session_state_delegate.cc2
-rw-r--r--ash/test/test_session_state_delegate.h2
-rw-r--r--ash/test/test_shelf_delegate.cc108
-rw-r--r--ash/test/test_shelf_delegate.h (renamed from ash/test/test_launcher_delegate.h)27
-rw-r--r--ash/test/test_shelf_item_delegate.cc52
-rw-r--r--ash/test/test_shelf_item_delegate.h42
-rw-r--r--ash/test/test_shell_delegate.cc6
-rw-r--r--ash/test/test_shell_delegate.h2
-rw-r--r--ash/touch/touch_uma.cc11
-rw-r--r--ash/wm/app_list_controller.cc4
-rw-r--r--ash/wm/caption_buttons/alternate_frame_size_button.cc211
-rw-r--r--ash/wm/caption_buttons/alternate_frame_size_button.h100
-rw-r--r--ash/wm/caption_buttons/alternate_frame_size_button_delegate.h57
-rw-r--r--ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc367
-rw-r--r--ash/wm/caption_buttons/caption_button_types.h30
-rw-r--r--ash/wm/caption_buttons/frame_caption_button.cc177
-rw-r--r--ash/wm/caption_buttons/frame_caption_button.h94
-rw-r--r--ash/wm/caption_buttons/frame_caption_button_container_view.cc137
-rw-r--r--ash/wm/caption_buttons/frame_caption_button_container_view.h32
-rw-r--r--ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc15
-rw-r--r--ash/wm/caption_buttons/frame_maximize_button.cc9
-rw-r--r--ash/wm/caption_buttons/frame_maximize_button.h6
-rw-r--r--ash/wm/caption_buttons/maximize_bubble_controller.h2
-rw-r--r--ash/wm/caption_buttons/maximize_bubble_frame_state.h20
-rw-r--r--ash/wm/custom_frame_view_ash.cc99
-rw-r--r--ash/wm/dock/docked_window_layout_manager.cc257
-rw-r--r--ash/wm/dock/docked_window_layout_manager.h17
-rw-r--r--ash/wm/dock/docked_window_layout_manager_unittest.cc16
-rw-r--r--ash/wm/dock/docked_window_resizer.cc55
-rw-r--r--ash/wm/dock/docked_window_resizer.h6
-rw-r--r--ash/wm/dock/docked_window_resizer_unittest.cc175
-rw-r--r--ash/wm/gestures/long_press_affordance_handler.cc1
-rw-r--r--ash/wm/gestures/shelf_gesture_handler.cc2
-rw-r--r--ash/wm/header_painter.cc40
-rw-r--r--ash/wm/header_painter.h32
-rw-r--r--ash/wm/header_painter_unittest.cc23
-rw-r--r--ash/wm/immersive_fullscreen_controller.cc121
-rw-r--r--ash/wm/immersive_fullscreen_controller.h11
-rw-r--r--ash/wm/immersive_fullscreen_controller_unittest.cc139
-rw-r--r--ash/wm/mru_window_tracker_unittest.cc2
-rw-r--r--ash/wm/overview/window_overview.cc1
-rw-r--r--ash/wm/overview/window_selector.cc6
-rw-r--r--ash/wm/overview/window_selector.h3
-rw-r--r--ash/wm/overview/window_selector_controller.cc12
-rw-r--r--ash/wm/overview/window_selector_unittest.cc11
-rw-r--r--ash/wm/panels/panel_frame_view.cc5
-rw-r--r--ash/wm/panels/panel_layout_manager.cc3
-rw-r--r--ash/wm/panels/panel_layout_manager_unittest.cc15
-rw-r--r--ash/wm/panels/panel_window_resizer.cc6
-rw-r--r--ash/wm/panels/panel_window_resizer_unittest.cc8
-rw-r--r--ash/wm/session_state_animator.cc17
-rw-r--r--ash/wm/session_state_animator.h11
-rw-r--r--ash/wm/solo_window_tracker.cc2
-rw-r--r--ash/wm/solo_window_tracker_unittest.cc16
-rw-r--r--ash/wm/sticky_keys.cc27
-rw-r--r--ash/wm/sticky_keys.h5
-rw-r--r--ash/wm/sticky_keys_unittest.cc114
-rw-r--r--ash/wm/system_gesture_event_filter_unittest.cc2
-rw-r--r--ash/wm/system_modal_container_layout_manager.cc1
-rw-r--r--ash/wm/window_animations.cc33
-rw-r--r--ash/wm/window_animations.h13
-rw-r--r--ash/wm/window_animations_unittest.cc106
-rw-r--r--ash/wm/window_cycle_list.cc2
-rw-r--r--ash/wm/window_positioner.cc3
-rw-r--r--ash/wm/window_state.cc111
-rw-r--r--ash/wm/window_state.h18
-rw-r--r--ash/wm/window_state_observer.h5
-rw-r--r--ash/wm/window_util.cc42
-rw-r--r--ash/wm/window_util.h19
-rw-r--r--ash/wm/workspace/colored_window_controller.cc92
-rw-r--r--ash/wm/workspace/colored_window_controller.h52
-rw-r--r--ash/wm/workspace/desktop_background_fade_controller.cc65
-rw-r--r--ash/wm/workspace/desktop_background_fade_controller.h68
-rw-r--r--ash/wm/workspace/phantom_window_controller.cc2
-rw-r--r--ash/wm/workspace/workspace_layout_manager.cc125
-rw-r--r--ash/wm/workspace/workspace_layout_manager.h22
-rw-r--r--ash/wm/workspace/workspace_layout_manager_unittest.cc71
-rw-r--r--ash/wm/workspace/workspace_window_resizer.cc12
-rw-r--r--ash/wm/workspace/workspace_window_resizer_unittest.cc18
-rw-r--r--ash/wm/workspace_controller.cc61
-rw-r--r--ash/wm/workspace_controller_unittest.cc194
218 files changed, 5518 insertions, 2659 deletions
diff --git a/ash/OWNERS b/ash/OWNERS
index a3e7809a2b..e7dd72d402 100644
--- a/ash/OWNERS
+++ b/ash/OWNERS
@@ -3,6 +3,9 @@ jamescook@chromium.org
oshima@chromium.org
sky@chromium.org
+# These are for the common case of adding or renaming a few items. If you're
+# doing structural changes, please get a review from a reviewer above.
+per-file ash.gyp=*
per-file ash_strings.grd=*
per-file ash_chromeos_strings.grdp=*
per-file ash_switches.*=*
diff --git a/ash/accelerators/accelerator_commands.cc b/ash/accelerators/accelerator_commands.cc
index 003562e2b5..b50959f498 100644
--- a/ash/accelerators/accelerator_commands.cc
+++ b/ash/accelerators/accelerator_commands.cc
@@ -9,6 +9,7 @@
#include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
+#include "content/public/browser/user_metrics.h"
namespace ash {
namespace accelerators {
@@ -25,8 +26,6 @@ bool ToggleMinimized() {
wm::WindowState* window_state = wm::GetWindowState(window);
if (!window_state->CanMinimize())
return false;
- ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
- ash::UMA_MINIMIZE_PER_KEY);
window_state->Minimize();
return true;
}
@@ -35,6 +34,8 @@ void ToggleMaximized() {
wm::WindowState* window_state = wm::GetActiveWindowState();
if (!window_state)
return;
+ content::RecordAction(
+ content::UserMetricsAction("Accel_Toggle_Maximized"));
// Get out of fullscreen when in fullscreen mode.
if (window_state->IsFullscreen())
ToggleFullscreen();
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index d9c2d7955d..0ac3f7ef4b 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -20,7 +20,6 @@
#include "ash/focus_cycler.h"
#include "ash/ime_control_delegate.h"
#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_delegate.h"
#include "ash/magnifier/magnification_controller.h"
#include "ash/magnifier/partial_magnification_controller.h"
#include "ash/media_delegate.h"
@@ -30,6 +29,7 @@
#include "ash/rotator/screen_rotation.h"
#include "ash/screenshot_delegate.h"
#include "ash/session_state_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
@@ -82,6 +82,7 @@ namespace ash {
namespace {
using internal::DisplayInfo;
+using content::UserMetricsAction;
bool DebugShortcutsEnabled() {
#if defined(NDEBUG)
@@ -92,7 +93,38 @@ bool DebugShortcutsEnabled() {
#endif
}
-void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
+bool HandleAccessibleFocusCycle(bool reverse) {
+ if (reverse) {
+ content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Previous"));
+ } else {
+ content::RecordAction(UserMetricsAction("Accel_Accessible_Focus_Next"));
+ }
+
+ if (!Shell::GetInstance()->accessibility_delegate()->
+ IsSpokenFeedbackEnabled()) {
+ return false;
+ }
+ aura::Window* active_window = ash::wm::GetActiveWindow();
+ if (!active_window)
+ return false;
+ views::Widget* widget =
+ views::Widget::GetWidgetForNativeWindow(active_window);
+ if (!widget)
+ return false;
+ views::FocusManager* focus_manager = widget->GetFocusManager();
+ if (!focus_manager)
+ return false;
+ views::View* view = focus_manager->GetFocusedView();
+ if (!view)
+ return false;
+ if (!strcmp(view->GetClassName(), views::WebView::kViewClassName))
+ return false;
+
+ focus_manager->AdvanceFocus(reverse);
+ return true;
+}
+
+bool HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance();
if (accelerator.key_code() == ui::VKEY_TAB)
@@ -101,13 +133,14 @@ void HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) {
shell->window_selector_controller()->HandleCycleWindow(
WindowSelector::BACKWARD);
- return;
+ return true;
}
shell->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::BACKWARD, accelerator.IsAltDown());
+ return true;
}
-void HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
+bool HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance();
if (accelerator.key_code() == ui::VKEY_TAB)
@@ -116,13 +149,14 @@ void HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) {
shell->window_selector_controller()->HandleCycleWindow(
WindowSelector::FORWARD);
- return;
+ return true;
}
shell->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::FORWARD, accelerator.IsAltDown());
+ return true;
}
-void HandleCycleLinear(const ui::Accelerator& accelerator) {
+bool HandleCycleLinear(const ui::Accelerator& accelerator) {
Shell* shell = Shell::GetInstance();
// TODO(jamescook): When overview becomes the default the AcceleratorAction
@@ -130,98 +164,195 @@ void HandleCycleLinear(const ui::Accelerator& accelerator) {
if (switches::UseOverviewMode()) {
content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
shell->window_selector_controller()->ToggleOverview();
- return;
+ return true;
}
if (accelerator.key_code() == ui::VKEY_MEDIA_LAUNCH_APP1)
content::RecordAction(content::UserMetricsAction("Accel_NextWindow_F5"));
shell->window_cycle_controller()->HandleLinearCycleWindow();
+ return true;
}
-bool HandleAccessibleFocusCycle(bool reverse) {
- if (!Shell::GetInstance()->accessibility_delegate()->
- IsSpokenFeedbackEnabled()) {
+bool HandleDisableCapsLock(ui::KeyboardCode key_code,
+ ui::EventType previous_event_type,
+ ui::KeyboardCode previous_key_code) {
+ Shell* shell = Shell::GetInstance();
+
+ if (previous_event_type == ui::ET_KEY_RELEASED ||
+ (previous_key_code != ui::VKEY_LSHIFT &&
+ previous_key_code != ui::VKEY_SHIFT &&
+ previous_key_code != ui::VKEY_RSHIFT)) {
+ // If something else was pressed between the Shift key being pressed
+ // and released, then ignore the release of the Shift key.
return false;
}
- aura::Window* active_window = ash::wm::GetActiveWindow();
- if (!active_window)
- return false;
- views::Widget* widget =
- views::Widget::GetWidgetForNativeWindow(active_window);
- if (!widget)
- return false;
- views::FocusManager* focus_manager = widget->GetFocusManager();
- if (!focus_manager)
- return false;
- views::View* view = focus_manager->GetFocusedView();
- if (!view)
- return false;
- if (!strcmp(view->GetClassName(), views::WebView::kViewClassName))
- return false;
+ content::RecordAction(UserMetricsAction("Accel_Disable_Caps_Lock"));
+ if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
+ shell->caps_lock_delegate()->SetCapsLockEnabled(false);
+ return true;
+ }
+ return false;
+}
- focus_manager->AdvanceFocus(reverse);
+bool HandleFocusLauncher() {
+ Shell* shell = Shell::GetInstance();
+ content::RecordAction(content::UserMetricsAction("Accel_Focus_Launcher"));
+ return shell->focus_cycler()->FocusWidget(
+ Launcher::ForPrimaryDisplay()->shelf_widget());
+}
+
+bool HandleLaunchAppN(int n) {
+ content::RecordAction(UserMetricsAction("Accel_Launch_App"));
+ Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(n);
return true;
}
-void HandleSilenceSpokenFeedback() {
- AccessibilityDelegate* delegate =
- Shell::GetInstance()->accessibility_delegate();
- if (!delegate->IsSpokenFeedbackEnabled())
- return;
- delegate->SilenceSpokenFeedback();
+bool HandleLaunchLastApp() {
+ content::RecordAction(UserMetricsAction("Accel_Launch_Last_App"));
+ Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
+ return true;
}
-#if defined(OS_CHROMEOS)
-bool HandleLock() {
- Shell::GetInstance()->session_state_delegate()->LockScreen();
+// Magnify the screen
+bool HandleMagnifyScreen(int delta_index) {
+ if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) {
+ // TODO(yoshiki): Move the following logic to MagnificationController.
+ float scale =
+ ash::Shell::GetInstance()->magnification_controller()->GetScale();
+ // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale.
+ int scale_index =
+ std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5);
+
+ int new_scale_index = std::max(0, std::min(8, scale_index + delta_index));
+
+ ash::Shell::GetInstance()->magnification_controller()->
+ SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true);
+ } else if (ash::Shell::GetInstance()->
+ partial_magnification_controller()->is_enabled()) {
+ float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1;
+ ash::Shell::GetInstance()->partial_magnification_controller()->
+ SetScale(scale);
+ }
+
return true;
}
-bool HandleFileManager() {
- Shell::GetInstance()->new_window_delegate()->OpenFileManager();
+bool HandleMediaNextTrack() {
+ Shell::GetInstance()->media_delegate()->HandleMediaNextTrack();
return true;
}
-bool HandleCrosh() {
- Shell::GetInstance()->new_window_delegate()->OpenCrosh();
+bool HandleMediaPlayPause() {
+ Shell::GetInstance()->media_delegate()->HandleMediaPlayPause();
return true;
}
-bool HandleToggleSpokenFeedback() {
- Shell::GetInstance()->accessibility_delegate()->
- ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
+bool HandleMediaPrevTrack() {
+ Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack();
return true;
}
-bool SwitchToNextUser() {
- if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled())
- return false;
- ash::SessionStateDelegate* delegate =
- ash::Shell::GetInstance()->session_state_delegate();
- if (delegate->NumberOfLoggedInUsers() <= 1)
+bool HandleNewIncognitoWindow() {
+ content::RecordAction(UserMetricsAction("Accel_New_Incognito_Window"));
+ bool incognito_allowed =
+ Shell::GetInstance()->delegate()->IsIncognitoAllowed();
+ if (incognito_allowed)
+ Shell::GetInstance()->new_window_delegate()->NewWindow(
+ true /* is_incognito */);
+ return incognito_allowed;
+}
+
+bool HandleNewTab(ui::KeyboardCode key_code) {
+ if (key_code == ui::VKEY_T)
+ content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
+ Shell::GetInstance()->new_window_delegate()->NewTab();
+ return true;
+}
+
+bool HandleNewWindow() {
+ content::RecordAction(content::UserMetricsAction("Accel_New_Window"));
+ Shell::GetInstance()->new_window_delegate()->NewWindow(
+ false /* is_incognito */);
+ return true;
+}
+
+bool HandleNextIme(ImeControlDelegate* ime_control_delegate,
+ ui::EventType previous_event_type,
+ ui::KeyboardCode previous_key_code) {
+ // This check is necessary e.g. not to process the Shift+Alt+
+ // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
+ // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
+ // is released.
+ if (previous_event_type == ui::ET_KEY_RELEASED &&
+ // Workaround for crbug.com/139556. CJK IME users tend to press
+ // Enter (or Space) and Shift+Alt almost at the same time to commit
+ // an IME string and then switch from the IME to the English layout.
+ // This workaround allows the user to trigger NEXT_IME even if the
+ // user presses Shift+Alt before releasing Enter.
+ // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
+ previous_key_code != ui::VKEY_RETURN &&
+ previous_key_code != ui::VKEY_SPACE) {
+ // We totally ignore this accelerator.
+ // TODO(mazda): Fix crbug.com/158217
return false;
- MultiProfileUMA::RecordSwitchActiveUser(
- MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
- delegate->SwitchActiveUserToNext();
+ }
+ content::RecordAction(UserMetricsAction("Accel_Next_Ime"));
+ if (ime_control_delegate)
+ return ime_control_delegate->HandleNextIme();
+ return false;
+}
+
+bool HandleOpenFeedbackPage() {
+ content::RecordAction(UserMetricsAction("Accel_Open_Feedback_Page"));
+ ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage();
return true;
}
-#endif // defined(OS_CHROMEOS)
+bool HandlePositionCenter() {
+ content::RecordAction(UserMetricsAction("Accel_Window_Position_Center"));
+ aura::Window* window = wm::GetActiveWindow();
+ // Docked windows do not support centering and ignore accelerator.
+ if (window && !wm::GetWindowState(window)->IsDocked()) {
+ wm::CenterWindow(window);
+ return true;
+ }
+ return false;
+}
+
+bool HandlePreviousIme(ImeControlDelegate* ime_control_delegate,
+ const ui::Accelerator& accelerator) {
+ content::RecordAction(UserMetricsAction("Accel_Previous_Ime"));
+ if (ime_control_delegate)
+ return ime_control_delegate->HandlePreviousIme(accelerator);
+ return false;
+}
+
+bool HandleRestoreTab() {
+ content::RecordAction(content::UserMetricsAction("Accel_Restore_Tab"));
+ Shell::GetInstance()->new_window_delegate()->RestoreTab();
+ return true;
+}
bool HandleRotatePaneFocus(Shell::Direction direction) {
Shell* shell = Shell::GetInstance();
switch (direction) {
- case Shell::FORWARD:
+ // TODO(stevet): Not sure if this is the same as IDC_FOCUS_NEXT_PANE.
+ case Shell::FORWARD: {
+ content::RecordAction(UserMetricsAction("Accel_Focus_Next_Pane"));
shell->focus_cycler()->RotateFocus(internal::FocusCycler::FORWARD);
break;
- case Shell::BACKWARD:
+ }
+ case Shell::BACKWARD: {
+ content::RecordAction(UserMetricsAction("Accel_Focus_Previous_Pane"));
shell->focus_cycler()->RotateFocus(internal::FocusCycler::BACKWARD);
break;
+ }
}
return true;
}
// Rotate the active window.
bool HandleRotateActiveWindow() {
+ content::RecordAction(UserMetricsAction("Accel_Rotate_Window"));
aura::Window* active_window = wm::GetActiveWindow();
if (active_window) {
// The rotation animation bases its target transform on the current
@@ -252,12 +383,44 @@ gfx::Display::Rotation GetNextRotation(gfx::Display::Rotation current) {
return gfx::Display::ROTATE_0;
}
+// Rotates the screen.
+bool HandleRotateScreen() {
+ content::RecordAction(UserMetricsAction("Accel_Rotate_Window"));
+ gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
+ gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
+ const DisplayInfo& display_info =
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
+ Shell::GetInstance()->display_manager()->SetDisplayRotation(
+ display.id(), GetNextRotation(display_info.rotation()));
+ return true;
+}
+
+bool HandleScaleReset() {
+ internal::DisplayManager* display_manager =
+ Shell::GetInstance()->display_manager();
+ int64 display_id = display_manager->GetDisplayIdForUIScaling();
+ if (display_id == gfx::Display::kInvalidDisplayID)
+ return false;
+
+ content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Reset"));
+
+ display_manager->SetDisplayUIScale(display_id, 1.0f);
+ return true;
+}
+
bool HandleScaleUI(bool up) {
internal::DisplayManager* display_manager =
Shell::GetInstance()->display_manager();
int64 display_id = display_manager->GetDisplayIdForUIScaling();
if (display_id == gfx::Display::kInvalidDisplayID)
return false;
+
+ if (up) {
+ content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Up"));
+ } else {
+ content::RecordAction(UserMetricsAction("Accel_Scale_Ui_Down"));
+ }
+
const DisplayInfo& display_info = display_manager->GetDisplayInfo(display_id);
float next_scale =
internal::DisplayManager::GetNextUIScale(display_info, up);
@@ -265,24 +428,145 @@ bool HandleScaleUI(bool up) {
return true;
}
-bool HandleScaleReset() {
- internal::DisplayManager* display_manager =
- Shell::GetInstance()->display_manager();
- int64 display_id = display_manager->GetDisplayIdForUIScaling();
- if (display_id == gfx::Display::kInvalidDisplayID)
+bool HandleSwapPrimaryDisplay() {
+ content::RecordAction(UserMetricsAction("Accel_Swap_Primary_Display"));
+ Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
+ return true;
+}
+
+bool HandleShowKeyboardOverlay() {
+ content::RecordAction(UserMetricsAction("Accel_Show_Keyboard_Overlay"));
+ ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay();
+
+ return true;
+}
+
+void HandleShowMessageCenterBubble() {
+ content::RecordAction(UserMetricsAction("Accel_Show_Message_Center_Bubble"));
+ internal::RootWindowController* controller =
+ internal::RootWindowController::ForTargetRootWindow();
+ internal::StatusAreaWidget* status_area_widget =
+ controller->shelf()->status_area_widget();
+ if (status_area_widget) {
+ WebNotificationTray* notification_tray =
+ status_area_widget->web_notification_tray();
+ if (notification_tray->visible())
+ notification_tray->ShowMessageCenterBubble();
+ }
+}
+
+bool HandleShowOak() {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableOak)) {
+ oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
+ return true;
+ }
+ return false;
+}
+
+bool HandleShowSystemTrayBubble() {
+ content::RecordAction(UserMetricsAction("Accel_Show_System_Tray_Bubble"));
+ internal::RootWindowController* controller =
+ internal::RootWindowController::ForTargetRootWindow();
+ if (!controller->GetSystemTray()->HasSystemBubble()) {
+ controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
+ return true;
+ }
+ return false;
+}
+
+bool HandleShowTaskManager() {
+ content::RecordAction(UserMetricsAction("Accel_Show_Task_Manager"));
+ Shell::GetInstance()->new_window_delegate()->ShowTaskManager();
+ return true;
+}
+
+void HandleSilenceSpokenFeedback() {
+ content::RecordAction(UserMetricsAction("Accel_Silence_Spoken_Feedback"));
+
+ AccessibilityDelegate* delegate =
+ Shell::GetInstance()->accessibility_delegate();
+ if (!delegate->IsSpokenFeedbackEnabled())
+ return;
+ delegate->SilenceSpokenFeedback();
+}
+
+bool HandleSwitchIme(ImeControlDelegate* ime_control_delegate,
+ const ui::Accelerator& accelerator) {
+ content::RecordAction(UserMetricsAction("Accel_Switch_Ime"));
+ if (ime_control_delegate)
+ return ime_control_delegate->HandleSwitchIme(accelerator);
+ return false;
+}
+
+bool HandleTakePartialScreenshot(ScreenshotDelegate* screenshot_delegate) {
+ content::RecordAction(UserMetricsAction("Accel_Take_Partial_Screenshot"));
+ if (screenshot_delegate) {
+ ash::PartialScreenshotView::StartPartialScreenshot(
+ screenshot_delegate);
+ }
+ // Return true to prevent propagation of the key event because
+ // this key combination is reserved for partial screenshot.
+ return true;
+}
+
+bool HandleTakeScreenshot(ScreenshotDelegate* screenshot_delegate) {
+ content::RecordAction(UserMetricsAction("Accel_Take_Screenshot"));
+ if (screenshot_delegate &&
+ screenshot_delegate->CanTakeScreenshot()) {
+ screenshot_delegate->HandleTakeScreenshotForAllRootWindows();
+ }
+ // Return true to prevent propagation of the key event.
+ return true;
+}
+
+bool HandleToggleAppList(ui::KeyboardCode key_code,
+ ui::EventType previous_event_type,
+ ui::KeyboardCode previous_key_code,
+ const ui::Accelerator& accelerator) {
+ // If something else was pressed between the Search key (LWIN)
+ // being pressed and released, then ignore the release of the
+ // Search key.
+ if (key_code == ui::VKEY_LWIN &&
+ (previous_event_type == ui::ET_KEY_RELEASED ||
+ previous_key_code != ui::VKEY_LWIN))
return false;
- display_manager->SetDisplayUIScale(display_id, 1.0f);
+ if (key_code == ui::VKEY_LWIN)
+ content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
+ // When spoken feedback is enabled, we should neither toggle the list nor
+ // consume the key since Search+Shift is one of the shortcuts the a11y
+ // feature uses. crbug.com/132296
+ DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
+ if (Shell::GetInstance()->accessibility_delegate()->
+ IsSpokenFeedbackEnabled())
+ return false;
+ ash::Shell::GetInstance()->ToggleAppList(NULL);
return true;
}
-// Rotates the screen.
-bool HandleRotateScreen() {
- gfx::Point point = Shell::GetScreen()->GetCursorScreenPoint();
- gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint(point);
- const DisplayInfo& display_info =
- Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
- Shell::GetInstance()->display_manager()->SetDisplayRotation(
- display.id(), GetNextRotation(display_info.rotation()));
+bool HandleToggleCapsLock(ui::KeyboardCode key_code,
+ ui::EventType previous_event_type,
+ ui::KeyboardCode previous_key_code) {
+ Shell* shell = Shell::GetInstance();
+ if (key_code == ui::VKEY_LWIN) {
+ // If something else was pressed between the Search key (LWIN)
+ // being pressed and released, then ignore the release of the
+ // Search key.
+ // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
+ if (previous_event_type == ui::ET_KEY_RELEASED ||
+ previous_key_code != ui::VKEY_LWIN)
+ return false;
+ }
+ content::RecordAction(UserMetricsAction("Accel_Toggle_Caps_Lock"));
+ shell->caps_lock_delegate()->ToggleCapsLock();
+ return true;
+}
+
+bool HandleToggleFullscreen(ui::KeyboardCode key_code) {
+ if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
+ content::RecordAction(UserMetricsAction("Accel_Fullscreen_F4"));
+ }
+ accelerators::ToggleFullscreen();
return true;
}
@@ -291,45 +575,127 @@ bool HandleToggleRootWindowFullScreen() {
return true;
}
-// Magnify the screen
-bool HandleMagnifyScreen(int delta_index) {
- if (ash::Shell::GetInstance()->magnification_controller()->IsEnabled()) {
- // TODO(yoshiki): Move the following logic to MagnificationController.
- float scale =
- ash::Shell::GetInstance()->magnification_controller()->GetScale();
- // Calculate rounded logarithm (base kMagnificationScaleFactor) of scale.
- int scale_index =
- std::floor(std::log(scale) / std::log(kMagnificationScaleFactor) + 0.5);
-
- int new_scale_index = std::max(0, std::min(8, scale_index + delta_index));
+bool HandleWindowSnap(int action) {
+ wm::WindowState* window_state = wm::GetActiveWindowState();
+ // Disable window snapping shortcut key for full screen window due to
+ // http://crbug.com/135487.
+ if (!window_state ||
+ window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
+ window_state->IsFullscreen()) {
+ return false;
+ }
- ash::Shell::GetInstance()->magnification_controller()->
- SetScale(std::pow(kMagnificationScaleFactor, new_scale_index), true);
- } else if (ash::Shell::GetInstance()->
- partial_magnification_controller()->is_enabled()) {
- float scale = delta_index > 0 ? kDefaultPartialMagnifiedScale : 1;
- ash::Shell::GetInstance()->partial_magnification_controller()->
- SetScale(scale);
+ if (action == WINDOW_SNAP_LEFT) {
+ content::RecordAction(UserMetricsAction("Accel_Window_Snap_Left"));
+ } else {
+ content::RecordAction(UserMetricsAction("Accel_Window_Snap_Right"));
}
+ internal::SnapSizer::SnapWindow(window_state,
+ action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
+ internal::SnapSizer::RIGHT_EDGE);
return true;
}
-bool HandleMediaNextTrack() {
- Shell::GetInstance()->media_delegate()->HandleMediaNextTrack();
+bool HandleWindowMinimize() {
+ content::RecordAction(
+ content::UserMetricsAction("Accel_Toggle_Minimized_Minus"));
+ return accelerators::ToggleMinimized();
+}
+
+#if defined(OS_CHROMEOS)
+bool HandleAddRemoveDisplay() {
+ content::RecordAction(UserMetricsAction("Accel_Add_Remove_Display"));
+ Shell::GetInstance()->display_manager()->AddRemoveDisplay();
return true;
}
-bool HandleMediaPlayPause() {
- Shell::GetInstance()->media_delegate()->HandleMediaPlayPause();
+bool HandleCrosh() {
+ content::RecordAction(UserMetricsAction("Accel_Open_Crosh"));
+
+ Shell::GetInstance()->new_window_delegate()->OpenCrosh();
return true;
}
-bool HandleMediaPrevTrack() {
- Shell::GetInstance()->media_delegate()->HandleMediaPrevTrack();
+bool HandleFileManager() {
+ content::RecordAction(UserMetricsAction("Accel_Open_File_Manager"));
+
+ Shell::GetInstance()->new_window_delegate()->OpenFileManager();
+ return true;
+}
+
+bool HandleLock(ui::KeyboardCode key_code) {
+ content::RecordAction(UserMetricsAction("Accel_LockScreen_L"));
+ Shell::GetInstance()->session_state_delegate()->LockScreen();
+ return true;
+}
+
+bool HandleCycleUser(SessionStateDelegate::CycleUser cycle_user) {
+ if (!Shell::GetInstance()->delegate()->IsMultiProfilesEnabled())
+ return false;
+ ash::SessionStateDelegate* delegate =
+ ash::Shell::GetInstance()->session_state_delegate();
+ if (delegate->NumberOfLoggedInUsers() <= 1)
+ return false;
+ MultiProfileUMA::RecordSwitchActiveUser(
+ MultiProfileUMA::SWITCH_ACTIVE_USER_BY_ACCELERATOR);
+ switch (cycle_user) {
+ case SessionStateDelegate::CYCLE_TO_NEXT_USER:
+ content::RecordAction(UserMetricsAction("Accel_Switch_To_Next_User"));
+ break;
+ case SessionStateDelegate::CYCLE_TO_PREVIOUS_USER:
+ content::RecordAction(UserMetricsAction("Accel_Switch_To_Previous_User"));
+ break;
+ }
+ delegate->CycleActiveUser(cycle_user);
+ return true;
+}
+
+bool HandleToggleMirrorMode() {
+ content::RecordAction(UserMetricsAction("Accel_Toggle_Mirror_Mode"));
+ Shell::GetInstance()->display_controller()->ToggleMirrorMode();
+ return true;
+}
+
+bool HandleToggleSpokenFeedback() {
+ content::RecordAction(UserMetricsAction("Accel_Toggle_Spoken_Feedback"));
+
+ Shell::GetInstance()->accessibility_delegate()->
+ ToggleSpokenFeedback(A11Y_NOTIFICATION_SHOW);
return true;
}
+bool HandleTouchHudClear() {
+ internal::RootWindowController* controller =
+ internal::RootWindowController::ForTargetRootWindow();
+ if (controller->touch_hud_debug()) {
+ controller->touch_hud_debug()->Clear();
+ return true;
+ }
+ return false;
+}
+
+bool HandleTouchHudModeChange() {
+ internal::RootWindowController* controller =
+ internal::RootWindowController::ForTargetRootWindow();
+ if (controller->touch_hud_debug()) {
+ controller->touch_hud_debug()->ChangeToNextMode();
+ return true;
+ }
+ return false;
+}
+
+bool HandleTouchHudProjectToggle() {
+ content::RecordAction(UserMetricsAction("Accel_Touch_Hud_Clear"));
+ bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
+ Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
+ return true;
+}
+
+#endif // defined(OS_CHROMEOS)
+
+// Debug print methods.
+
bool HandlePrintLayerHierarchy() {
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (size_t i = 0; i < root_windows.size(); ++i) {
@@ -565,25 +931,18 @@ bool AcceleratorController::PerformAction(int action,
case ACCESSIBLE_FOCUS_PREVIOUS:
return HandleAccessibleFocusCycle(true);
case CYCLE_BACKWARD_MRU:
- HandleCycleBackwardMRU(accelerator);
- return true;
+ return HandleCycleBackwardMRU(accelerator);
case CYCLE_FORWARD_MRU:
- HandleCycleForwardMRU(accelerator);
- return true;
+ return HandleCycleForwardMRU(accelerator);
case CYCLE_LINEAR:
- HandleCycleLinear(accelerator);
- return true;
+ return HandleCycleLinear(accelerator);
#if defined(OS_CHROMEOS)
case ADD_REMOVE_DISPLAY:
- Shell::GetInstance()->display_manager()->AddRemoveDisplay();
- return true;
+ return HandleAddRemoveDisplay();
case TOGGLE_MIRROR_MODE:
- Shell::GetInstance()->display_controller()->ToggleMirrorMode();
- return true;
+ return HandleToggleMirrorMode();
case LOCK_SCREEN:
- if (key_code == ui::VKEY_L)
- content::RecordAction(content::UserMetricsAction("Accel_LockScreen_L"));
- return HandleLock();
+ return HandleLock(key_code);
case OPEN_FILE_MANAGER:
return HandleFileManager();
case OPEN_CROSH:
@@ -592,129 +951,53 @@ bool AcceleratorController::PerformAction(int action,
HandleSilenceSpokenFeedback();
break;
case SWAP_PRIMARY_DISPLAY:
- Shell::GetInstance()->display_controller()->SwapPrimaryDisplay();
- return true;
+ return HandleSwapPrimaryDisplay();
case SWITCH_TO_NEXT_USER:
- return SwitchToNextUser();
+ return HandleCycleUser(SessionStateDelegate::CYCLE_TO_NEXT_USER);
+ case SWITCH_TO_PREVIOUS_USER:
+ return HandleCycleUser(SessionStateDelegate::CYCLE_TO_PREVIOUS_USER);
case TOGGLE_SPOKEN_FEEDBACK:
return HandleToggleSpokenFeedback();
case TOGGLE_WIFI:
Shell::GetInstance()->system_tray_notifier()->NotifyRequestToggleWifi();
return true;
- case TOUCH_HUD_CLEAR: {
- internal::RootWindowController* controller =
- internal::RootWindowController::ForTargetRootWindow();
- if (controller->touch_hud_debug()) {
- controller->touch_hud_debug()->Clear();
- return true;
- }
- return false;
- }
- case TOUCH_HUD_MODE_CHANGE: {
- internal::RootWindowController* controller =
- internal::RootWindowController::ForTargetRootWindow();
- if (controller->touch_hud_debug()) {
- controller->touch_hud_debug()->ChangeToNextMode();
- return true;
- }
- return false;
- }
- case TOUCH_HUD_PROJECTION_TOGGLE: {
- bool enabled = Shell::GetInstance()->is_touch_hud_projection_enabled();
- Shell::GetInstance()->SetTouchHudProjectionEnabled(!enabled);
- return true;
- }
+ case TOUCH_HUD_CLEAR:
+ return HandleTouchHudClear();
+ case TOUCH_HUD_MODE_CHANGE:
+ return HandleTouchHudModeChange();
+ case TOUCH_HUD_PROJECTION_TOGGLE:
+ return HandleTouchHudProjectToggle();
case DISABLE_GPU_WATCHDOG:
content::GpuDataManager::GetInstance()->DisableGpuWatchdog();
return true;
-#endif
+#endif // OS_CHROMEOS
case OPEN_FEEDBACK_PAGE:
- ash::Shell::GetInstance()->new_window_delegate()->OpenFeedbackPage();
- return true;
+ return HandleOpenFeedbackPage();
case EXIT:
// UMA metrics are recorded in the handler.
exit_warning_handler_.HandleAccelerator();
return true;
- case NEW_INCOGNITO_WINDOW: {
- bool incognito_allowed =
- Shell::GetInstance()->delegate()->IsIncognitoAllowed();
- if (incognito_allowed)
- Shell::GetInstance()->new_window_delegate()->NewWindow(
- true /* is_incognito */);
- return incognito_allowed;
- }
+ case NEW_INCOGNITO_WINDOW:
+ return HandleNewIncognitoWindow();
case NEW_TAB:
- if (key_code == ui::VKEY_T)
- content::RecordAction(content::UserMetricsAction("Accel_NewTab_T"));
- Shell::GetInstance()->new_window_delegate()->NewTab();
- return true;
+ return HandleNewTab(key_code);
case NEW_WINDOW:
- Shell::GetInstance()->new_window_delegate()->NewWindow(
- false /* is_incognito */);
- return true;
+ return HandleNewWindow();
case RESTORE_TAB:
- Shell::GetInstance()->new_window_delegate()->RestoreTab();
- return true;
+ return HandleRestoreTab();
case TAKE_SCREENSHOT:
- if (screenshot_delegate_.get() &&
- screenshot_delegate_->CanTakeScreenshot()) {
- screenshot_delegate_->HandleTakeScreenshotForAllRootWindows();
- }
- // Return true to prevent propagation of the key event.
- return true;
+ return HandleTakeScreenshot(screenshot_delegate_.get());
case TAKE_PARTIAL_SCREENSHOT:
- if (screenshot_delegate_) {
- ash::PartialScreenshotView::StartPartialScreenshot(
- screenshot_delegate_.get());
- }
- // Return true to prevent propagation of the key event because
- // this key combination is reserved for partial screenshot.
- return true;
+ return HandleTakePartialScreenshot(screenshot_delegate_.get());
case TOGGLE_APP_LIST:
- // If something else was pressed between the Search key (LWIN)
- // being pressed and released, then ignore the release of the
- // Search key.
- if (key_code == ui::VKEY_LWIN &&
- (previous_event_type == ui::ET_KEY_RELEASED ||
- previous_key_code != ui::VKEY_LWIN))
- return false;
- if (key_code == ui::VKEY_LWIN)
- content::RecordAction(content::UserMetricsAction("Accel_Search_LWin"));
- // When spoken feedback is enabled, we should neither toggle the list nor
- // consume the key since Search+Shift is one of the shortcuts the a11y
- // feature uses. crbug.com/132296
- DCHECK_EQ(ui::VKEY_LWIN, accelerator.key_code());
- if (Shell::GetInstance()->accessibility_delegate()->
- IsSpokenFeedbackEnabled())
- return false;
- ash::Shell::GetInstance()->ToggleAppList(NULL);
- return true;
+ return HandleToggleAppList(
+ key_code, previous_event_type, previous_key_code, accelerator);
case DISABLE_CAPS_LOCK:
- if (previous_event_type == ui::ET_KEY_RELEASED ||
- (previous_key_code != ui::VKEY_LSHIFT &&
- previous_key_code != ui::VKEY_SHIFT &&
- previous_key_code != ui::VKEY_RSHIFT)) {
- // If something else was pressed between the Shift key being pressed
- // and released, then ignore the release of the Shift key.
- return false;
- }
- if (shell->caps_lock_delegate()->IsCapsLockEnabled()) {
- shell->caps_lock_delegate()->SetCapsLockEnabled(false);
- return true;
- }
- return false;
+ return HandleDisableCapsLock(
+ key_code, previous_event_type, previous_key_code);
case TOGGLE_CAPS_LOCK:
- if (key_code == ui::VKEY_LWIN) {
- // If something else was pressed between the Search key (LWIN)
- // being pressed and released, then ignore the release of the
- // Search key.
- // TODO(danakj): Releasing Alt first breaks this: crbug.com/166495
- if (previous_event_type == ui::ET_KEY_RELEASED ||
- previous_key_code != ui::VKEY_LWIN)
- return false;
- }
- shell->caps_lock_delegate()->ToggleCapsLock();
- return true;
+ return HandleToggleCapsLock(
+ key_code, previous_event_type, previous_key_code);
case BRIGHTNESS_DOWN:
if (brightness_control_delegate_)
return brightness_control_delegate_->HandleBrightnessDown(accelerator);
@@ -749,145 +1032,61 @@ bool AcceleratorController::PerformAction(int action,
return volume_delegate && volume_delegate->HandleVolumeUp(accelerator);
}
case FOCUS_LAUNCHER:
- return shell->focus_cycler()->FocusWidget(
- Launcher::ForPrimaryDisplay()->shelf_widget());
+ return HandleFocusLauncher();
case FOCUS_NEXT_PANE:
return HandleRotatePaneFocus(Shell::FORWARD);
case FOCUS_PREVIOUS_PANE:
return HandleRotatePaneFocus(Shell::BACKWARD);
case SHOW_KEYBOARD_OVERLAY:
- ash::Shell::GetInstance()->new_window_delegate()->ShowKeyboardOverlay();
- return true;
+ return HandleShowKeyboardOverlay();
case SHOW_OAK:
- if (CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kAshEnableOak)) {
- oak::ShowOakWindowWithContext(Shell::GetPrimaryRootWindow());
- return true;
- }
+ return HandleShowOak();
+ case SHOW_SYSTEM_TRAY_BUBBLE:
+ return HandleShowSystemTrayBubble();
+ case SHOW_MESSAGE_CENTER_BUBBLE:
+ HandleShowMessageCenterBubble();
break;
- case SHOW_SYSTEM_TRAY_BUBBLE: {
- internal::RootWindowController* controller =
- internal::RootWindowController::ForTargetRootWindow();
- if (!controller->GetSystemTray()->HasSystemBubble()) {
- controller->GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
- return true;
- }
- break;
- }
- case SHOW_MESSAGE_CENTER_BUBBLE: {
- internal::RootWindowController* controller =
- internal::RootWindowController::ForTargetRootWindow();
- internal::StatusAreaWidget* status_area_widget =
- controller->shelf()->status_area_widget();
- if (status_area_widget) {
- WebNotificationTray* notification_tray =
- status_area_widget->web_notification_tray();
- if (notification_tray->visible())
- notification_tray->ShowMessageCenterBubble();
- }
- break;
- }
case SHOW_TASK_MANAGER:
- Shell::GetInstance()->new_window_delegate()->ShowTaskManager();
- return true;
+ return HandleShowTaskManager();
case NEXT_IME:
- // This check is necessary e.g. not to process the Shift+Alt+
- // ET_KEY_RELEASED accelerator for Chrome OS (see ash/accelerators/
- // accelerator_controller.cc) when Shift+Alt+Tab is pressed and then Tab
- // is released.
- if (previous_event_type == ui::ET_KEY_RELEASED &&
- // Workaround for crbug.com/139556. CJK IME users tend to press
- // Enter (or Space) and Shift+Alt almost at the same time to commit
- // an IME string and then switch from the IME to the English layout.
- // This workaround allows the user to trigger NEXT_IME even if the
- // user presses Shift+Alt before releasing Enter.
- // TODO(nona|mazda): Fix crbug.com/139556 in a cleaner way.
- previous_key_code != ui::VKEY_RETURN &&
- previous_key_code != ui::VKEY_SPACE) {
- // We totally ignore this accelerator.
- // TODO(mazda): Fix crbug.com/158217
- return false;
- }
- if (ime_control_delegate_)
- return ime_control_delegate_->HandleNextIme();
- break;
+ return HandleNextIme(
+ ime_control_delegate_.get(), previous_event_type, previous_key_code);
case PREVIOUS_IME:
- if (ime_control_delegate_)
- return ime_control_delegate_->HandlePreviousIme(accelerator);
- break;
+ return HandlePreviousIme(ime_control_delegate_.get(), accelerator);
case PRINT_UI_HIERARCHIES:
return HandlePrintUIHierarchies();
case SWITCH_IME:
- if (ime_control_delegate_)
- return ime_control_delegate_->HandleSwitchIme(accelerator);
- break;
+ return HandleSwitchIme(ime_control_delegate_.get(), accelerator);
case LAUNCH_APP_0:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(0);
- return true;
+ return HandleLaunchAppN(0);
case LAUNCH_APP_1:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(1);
- return true;
+ return HandleLaunchAppN(1);
case LAUNCH_APP_2:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(2);
- return true;
+ return HandleLaunchAppN(2);
case LAUNCH_APP_3:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(3);
- return true;
+ return HandleLaunchAppN(3);
case LAUNCH_APP_4:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(4);
- return true;
+ return HandleLaunchAppN(4);
case LAUNCH_APP_5:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(5);
- return true;
+ return HandleLaunchAppN(5);
case LAUNCH_APP_6:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(6);
- return true;
+ return HandleLaunchAppN(6);
case LAUNCH_APP_7:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(7);
- return true;
+ return HandleLaunchAppN(7);
case LAUNCH_LAST_APP:
- Launcher::ForPrimaryDisplay()->LaunchAppIndexAt(-1);
- return true;
+ return HandleLaunchLastApp();
case WINDOW_SNAP_LEFT:
- case WINDOW_SNAP_RIGHT: {
- wm::WindowState* window_state = wm::GetActiveWindowState();
- // Disable window snapping shortcut key for full screen window due to
- // http://crbug.com/135487.
- if (!window_state ||
- window_state->window()->type() != aura::client::WINDOW_TYPE_NORMAL ||
- window_state->IsFullscreen()) {
- break;
- }
-
- internal::SnapSizer::SnapWindow(window_state,
- action == WINDOW_SNAP_LEFT ? internal::SnapSizer::LEFT_EDGE :
- internal::SnapSizer::RIGHT_EDGE);
- return true;
- }
+ case WINDOW_SNAP_RIGHT:
+ return HandleWindowSnap(action);
case WINDOW_MINIMIZE:
- return accelerators::ToggleMinimized();
- case TOGGLE_FULLSCREEN: {
- if (key_code == ui::VKEY_MEDIA_LAUNCH_APP2) {
- content::RecordAction(
- content::UserMetricsAction("Accel_Fullscreen_F4"));
- }
- accelerators::ToggleFullscreen();
- return true;
- }
- case TOGGLE_MAXIMIZED: {
+ return HandleWindowMinimize();
+ case TOGGLE_FULLSCREEN:
+ return HandleToggleFullscreen(key_code);
+ case TOGGLE_MAXIMIZED:
accelerators::ToggleMaximized();
return true;
- }
- case WINDOW_POSITION_CENTER: {
- content::RecordAction(content::UserMetricsAction("Accel_Center"));
- aura::Window* window = wm::GetActiveWindow();
- // Docked windows do not support centering and ignore accelerator.
- if (window && !wm::GetWindowState(window)->IsDocked()) {
- wm::CenterWindow(window);
- return true;
- }
- break;
- }
+ case WINDOW_POSITION_CENTER:
+ return HandlePositionCenter();
case SCALE_UI_UP:
return HandleScaleUI(true /* up */);
case SCALE_UI_DOWN:
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index ce3476b888..8569171ca4 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -505,7 +505,7 @@ TEST_F(AcceleratorControllerTest, WindowSnap) {
EXPECT_EQ(window->bounds().ToString(), snap_right.ToString());
}
{
- gfx::Rect normal_bounds = window->bounds();
+ gfx::Rect normal_bounds = window_state->GetRestoreBoundsInParent();
GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
EXPECT_TRUE(window_state->IsMaximized());
@@ -513,6 +513,8 @@ TEST_F(AcceleratorControllerTest, WindowSnap) {
GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
EXPECT_FALSE(window_state->IsMaximized());
+ // Window gets restored to its restore bounds since side-maximized state
+ // is treated as a "maximized" state.
EXPECT_EQ(normal_bounds.ToString(), window->bounds().ToString());
GetController()->PerformAction(TOGGLE_MAXIMIZED, dummy);
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index 4ffc1ab54a..cd3a580473 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -81,7 +81,9 @@ const AcceleratorData kAcceleratorData[] = {
{ true, ui::VKEY_Z, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
TOGGLE_SPOKEN_FEEDBACK },
{ true, ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN, SILENCE_SPOKEN_FEEDBACK},
- { true, ui::VKEY_MEDIA_LAUNCH_APP1, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
+ { true, ui::VKEY_OEM_COMMA, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
+ SWITCH_TO_PREVIOUS_USER },
+ { true, ui::VKEY_OEM_PERIOD, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN,
SWITCH_TO_NEXT_USER },
#endif // defined(OS_CHROMEOS)
{ true, ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, OPEN_FEEDBACK_PAGE },
diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h
index 21bbf6ae6b..d12096037b 100644
--- a/ash/accelerators/accelerator_table.h
+++ b/ash/accelerators/accelerator_table.h
@@ -135,6 +135,7 @@ enum AcceleratorAction {
OPEN_CROSH,
OPEN_FILE_MANAGER,
SWITCH_TO_NEXT_USER,
+ SWITCH_TO_PREVIOUS_USER,
#endif
};
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 091465ec35..089ea15d9a 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -156,10 +156,6 @@
'keyboard_uma_event_filter.h',
'launcher/launcher.cc',
'launcher/launcher.h',
- 'launcher/launcher_delegate.h',
- 'launcher/launcher_item_delegate_manager.cc',
- 'launcher/launcher_item_delegate_manager.h',
- 'launcher/launcher_item_delegate.h',
'launcher/launcher_types.cc',
'launcher/launcher_types.h',
'magnifier/magnification_controller.cc',
@@ -210,10 +206,15 @@
'shelf/shelf_button.cc',
'shelf/shelf_button.h',
'shelf/shelf_button_host.h',
+ 'shelf/shelf_delegate.h',
'shelf/shelf_icon_observer.h',
+ 'shelf/shelf_item_delegate.h',
+ 'shelf/shelf_item_delegate_manager.cc',
+ 'shelf/shelf_item_delegate_manager.h',
'shelf/shelf_layout_manager.cc',
'shelf/shelf_layout_manager.h',
'shelf/shelf_layout_manager_observer.h',
+ 'shelf/shelf_menu_model.h',
'shelf/shelf_model.cc',
'shelf/shelf_model.h',
'shelf/shelf_model_observer.h',
@@ -228,6 +229,8 @@
'shelf/shelf_view.h',
'shelf/shelf_widget.cc',
'shelf/shelf_widget.h',
+ 'shelf/shelf_window_watcher.cc',
+ 'shelf/shelf_window_watcher.h',
'shell.cc',
'shell.h',
'shell_delegate.h',
@@ -422,8 +425,14 @@
'wm/base_layout_manager.h',
'wm/boot_splash_screen_chromeos.cc',
'wm/boot_splash_screen_chromeos.h',
+ 'wm/caption_buttons/alternate_frame_size_button.cc',
+ 'wm/caption_buttons/alternate_frame_size_button.h',
+ 'wm/caption_buttons/alternate_frame_size_button_delegate.h',
'wm/caption_buttons/bubble_contents_button_row.cc',
'wm/caption_buttons/bubble_contents_button_row.h',
+ 'wm/caption_buttons/caption_button_types.h',
+ 'wm/caption_buttons/frame_caption_button.cc',
+ 'wm/caption_buttons/frame_caption_button.h',
'wm/caption_buttons/frame_caption_button_container_view.cc',
'wm/caption_buttons/frame_caption_button_container_view.h',
'wm/caption_buttons/frame_maximize_button.cc',
@@ -433,7 +442,6 @@
'wm/caption_buttons/maximize_bubble_controller.h',
'wm/caption_buttons/maximize_bubble_controller_bubble.cc',
'wm/caption_buttons/maximize_bubble_controller_bubble.h',
- 'wm/caption_buttons/maximize_bubble_frame_state.h',
'wm/coordinate_conversion.cc',
'wm/coordinate_conversion.h',
'wm/custom_frame_view_ash.cc',
@@ -568,10 +576,6 @@
'wm/wm_types.h',
'wm/workspace_controller.cc',
'wm/workspace_controller.h',
- 'wm/workspace/colored_window_controller.cc',
- 'wm/workspace/colored_window_controller.h',
- 'wm/workspace/desktop_background_fade_controller.cc',
- 'wm/workspace/desktop_background_fade_controller.h',
'wm/workspace/magnetism_matcher.cc',
'wm/workspace/magnetism_matcher.h',
'wm/workspace/multi_window_resize_controller.cc',
@@ -651,32 +655,30 @@
'test/ash_test_helper.h',
'test/cursor_manager_test_api.cc',
'test/cursor_manager_test_api.h',
- 'test/launcher_test_api.cc',
- 'test/launcher_test_api.h',
- 'test/shelf_view_test_api.cc',
- 'test/shelf_view_test_api.h',
'test/display_manager_test_api.cc',
'test/display_manager_test_api.h',
- 'test/launcher_item_delegate_manager_test_api.cc',
- 'test/launcher_item_delegate_manager_test_api.h',
+ 'test/launcher_test_api.cc',
+ 'test/launcher_test_api.h',
'test/mirror_window_test_api.cc',
'test/mirror_window_test_api.h',
'test/overflow_bubble_view_test_api.cc',
'test/overflow_bubble_view_test_api.h',
+ 'test/shelf_item_delegate_manager_test_api.cc',
+ 'test/shelf_item_delegate_manager_test_api.h',
+ 'test/shelf_view_test_api.cc',
+ 'test/shelf_view_test_api.h',
'test/shell_test_api.cc',
'test/shell_test_api.h',
'test/test_activation_delegate.cc',
'test/test_activation_delegate.h',
- 'test/test_user_wallpaper_delegate.cc',
- 'test/test_user_wallpaper_delegate.h',
- 'test/test_launcher_delegate.cc',
- 'test/test_launcher_delegate.h',
- 'test/test_launcher_item_delegate.cc',
- 'test/test_launcher_item_delegate.h',
'test/test_screenshot_delegate.cc',
'test/test_screenshot_delegate.cc',
'test/test_session_state_delegate.cc',
'test/test_session_state_delegate.h',
+ 'test/test_shelf_delegate.cc',
+ 'test/test_shelf_delegate.h',
+ 'test/test_shelf_item_delegate.cc',
+ 'test/test_shelf_item_delegate.h',
'test/test_shell_delegate.cc',
'test/test_shell_delegate.h',
'test/test_suite.cc',
@@ -685,6 +687,8 @@
'test/test_suite_init.mm',
'test/test_system_tray_delegate.cc',
'test/test_system_tray_delegate.h',
+ 'test/test_user_wallpaper_delegate.cc',
+ 'test/test_user_wallpaper_delegate.h',
'test/ui_controls_factory_ash.cc',
'test/ui_controls_factory_ash.h',
],
@@ -786,15 +790,16 @@
'shelf/shelf_tooltip_manager_unittest.cc',
'shelf/shelf_view_unittest.cc',
'shelf/shelf_widget_unittest.cc',
+ 'shelf/shelf_window_watcher_unittest.cc',
'shell/app_list.cc',
'shell/bubble.cc',
'shell/context_menu.cc',
'shell/context_menu.h',
- 'shell/launcher_delegate_impl.cc',
- 'shell/launcher_delegate_impl.h',
'shell/lock_view.cc',
'shell/panel_window.cc',
'shell/panel_window.h',
+ 'shell/shelf_delegate_impl.cc',
+ 'shell/shelf_delegate_impl.h',
'shell/shell_delegate_impl.cc',
'shell/shell_delegate_impl.h',
'shell/widgets.cc',
@@ -802,8 +807,8 @@
'shell/window_type_launcher.h',
'shell/window_watcher.cc',
'shell/window_watcher.h',
- 'shell/window_watcher_launcher_item_delegate.cc',
- 'shell/window_watcher_launcher_item_delegate.h',
+ 'shell/window_watcher_shelf_item_delegate.cc',
+ 'shell/window_watcher_shelf_item_delegate.h',
'shell/window_watcher_unittest.cc',
'shell_unittest.cc',
'system/chromeos/managed/tray_locally_managed_user_unittest.cc',
@@ -815,6 +820,7 @@
'system/chromeos/tray_display_unittest.cc',
'system/date/date_view_unittest.cc',
'system/tray/system_tray_unittest.cc',
+ 'system/tray/tray_details_view_unittest.cc',
'system/user/tray_user_unittest.cc',
'system/web_notification/web_notification_tray_unittest.cc',
'test/ash_test_helper_unittest.cc',
@@ -824,6 +830,7 @@
'wm/app_list_controller_unittest.cc',
'wm/ash_native_cursor_manager_unittest.cc',
'wm/base_layout_manager_unittest.cc',
+ 'wm/caption_buttons/alternate_frame_size_button_unittest.cc',
'wm/caption_buttons/frame_caption_button_container_view_unittest.cc',
'wm/caption_buttons/frame_maximize_button_unittest.cc',
'wm/dock/docked_window_layout_manager_unittest.cc',
@@ -958,11 +965,11 @@
'shell/example_factory.h',
'shell/keyboard_controller_proxy_stub.cc',
'shell/keyboard_controller_proxy_stub.h',
- 'shell/launcher_delegate_impl.cc',
- 'shell/launcher_delegate_impl.h',
'shell/lock_view.cc',
'shell/panel_window.cc',
'shell/panel_window.h',
+ 'shell/shelf_delegate_impl.cc',
+ 'shell/shelf_delegate_impl.h',
'shell/shell_delegate_impl.cc',
'shell/shell_delegate_impl.h',
'shell/shell_main.cc',
@@ -975,8 +982,8 @@
'shell/window_type_launcher.h',
'shell/window_watcher.cc',
'shell/window_watcher.h',
- 'shell/window_watcher_launcher_item_delegate.cc',
- 'shell/window_watcher_launcher_item_delegate.h',
+ 'shell/window_watcher_shelf_item_delegate.cc',
+ 'shell/window_watcher_shelf_item_delegate.h',
'../content/app/startup_helper_win.cc',
'../ui/views/test/test_views_delegate.cc',
],
diff --git a/ash/ash_constants.cc b/ash/ash_constants.cc
index a1404d0608..82501f2085 100644
--- a/ash/ash_constants.cc
+++ b/ash/ash_constants.cc
@@ -9,8 +9,6 @@
namespace ash {
-DEFINE_WINDOW_PROPERTY_KEY(bool, kConstrainedWindowKey, false);
-
const int kResizeAreaCornerSize = 16;
const int kResizeOutsideBoundsSize = 6;
const int kResizeOutsideBoundsScaleForTouch = 5;
diff --git a/ash/ash_constants.h b/ash/ash_constants.h
index 8e889851ea..cad4354bcb 100644
--- a/ash/ash_constants.h
+++ b/ash/ash_constants.h
@@ -13,11 +13,6 @@ typedef unsigned int SkColor;
namespace ash {
-// The window is a constrained window and lives therefore entirely within
-// another aura window.
-ASH_EXPORT extern const aura::WindowProperty<bool>* const
- kConstrainedWindowKey;
-
// In the window corners, the resize areas don't actually expand bigger, but the
// 16 px at the end of each edge triggers diagonal resizing.
ASH_EXPORT extern const int kResizeAreaCornerSize;
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 88f569d3b3..29463d0889 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -83,9 +83,7 @@ const char kAshEnableAdvancedGestures[] = "ash-enable-advanced-gestures";
// Use alternate visual style for the caption buttons (minimize, maximize,
// restore, close). The alternate style:
// - Adds a dedicated button for minimize.
-// - Increases the height of the maximized header.
// - Removes the maximize button's help bubble.
-// - Switches snapping a window left/right to be always 50%.
const char kAshEnableAlternateFrameCaptionButtonStyle[] =
"ash-enable-alternate-caption-button";
@@ -206,8 +204,11 @@ const char kAshDisableDragAndDropAppListToLauncher[] =
"ash-disable-drag-and-drop-applist-to-launcher";
bool UseAlternateFrameCaptionButtonStyle() {
- return CommandLine::ForCurrentProcess()->
- HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle);
+ // For the sake of simplicity, the alternate caption button style is only
+ // used if snapped windows are always 50% of the screen's width.
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ return command_line->HasSwitch(kAshEnableAlternateFrameCaptionButtonStyle) &&
+ !command_line->HasSwitch(kAshMultipleSnapWindowWidths);
}
bool UseAlternateShelfLayout() {
diff --git a/ash/display/display_error_observer_chromeos.cc b/ash/display/display_error_observer_chromeos.cc
index 3b619ef63f..34e1bc2e5d 100644
--- a/ash/display/display_error_observer_chromeos.cc
+++ b/ash/display/display_error_observer_chromeos.cc
@@ -49,7 +49,9 @@ void DisplayErrorObserver::OnDisplayModeChangeFailed(
base::string16(), // message
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
base::string16(), // display_source
- message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY_ERROR),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierDisplayError),
message_center::RichNotificationData(),
NULL));
message_center::MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc
index 3069c83e52..0914772ac0 100644
--- a/ash/display/resolution_notification_controller.cc
+++ b/ash/display/resolution_notification_controller.cc
@@ -229,7 +229,8 @@ void ResolutionNotificationController::CreateOrUpdateNotification(
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
base::string16() /* display_source */,
message_center::NotifierId(
- system_notifier::NOTIFIER_DISPLAY_RESOLUTION_CHANGE),
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierDisplayResolutionChange),
data,
new ResolutionChangeNotificationDelegate(
this, change_info_->timeout_count > 0)));
diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc
index 07ab4f3933..1d5d636347 100644
--- a/ash/drag_drop/drag_drop_controller.cc
+++ b/ash/drag_drop/drag_drop_controller.cc
@@ -461,14 +461,10 @@ void DragDropController::OnGestureEvent(ui::GestureEvent* event) {
}
void DragDropController::OnWindowDestroyed(aura::Window* window) {
- if (drag_window_ == window) {
- drag_window_->RemoveObserver(this);
+ if (drag_window_ == window)
drag_window_ = NULL;
- }
- if (drag_source_window_ == window) {
- drag_source_window_->RemoveObserver(this);
+ if (drag_source_window_ == window)
drag_source_window_ = NULL;
- }
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/ash/launcher/launcher.cc b/ash/launcher/launcher.cc
index 0adf72ac01..58926e8c3b 100644
--- a/ash/launcher/launcher.cc
+++ b/ash/launcher/launcher.cc
@@ -8,11 +8,11 @@
#include <cmath>
#include "ash/focus_cycler.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
#include "ash/root_window_controller.h"
#include "ash/screen_ash.h"
+#include "ash/shelf/shelf_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_navigator.h"
@@ -43,11 +43,11 @@ namespace ash {
const char Launcher::kNativeViewName[] = "ShelfView";
Launcher::Launcher(ShelfModel* shelf_model,
- LauncherDelegate* launcher_delegate,
+ ShelfDelegate* shelf_delegate,
ShelfWidget* shelf_widget)
: shelf_view_(NULL),
alignment_(shelf_widget->GetAlignment()),
- delegate_(launcher_delegate),
+ delegate_(shelf_delegate),
shelf_widget_(shelf_widget) {
shelf_view_ = new internal::ShelfView(
shelf_model, delegate_, shelf_widget_->shelf_layout_manager());
@@ -108,10 +108,10 @@ void Launcher::ActivateLauncherItem(int index) {
ui::EF_NONE,
false);
- const ash::LauncherItem& item = shelf_view_->model()->items()[index];
- ash::LauncherItemDelegate* item_delegate =
- Shell::GetInstance()->launcher_item_delegate_manager()->
- GetLauncherItemDelegate(item.id);
+ const LauncherItem& item = shelf_view_->model()->items()[index];
+ ShelfItemDelegate* item_delegate =
+ Shell::GetInstance()->shelf_item_delegate_manager()->GetShelfItemDelegate(
+ item.id);
item_delegate->ItemSelected(event);
}
diff --git a/ash/launcher/launcher.h b/ash/launcher/launcher.h
index 5f1da0b111..5c50b8ce7d 100644
--- a/ash/launcher/launcher.h
+++ b/ash/launcher/launcher.h
@@ -41,7 +41,7 @@ namespace test {
class LauncherTestAPI;
}
-class LauncherDelegate;
+class ShelfDelegate;
class ShelfIconObserver;
class ShelfModel;
class ShelfWidget;
@@ -50,9 +50,7 @@ class ASH_EXPORT Launcher {
public:
static const char kNativeViewName[];
- Launcher(ShelfModel* shelf_model,
- LauncherDelegate* launcher_delegate,
- ShelfWidget* shelf_widget);
+ Launcher(ShelfModel* model, ShelfDelegate* delegate, ShelfWidget* widget);
virtual ~Launcher();
// Return the launcher for the primary display. NULL if no user is
@@ -122,7 +120,7 @@ class ASH_EXPORT Launcher {
ShelfAlignment alignment_;
- LauncherDelegate* delegate_;
+ ShelfDelegate* delegate_;
ShelfWidget* shelf_widget_;
diff --git a/ash/launcher/launcher_item_delegate_manager.cc b/ash/launcher/launcher_item_delegate_manager.cc
deleted file mode 100644
index efd885717f..0000000000
--- a/ash/launcher/launcher_item_delegate_manager.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2013 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/launcher/launcher_item_delegate_manager.h"
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shell.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-
-namespace ash {
-
-LauncherItemDelegateManager::LauncherItemDelegateManager(ShelfModel* model)
- : model_(model) {
- DCHECK(model_);
- model_->AddObserver(this);
-}
-
-LauncherItemDelegateManager::~LauncherItemDelegateManager() {
- model_->RemoveObserver(this);
- STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(),
- id_to_item_delegate_map_.end());
-}
-
-void LauncherItemDelegateManager::SetLauncherItemDelegate(
- ash::LauncherID id,
- scoped_ptr<LauncherItemDelegate> item_delegate) {
- // If another LauncherItemDelegate is already registered for |id|, we assume
- // that this request is replacing LauncherItemDelegate for |id| with
- // |item_delegate|.
- RemoveLauncherItemDelegate(id);
- id_to_item_delegate_map_[id] = item_delegate.release();
-}
-
-LauncherItemDelegate* LauncherItemDelegateManager::GetLauncherItemDelegate(
- ash::LauncherID id) {
- if (model_->ItemIndexByID(id) != -1) {
- // Each LauncherItem has to have a LauncherItemDelegate.
- DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end());
- return id_to_item_delegate_map_[id];
- }
- return NULL;
-}
-
-void LauncherItemDelegateManager::ShelfItemAdded(int index) {
-}
-
-void LauncherItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) {
- RemoveLauncherItemDelegate(id);
-}
-
-void LauncherItemDelegateManager::ShelfItemMoved(int start_index,
- int target_index) {
-}
-
-void LauncherItemDelegateManager::ShelfItemChanged(
- int index,
- const LauncherItem& old_item) {
-}
-
-void LauncherItemDelegateManager::ShelfStatusChanged() {
-}
-
-void LauncherItemDelegateManager::RemoveLauncherItemDelegate(
- ash::LauncherID id) {
- if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) {
- delete id_to_item_delegate_map_[id];
- id_to_item_delegate_map_.erase(id);
- }
-}
-
-} // namespace ash
diff --git a/ash/launcher/launcher_item_delegate_manager.h b/ash/launcher/launcher_item_delegate_manager.h
deleted file mode 100644
index 1768cd964e..0000000000
--- a/ash/launcher/launcher_item_delegate_manager.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
-#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
-
-#include <map>
-
-#include "ash/ash_export.h"
-#include "ash/launcher/launcher_types.h"
-#include "ash/shelf/shelf_model_observer.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_ptr.h"
-
-namespace ash {
-class LauncherItemDelegate;
-class ShelfModel;
-
-namespace test {
-class LauncherItemDelegateManagerTestAPI;
-}
-
-// LauncherItemDelegateManager manages the set of LauncherItemDelegates for the
-// launcher. LauncherItemDelegateManager does not create LauncherItemDelegates,
-// rather it is expected that someone else invokes SetLauncherItemDelegate
-// appropriately. On the other hand, LauncherItemDelegateManager destroys
-// LauncherItemDelegates when the corresponding item from the model is removed.
-class ASH_EXPORT LauncherItemDelegateManager : public ShelfModelObserver {
- public:
- explicit LauncherItemDelegateManager(ShelfModel* model);
- virtual ~LauncherItemDelegateManager();
-
- // Set |item_delegate| for |id| and take an ownership.
- void SetLauncherItemDelegate(
- ash::LauncherID id,
- scoped_ptr<ash::LauncherItemDelegate> item_delegate);
-
- // Returns LauncherItemDelegate for |item_type|. Always returns non-NULL.
- LauncherItemDelegate* GetLauncherItemDelegate(ash::LauncherID id);
-
- // ShelfModelObserver overrides:
- virtual void ShelfItemAdded(int model_index) OVERRIDE;
- virtual void ShelfItemRemoved(int index, ash::LauncherID id) OVERRIDE;
- virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE;
- virtual void ShelfItemChanged(int index,
- const LauncherItem& old_item) OVERRIDE;
- virtual void ShelfStatusChanged() OVERRIDE;
-
- private:
- friend class ash::test::LauncherItemDelegateManagerTestAPI;
-
- typedef std::map<ash::LauncherID, LauncherItemDelegate*>
- LauncherIDToItemDelegateMap;
-
- // Remove and destroy LauncherItemDelegate for |id|.
- void RemoveLauncherItemDelegate(ash::LauncherID id);
-
- // Owned by Shell.
- ShelfModel* model_;
-
- LauncherIDToItemDelegateMap id_to_item_delegate_map_;
-
- DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManager);
-};
-
-} // namespace ash
-
-#endif // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_MANAGER_H_
diff --git a/ash/launcher/launcher_types.cc b/ash/launcher/launcher_types.cc
index 46fc827ade..67dc5729c9 100644
--- a/ash/launcher/launcher_types.cc
+++ b/ash/launcher/launcher_types.cc
@@ -8,14 +8,25 @@ namespace ash {
const int kLauncherPreferredSize = 48;
const int kLauncherBackgroundAlpha = 204;
+const int kInvalidImageResourceID = -1;
+const int kInvalidLauncherID = 0;
+const int kTimeToSwitchBackgroundMs = 1000;
LauncherItem::LauncherItem()
: type(TYPE_UNDEFINED),
- id(0),
+ id(kInvalidLauncherID),
status(STATUS_CLOSED) {
}
LauncherItem::~LauncherItem() {
}
+LauncherItemDetails::LauncherItemDetails()
+ : type(TYPE_UNDEFINED),
+ image_resource_id(kInvalidImageResourceID) {
+}
+
+LauncherItemDetails::~LauncherItemDetails() {
+}
+
} // namespace ash
diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h
index c60062902a..bbb1bbc41d 100644
--- a/ash/launcher/launcher_types.h
+++ b/ash/launcher/launcher_types.h
@@ -8,6 +8,7 @@
#include <vector>
#include "ash/ash_export.h"
+#include "base/strings/string16.h"
#include "ui/gfx/image/image_skia.h"
namespace ash {
@@ -21,6 +22,14 @@ ASH_EXPORT extern const int kLauncherPreferredSize;
// Max alpha of the launcher background.
ASH_EXPORT extern const int kLauncherBackgroundAlpha;
+// Invalid image resource id used for LauncherItemDetails.
+extern const int kInvalidImageResourceID;
+
+extern const int kInvalidLauncherID;
+
+// Animation duration for switching black shelf and dock background on and off.
+ASH_EXPORT extern const int kTimeToSwitchBackgroundMs;
+
// Type the LauncherItem represents.
enum LauncherItemType {
// Represents a running app panel.
@@ -81,6 +90,22 @@ enum CycleDirection {
CYCLE_BACKWARD
};
+// LauncherItemDetails may be set on Window (by way of
+// SetLauncherItemDetailsForWindow) to make the window appear in the shelf. See
+// ShelfWindowWatcher for details.
+struct ASH_EXPORT LauncherItemDetails {
+ LauncherItemDetails();
+ ~LauncherItemDetails();
+
+ LauncherItemType type;
+
+ // Resource id of the image to display on the shelf.
+ int image_resource_id;
+
+ // Title of the item.
+ base::string16 title;
+};
+
} // namespace ash
#endif // ASH_LAUNCHER_LAUNCHER_TYPES_H_
diff --git a/ash/launcher/launcher_unittest.cc b/ash/launcher/launcher_unittest.cc
index 6359508ade..24eca33499 100644
--- a/ash/launcher/launcher_unittest.cc
+++ b/ash/launcher/launcher_unittest.cc
@@ -3,8 +3,8 @@
// found in the LICENSE file.
#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
#include "ash/shelf/shelf_button.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_widget.h"
@@ -12,7 +12,7 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/launcher_test_api.h"
#include "ash/test/shelf_view_test_api.h"
-#include "ash/test/test_launcher_item_delegate.h"
+#include "ash/test/test_shelf_item_delegate.h"
#include "ash/wm/window_util.h"
#include "ui/aura/root_window.h"
#include "ui/gfx/display.h"
@@ -51,7 +51,7 @@ class LauncherTest : public ash::test::AshTestBase {
shelf_view_ = test.shelf_view();
shelf_model_ = shelf_view_->model();
item_delegate_manager_ =
- Shell::GetInstance()->launcher_item_delegate_manager();
+ Shell::GetInstance()->shelf_item_delegate_manager();
test_.reset(new ash::test::ShelfViewTestAPI(shelf_view_));
}
@@ -72,7 +72,7 @@ class LauncherTest : public ash::test::AshTestBase {
return shelf_model_;
}
- LauncherItemDelegateManager* item_manager() {
+ ShelfItemDelegateManager* item_manager() {
return item_delegate_manager_;
}
@@ -84,8 +84,8 @@ class LauncherTest : public ash::test::AshTestBase {
Launcher* launcher_;
ShelfView* shelf_view_;
ShelfModel* shelf_model_;
- LauncherItemDelegateManager* item_delegate_manager_;
- scoped_ptr<ash::test::ShelfViewTestAPI> test_;
+ ShelfItemDelegateManager* item_delegate_manager_;
+ scoped_ptr<test::ShelfViewTestAPI> test_;
DISALLOW_COPY_AND_ASSIGN(LauncherTest);
};
@@ -121,10 +121,10 @@ TEST_F(LauncherTest, checkHoverAfterMenu) {
item.status = STATUS_RUNNING;
int index = shelf_model()->Add(item);
- scoped_ptr<LauncherItemDelegate> delegate(
- new ash::test::TestLauncherItemDelegate(NULL));
- item_manager()->SetLauncherItemDelegate(shelf_model()->items()[index].id,
- delegate.Pass());
+ scoped_ptr<ShelfItemDelegate> delegate(
+ new test::TestShelfItemDelegate(NULL));
+ item_manager()->SetShelfItemDelegate(shelf_model()->items()[index].id,
+ delegate.Pass());
ASSERT_EQ(++button_count, test_api()->GetButtonCount());
ShelfButton* button = test_api()->GetButton(index);
diff --git a/ash/multi_profile_uma.cc b/ash/multi_profile_uma.cc
index f6d9840f5a..cbf3b059a3 100644
--- a/ash/multi_profile_uma.cc
+++ b/ash/multi_profile_uma.cc
@@ -30,10 +30,28 @@ void MultiProfileUMA::RecordSwitchActiveUser(SwitchActiveUserAction action) {
}
// static
+void MultiProfileUMA::RecordTeleportWindowType(TeleportWindowType window_type) {
+ UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindowType",
+ window_type,
+ NUM_TELEPORT_WINDOW_TYPES);
+}
+
+// static
void MultiProfileUMA::RecordTeleportAction(TeleportWindowAction action) {
UMA_HISTOGRAM_ENUMERATION("MultiProfile.TeleportWindow",
action,
NUM_TELEPORT_WINDOW_ACTIONS);
}
+// static
+void MultiProfileUMA::RecordUserCount(int number_of_users) {
+ UMA_HISTOGRAM_COUNTS_100("MultiProfile.UsersPerSession", number_of_users);
+}
+
+// static
+void MultiProfileUMA::RecordDiscardedTab(int number_of_users) {
+ UMA_HISTOGRAM_COUNTS_100("MultiProfile.DiscardedTabsPerUser",
+ number_of_users);
+}
+
} // namespace ash
diff --git a/ash/multi_profile_uma.h b/ash/multi_profile_uma.h
index 1c70482b67..937b4d818e 100644
--- a/ash/multi_profile_uma.h
+++ b/ash/multi_profile_uma.h
@@ -35,6 +35,17 @@ class ASH_EXPORT MultiProfileUMA {
NUM_SWITCH_ACTIVE_USER_ACTIONS
};
+ enum TeleportWindowType {
+ TELEPORT_WINDOW_BROWSER = 0,
+ TELEPORT_WINDOW_INCOGNITO_BROWSER,
+ TELEPORT_WINDOW_V1_APP,
+ TELEPORT_WINDOW_V2_APP,
+ TELEPORT_WINDOW_PANEL,
+ TELEPORT_WINDOW_POPUP,
+ TELEPORT_WINDOW_UNKNOWN,
+ NUM_TELEPORT_WINDOW_TYPES
+ };
+
enum TeleportWindowAction {
TELEPORT_WINDOW_DRAG_AND_DROP = 0,
TELEPORT_WINDOW_CAPTION_MENU,
@@ -52,9 +63,18 @@ class ASH_EXPORT MultiProfileUMA {
// Record switching the active user and what UI path was taken.
static void RecordSwitchActiveUser(SwitchActiveUserAction action);
+ // Record the type of window which got teleported to another desk.
+ static void RecordTeleportWindowType(TeleportWindowType window_type);
+
// Record the way and how many times a window got teleported to another desk.
static void RecordTeleportAction(TeleportWindowAction action);
+ // Record number of users joined into a session. Called upon logout.
+ static void RecordUserCount(int number_of_users);
+
+ // Record a discarded tab in the number of running users bucket.
+ static void RecordDiscardedTab(int number_of_users);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MultiProfileUMA);
};
diff --git a/ash/popup_message.h b/ash/popup_message.h
index 040b9fe7e1..72a0cf5453 100644
--- a/ash/popup_message.h
+++ b/ash/popup_message.h
@@ -13,6 +13,7 @@
namespace views {
class BubbleDelegateView;
+class Widget;
}
namespace ash {
diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd
index 5447785b74..6fec31bb14 100644
--- a/ash/resources/ash_resources.grd
+++ b/ash/resources/ash_resources.grd
@@ -20,6 +20,7 @@
BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
SAME CONDITIONALS. -->
<structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_BACKGROUND" file="common/launcher/launcher_background.png" />
+ <structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_CORNER" file="common/launcher/launcher_corner.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_DIMMING" file="common/launcher/launcher_dimming.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST" file="common/launcher/launcher_appmenu.png" />
<structure type="chrome_scaled_image" name="IDR_AURA_LAUNCHER_ICON_APPLIST_ALTERNATE" file="common/alt_launcher/status_app_menu_icon.png" />
diff --git a/ash/resources/default_100_percent/common/launcher/launcher_corner.png b/ash/resources/default_100_percent/common/launcher/launcher_corner.png
new file mode 100644
index 0000000000..d730065dd1
--- /dev/null
+++ b/ash/resources/default_100_percent/common/launcher/launcher_corner.png
Binary files differ
diff --git a/ash/resources/default_200_percent/common/launcher/launcher_corner.png b/ash/resources/default_200_percent/common/launcher/launcher_corner.png
new file mode 100644
index 0000000000..e5f8c9633a
--- /dev/null
+++ b/ash/resources/default_200_percent/common/launcher/launcher_corner.png
Binary files differ
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 45c0f7a637..4775accf97 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -518,23 +518,27 @@ void RootWindowController::UpdateShelfVisibility() {
shelf_->shelf_layout_manager()->UpdateVisibilityState();
}
-const aura::Window* RootWindowController::GetTopmostFullscreenWindow() const {
+const aura::Window* RootWindowController::GetWindowForFullscreenMode() const {
const aura::Window::Windows& windows =
GetContainer(kShellWindowId_DefaultContainer)->children();
+ const aura::Window* topmost_window = NULL;
for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
iter != windows.rend(); ++iter) {
- if (wm::GetWindowState(*iter)->IsFullscreen())
- return *iter;
+ if (((*iter)->type() == aura::client::WINDOW_TYPE_NORMAL ||
+ (*iter)->type() == aura::client::WINDOW_TYPE_PANEL) &&
+ (*iter)->layer()->GetTargetVisibility()) {
+ topmost_window = *iter;
+ break;
+ }
+ }
+ while (topmost_window) {
+ if (wm::GetWindowState(topmost_window)->IsFullscreen())
+ return topmost_window;
+ topmost_window = topmost_window->transient_parent();
}
return NULL;
}
-aura::Window* RootWindowController::GetTopmostFullscreenWindow() {
- return const_cast<aura::Window*>(
- const_cast<const RootWindowController*>(this)->
- GetTopmostFullscreenWindow());
-}
-
void RootWindowController::ActivateKeyboard(
keyboard::KeyboardController* keyboard_controller) {
if (!keyboard::IsKeyboardEnabled() ||
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 076bfe6a4f..6e2d545442 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -221,10 +221,9 @@ class ASH_EXPORT RootWindowController : public ShellObserver {
// Initialize touch HUDs if necessary.
void InitTouchHuds();
- // Returns the window, if any, which is in fullscreen mode. If multiple
- // windows are in fullscreen state, the topmost one is preferred.
- const aura::Window* GetTopmostFullscreenWindow() const;
- aura::Window* GetTopmostFullscreenWindow();
+ // Returns the topmost window or one of its transient parents, if any of them
+ // are in fullscreen mode.
+ const aura::Window* GetWindowForFullscreenMode() const;
// Activate virtual keyboard on current root window controller.
void ActivateKeyboard(keyboard::KeyboardController* keyboard_controller);
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index d547a8a335..2ff7362629 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -454,7 +454,7 @@ TEST_F(RootWindowControllerTest, ModalContainerBlockedSession) {
}
}
-TEST_F(RootWindowControllerTest, GetTopmostFullscreenWindow) {
+TEST_F(RootWindowControllerTest, GetWindowForFullscreenMode) {
UpdateDisplay("600x600");
internal::RootWindowController* controller =
Shell::GetInstance()->GetPrimaryRootWindowController();
@@ -467,39 +467,23 @@ TEST_F(RootWindowControllerTest, GetTopmostFullscreenWindow) {
Widget* w3 = Widget::CreateWindowWithParentAndBounds(NULL,
w2->GetNativeWindow(), gfx::Rect(0, 0, 100, 100));
- // Test that GetTopmostFullscreenWindow() finds the fullscreen window when one
+ // Test that GetWindowForFullscreenMode() finds the fullscreen window when one
// of its transient children is active.
w3->Activate();
- EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
+ EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
- // Since there's only one desktop workspace, it always returns the same
- // fullscreen window.
+ // If the topmost window is not fullscreen, it returns NULL.
w1->Activate();
- EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
-}
+ EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
+ w1->Close();
+ w3->Close();
-TEST_F(RootWindowControllerTest, MultipleFullscreenWindows) {
- UpdateDisplay("600x600");
- internal::RootWindowController* controller =
- Shell::GetInstance()->GetPrimaryRootWindowController();
-
- Widget* w1 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
- w1->Maximize();
- Widget* w2 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
- w2->SetFullscreen(true);
- Widget* w3 = CreateTestWidget(gfx::Rect(0, 0, 100, 100));
- w3->SetFullscreen(true);
-
- // Test that GetTopmostFullscreenWindow() finds the active fullscreen window.
+ // Only w2 remains, if minimized GetWindowForFullscreenMode should return
+ // NULL.
w2->Activate();
- EXPECT_EQ(w2->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
- w3->Activate();
- EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
-
- // If the active window is not fullscreen, it still returns the topmost
- // fullscreen window, which is the last active one.
- w1->Activate();
- EXPECT_EQ(w3->GetNativeWindow(), controller->GetTopmostFullscreenWindow());
+ EXPECT_EQ(w2->GetNativeWindow(), controller->GetWindowForFullscreenMode());
+ w2->Minimize();
+ EXPECT_EQ(NULL, controller->GetWindowForFullscreenMode());
}
// Test that user session window can't be focused if user session blocked by
diff --git a/ash/scoped_target_root_window.h b/ash/scoped_target_root_window.h
index 0e7a8b18d7..c0ab7ef755 100644
--- a/ash/scoped_target_root_window.h
+++ b/ash/scoped_target_root_window.h
@@ -4,6 +4,7 @@
#ifndef ASH_SCOPED_TARGET_ROOT_WINDOW_H_
#define ASH_SCOPED_TARGET_ROOT_WINDOW_H_
+#include "ash/ash_export.h"
#include "base/basictypes.h"
namespace aura {
@@ -18,7 +19,7 @@ namespace internal {
// in the same window where a user interaction happened.
// An example usage is to specify the target root window when creating
// a new window using launcher's icon.
-class ScopedTargetRootWindow {
+class ASH_EXPORT ScopedTargetRootWindow {
public:
explicit ScopedTargetRootWindow(aura::Window* root_window);
~ScopedTargetRootWindow();
diff --git a/ash/session_state_delegate.h b/ash/session_state_delegate.h
index 47570ff911..91c6229133 100644
--- a/ash/session_state_delegate.h
+++ b/ash/session_state_delegate.h
@@ -33,6 +33,12 @@ typedef std::vector<std::string> UserIdList;
// Delegate for checking and modifying the session state.
class ASH_EXPORT SessionStateDelegate {
public:
+ // Defines the cycle direction for |CycleActiveUser|.
+ enum CycleUser {
+ CYCLE_TO_NEXT_USER = 0, // Cycle to the next user.
+ CYCLE_TO_PREVIOUS_USER, // Cycle to the previous user.
+ };
+
virtual ~SessionStateDelegate() {};
// Returns the maximum possible number of logged in users.
@@ -96,9 +102,9 @@ class ASH_EXPORT SessionStateDelegate {
// (if that user has already signed in).
virtual void SwitchActiveUser(const std::string& user_id) = 0;
- // Switches the active user to the next user, with the same ordering as
- // GetLoggedInUsers.
- virtual void SwitchActiveUserToNext() = 0;
+ // Switches the active user to the next or previous user, with the same
+ // ordering as GetLoggedInUsers.
+ virtual void CycleActiveUser(CycleUser cycle_user) = 0;
// Adds or removes sessions state observer.
virtual void AddSessionStateObserver(SessionStateObserver* observer) = 0;
diff --git a/ash/session_state_delegate_stub.cc b/ash/session_state_delegate_stub.cc
index 6daa5930d5..cea4132523 100644
--- a/ash/session_state_delegate_stub.cc
+++ b/ash/session_state_delegate_stub.cc
@@ -82,7 +82,7 @@ void SessionStateDelegateStub::GetLoggedInUsers(UserIdList* users) {
void SessionStateDelegateStub::SwitchActiveUser(const std::string& user_id) {
}
-void SessionStateDelegateStub::SwitchActiveUserToNext() {
+void SessionStateDelegateStub::CycleActiveUser(CycleUser cycle_user) {
}
void SessionStateDelegateStub::AddSessionStateObserver(
diff --git a/ash/session_state_delegate_stub.h b/ash/session_state_delegate_stub.h
index 6fe1eb9aea..f496e6bbc8 100644
--- a/ash/session_state_delegate_stub.h
+++ b/ash/session_state_delegate_stub.h
@@ -38,7 +38,7 @@ class SessionStateDelegateStub : public SessionStateDelegate {
ash::MultiProfileIndex index) const OVERRIDE;
virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE;
virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE;
- virtual void SwitchActiveUserToNext() OVERRIDE;
+ virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE;
virtual void AddSessionStateObserver(
ash::SessionStateObserver* observer) OVERRIDE;
virtual void RemoveSessionStateObserver(
diff --git a/ash/shelf/alternate_app_list_button.cc b/ash/shelf/alternate_app_list_button.cc
index 4ce283418b..bf442d1abc 100644
--- a/ash/shelf/alternate_app_list_button.cc
+++ b/ash/shelf/alternate_app_list_button.cc
@@ -4,8 +4,10 @@
#include "ash/shelf/alternate_app_list_button.h"
+#include "ash/ash_constants.h"
#include "ash/ash_switches.h"
#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_button.h"
#include "ash/shelf/shelf_button_host.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_widget.h"
@@ -22,6 +24,7 @@
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/controls/button/image_button.h"
+#include "ui/views/painter.h"
namespace ash {
namespace internal {
@@ -39,6 +42,8 @@ AlternateAppListButton::AlternateAppListButton(views::ButtonListener* listener,
SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
SetSize(gfx::Size(ShelfLayoutManager::kShelfSize,
ShelfLayoutManager::kShelfSize));
+ SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+ kFocusBorderColor, gfx::Insets(1, 1, 1, 1)));
}
AlternateAppListButton::~AlternateAppListButton() {
@@ -156,7 +161,7 @@ void AlternateAppListButton::OnPaint(gfx::Canvas* canvas) {
forground_bounds.x(),
forground_bounds.y());
- OnPaintFocusBorder(canvas);
+ views::Painter::PaintFocusPainter(this, canvas, focus_painter());
}
void AlternateAppListButton::GetAccessibleState(
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index a9612bfe5f..697ed772b4 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -6,6 +6,7 @@
#include <vector>
+#include "ash/ash_constants.h"
#include "ash/launcher/launcher_types.h"
#include "ash/shelf/shelf_button_host.h"
#include "grit/ash_resources.h"
@@ -17,6 +18,7 @@
#include "ui/compositor/layer_animation_element.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/views/painter.h"
namespace ash {
namespace internal {
@@ -43,6 +45,8 @@ AppListButton::AppListButton(views::ButtonListener* listener,
SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE));
SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize));
SetImageAlignment(ImageButton::ALIGN_CENTER, ImageButton::ALIGN_TOP);
+ SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+ kFocusBorderColor, gfx::Insets(1, 1, 1, 1)));
}
AppListButton::~AppListButton() {
diff --git a/ash/shelf/app_list_shelf_item_delegate.cc b/ash/shelf/app_list_shelf_item_delegate.cc
index b9fd49dae2..7013d2be1f 100644
--- a/ash/shelf/app_list_shelf_item_delegate.cc
+++ b/ash/shelf/app_list_shelf_item_delegate.cc
@@ -19,7 +19,7 @@ AppListShelfItemDelegate::AppListShelfItemDelegate() {
}
AppListShelfItemDelegate::~AppListShelfItemDelegate() {
- // LauncherItemDelegateManager owns and destroys this class.
+ // ShelfItemDelegateManager owns and destroys this class.
}
bool AppListShelfItemDelegate::ItemSelected(const ui::Event& event) {
@@ -41,7 +41,7 @@ ui::MenuModel* AppListShelfItemDelegate::CreateContextMenu(
return NULL;
}
-LauncherMenuModel* AppListShelfItemDelegate::CreateApplicationMenu(
+ShelfMenuModel* AppListShelfItemDelegate::CreateApplicationMenu(
int event_flags) {
// AppList does not show an application menu.
return NULL;
diff --git a/ash/shelf/app_list_shelf_item_delegate.h b/ash/shelf/app_list_shelf_item_delegate.h
index 750a84bbb8..0396821b65 100644
--- a/ash/shelf/app_list_shelf_item_delegate.h
+++ b/ash/shelf/app_list_shelf_item_delegate.h
@@ -5,26 +5,25 @@
#ifndef ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_
#define ASH_SHELF_APP_LIST_SHELF_ITEM_DELEGATE_H_
-#include "ash/launcher/launcher_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
namespace ash {
namespace internal {
-// LauncherItemDelegate for TYPE_APP_LIST.
-class AppListShelfItemDelegate : public LauncherItemDelegate {
+// ShelfItemDelegate for TYPE_APP_LIST.
+class AppListShelfItemDelegate : public ShelfItemDelegate {
public:
AppListShelfItemDelegate();
virtual ~AppListShelfItemDelegate();
- // ash::LauncherItemDelegate overrides:
+ // ShelfItemDelegate:
virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
virtual base::string16 GetTitle() OVERRIDE;
- virtual ui::MenuModel* CreateContextMenu(
- aura::Window* root_window) OVERRIDE;
- virtual LauncherMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+ virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+ virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
virtual bool IsDraggable() OVERRIDE;
virtual bool ShouldShowTooltip() OVERRIDE;
diff --git a/ash/shelf/background_animator.cc b/ash/shelf/background_animator.cc
index 35e5fba3a7..8137d60289 100644
--- a/ash/shelf/background_animator.cc
+++ b/ash/shelf/background_animator.cc
@@ -34,11 +34,12 @@ void BackgroundAnimator::SetDuration(int time_in_ms) {
animation_.SetSlideDuration(time_in_ms);
}
-void BackgroundAnimator::SetPaintsBackground(bool value, ChangeType type) {
+void BackgroundAnimator::SetPaintsBackground(
+ bool value, BackgroundAnimatorChangeType type) {
if (paints_background_ == value)
return;
paints_background_ = value;
- if (type == CHANGE_IMMEDIATE && !animation_.is_animating()) {
+ if (type == BACKGROUND_CHANGE_IMMEDIATE && !animation_.is_animating()) {
animation_.Reset(value ? 1.0f : 0.0f);
AnimationProgressed(&animation_);
return;
diff --git a/ash/shelf/background_animator.h b/ash/shelf/background_animator.h
index 7436b30f1f..805f7d5e5d 100644
--- a/ash/shelf/background_animator.h
+++ b/ash/shelf/background_animator.h
@@ -11,6 +11,13 @@
#include "ui/gfx/animation/slide_animation.h"
namespace ash {
+
+// How the background can be changed.
+enum BackgroundAnimatorChangeType {
+ BACKGROUND_CHANGE_ANIMATE,
+ BACKGROUND_CHANGE_IMMEDIATE
+};
+
namespace internal {
// Delegate is notified any time the background changes.
@@ -25,12 +32,6 @@ class ASH_EXPORT BackgroundAnimatorDelegate {
// BackgroundAnimator is used by the shelf to animate the background (alpha).
class ASH_EXPORT BackgroundAnimator : public gfx::AnimationDelegate {
public:
- // How the background can be changed.
- enum ChangeType {
- CHANGE_ANIMATE,
- CHANGE_IMMEDIATE
- };
-
BackgroundAnimator(BackgroundAnimatorDelegate* delegate,
int min_alpha,
int max_alpha);
@@ -42,7 +43,7 @@ class ASH_EXPORT BackgroundAnimator : public gfx::AnimationDelegate {
// Sets whether a background is rendered. Initial value is false. If |type|
// is |CHANGE_IMMEDIATE| and an animation is not in progress this notifies
// the delegate immediately (synchronously from this method).
- void SetPaintsBackground(bool value, ChangeType type);
+ void SetPaintsBackground(bool value, BackgroundAnimatorChangeType type);
bool paints_background() const { return paints_background_; }
// Current alpha.
diff --git a/ash/shelf/shelf_button.cc b/ash/shelf/shelf_button.cc
index 4b10b8faf2..93a44e1a1a 100644
--- a/ash/shelf/shelf_button.cc
+++ b/ash/shelf/shelf_button.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "ash/ash_constants.h"
#include "ash/ash_switches.h"
#include "ash/shelf/shelf_button_host.h"
#include "ash/shelf/shelf_layout_manager.h"
@@ -470,6 +471,15 @@ void ShelfButton::OnBlur() {
CustomButton::OnBlur();
}
+void ShelfButton::OnPaint(gfx::Canvas* canvas) {
+ CustomButton::OnPaint(canvas);
+ if (HasFocus()) {
+ gfx::Rect paint_bounds(GetLocalBounds());
+ paint_bounds.Inset(1, 1, 1, 1);
+ canvas->DrawSolidFocusRect(paint_bounds, kFocusBorderColor);
+ }
+}
+
void ShelfButton::OnGestureEvent(ui::GestureEvent* event) {
switch (event->type()) {
case ui::ET_GESTURE_TAP_DOWN:
diff --git a/ash/shelf/shelf_button.h b/ash/shelf/shelf_button.h
index 1bfc510d90..60158f2ea9 100644
--- a/ash/shelf/shelf_button.h
+++ b/ash/shelf/shelf_button.h
@@ -107,6 +107,7 @@ class ASH_EXPORT ShelfButton : public views::CustomButton {
virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
virtual void OnFocus() OVERRIDE;
virtual void OnBlur() OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
// ui::EventHandler overrides:
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
diff --git a/ash/launcher/launcher_delegate.h b/ash/shelf/shelf_delegate.h
index 3868becd8f..e168c47027 100644
--- a/ash/launcher/launcher_delegate.h
+++ b/ash/shelf/shelf_delegate.h
@@ -1,25 +1,21 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
-#ifndef ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
-#define ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
+#ifndef ASH_SHELF_SHELF_DELEGATE_H_
+#define ASH_SHELF_SHELF_DELEGATE_H_
#include "ash/ash_export.h"
#include "ash/launcher/launcher_types.h"
-namespace aura {
-class Window;
-}
-
namespace ash {
class Launcher;
// Delegate for the Launcher.
-class ASH_EXPORT LauncherDelegate {
+class ASH_EXPORT ShelfDelegate {
public:
// Launcher owns the delegate.
- virtual ~LauncherDelegate() {}
+ virtual ~ShelfDelegate() {}
// Callback used to allow delegate to perform initialization actions that
// depend on the Launcher being in a known state.
@@ -53,4 +49,4 @@ class ASH_EXPORT LauncherDelegate {
} // namespace ash
-#endif // ASH_LAUNCHER_LAUNCHER_DELEGATE_H_
+#endif // ASH_SHELF_SHELF_DELEGATE_H_
diff --git a/ash/launcher/launcher_item_delegate.h b/ash/shelf/shelf_item_delegate.h
index c83de47b81..72815cf80f 100644
--- a/ash/launcher/launcher_item_delegate.h
+++ b/ash/shelf/shelf_item_delegate.h
@@ -2,42 +2,29 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
-#define ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+#ifndef ASH_SHELF_SHELF_ITEM_DELEGATE_H_
+#define ASH_SHELF_SHELF_ITEM_DELEGATE_H_
#include "ash/ash_export.h"
-#include "ash/launcher/launcher_types.h"
#include "base/strings/string16.h"
-#include "ui/base/models/simple_menu_model.h"
namespace aura {
-class RootWindow;
+class Window;
}
namespace ui {
class Event;
+class MenuModel;
}
namespace ash {
-// A special menu model which keeps track of an "active" menu item.
-class ASH_EXPORT LauncherMenuModel : public ui::SimpleMenuModel {
- public:
- explicit LauncherMenuModel(ui::SimpleMenuModel::Delegate* delegate)
- : ui::SimpleMenuModel(delegate) {}
-
- // Returns |true| when the given |command_id| is active and needs to be drawn
- // in a special state.
- virtual bool IsCommandActive(int command_id) const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(LauncherMenuModel);
-};
+class ShelfMenuModel;
// Delegate for the LauncherItem.
-class ASH_EXPORT LauncherItemDelegate {
+class ASH_EXPORT ShelfItemDelegate {
public:
- virtual ~LauncherItemDelegate() {}
+ virtual ~ShelfItemDelegate() {}
// Invoked when the user clicks on a window entry in the launcher.
// |event| is the click event. The |event| is dispatched by a view
@@ -65,7 +52,7 @@ class ASH_EXPORT LauncherItemDelegate {
// - A list containing the title and the active list of items.
// The caller takes ownership of the returned model.
// |event_flags| specifies the flags of the event which triggered this menu.
- virtual ash::LauncherMenuModel* CreateApplicationMenu(int event_flags) = 0;
+ virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) = 0;
// Whether the launcher item is draggable.
virtual bool IsDraggable() = 0;
@@ -76,4 +63,4 @@ class ASH_EXPORT LauncherItemDelegate {
} // namespace ash
-#endif // ASH_LAUNCHER_LAUNCHER_ITEM_DELEGATE_H_
+#endif // ASH_SHELF_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/shelf/shelf_item_delegate_manager.cc b/ash/shelf/shelf_item_delegate_manager.cc
new file mode 100644
index 0000000000..79124caac1
--- /dev/null
+++ b/ash/shelf/shelf_item_delegate_manager.cc
@@ -0,0 +1,72 @@
+// Copyright 2013 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/shelf/shelf_item_delegate_manager.h"
+
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shell.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+
+namespace ash {
+
+ShelfItemDelegateManager::ShelfItemDelegateManager(ShelfModel* model)
+ : model_(model) {
+ DCHECK(model_);
+ model_->AddObserver(this);
+}
+
+ShelfItemDelegateManager::~ShelfItemDelegateManager() {
+ model_->RemoveObserver(this);
+ STLDeleteContainerPairSecondPointers(id_to_item_delegate_map_.begin(),
+ id_to_item_delegate_map_.end());
+}
+
+void ShelfItemDelegateManager::SetShelfItemDelegate(
+ LauncherID id,
+ scoped_ptr<ShelfItemDelegate> item_delegate) {
+ // If another ShelfItemDelegate is already registered for |id|, we assume
+ // that this request is replacing ShelfItemDelegate for |id| with
+ // |item_delegate|.
+ RemoveShelfItemDelegate(id);
+ id_to_item_delegate_map_[id] = item_delegate.release();
+}
+
+ShelfItemDelegate* ShelfItemDelegateManager::GetShelfItemDelegate(
+ LauncherID id) {
+ if (model_->ItemIndexByID(id) != -1) {
+ // Each LauncherItem has to have a ShelfItemDelegate.
+ DCHECK(id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end());
+ return id_to_item_delegate_map_[id];
+ }
+ return NULL;
+}
+
+void ShelfItemDelegateManager::ShelfItemAdded(int index) {
+}
+
+void ShelfItemDelegateManager::ShelfItemRemoved(int index, LauncherID id) {
+ RemoveShelfItemDelegate(id);
+}
+
+void ShelfItemDelegateManager::ShelfItemMoved(int start_index,
+ int target_index) {
+}
+
+void ShelfItemDelegateManager::ShelfItemChanged(int index,
+ const LauncherItem& old_item) {
+}
+
+void ShelfItemDelegateManager::ShelfStatusChanged() {
+}
+
+void ShelfItemDelegateManager::RemoveShelfItemDelegate(LauncherID id) {
+ if (id_to_item_delegate_map_.find(id) != id_to_item_delegate_map_.end()) {
+ delete id_to_item_delegate_map_[id];
+ id_to_item_delegate_map_.erase(id);
+ }
+}
+
+} // namespace ash
diff --git a/ash/shelf/shelf_item_delegate_manager.h b/ash/shelf/shelf_item_delegate_manager.h
new file mode 100644
index 0000000000..dc4df7339b
--- /dev/null
+++ b/ash/shelf/shelf_item_delegate_manager.h
@@ -0,0 +1,67 @@
+// Copyright 2013 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.
+
+#ifndef ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
+#define ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
+
+#include <map>
+
+#include "ash/ash_export.h"
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_model_observer.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace ash {
+class ShelfItemDelegate;
+class ShelfModel;
+
+namespace test {
+class ShelfItemDelegateManagerTestAPI;
+}
+
+// ShelfItemDelegateManager manages the set of ShelfItemDelegates for the
+// launcher. ShelfItemDelegateManager does not create ShelfItemDelegates,
+// rather it is expected that someone else invokes SetShelfItemDelegate
+// appropriately. On the other hand, ShelfItemDelegateManager destroys
+// ShelfItemDelegates when the corresponding item from the model is removed.
+class ASH_EXPORT ShelfItemDelegateManager : public ShelfModelObserver {
+ public:
+ explicit ShelfItemDelegateManager(ShelfModel* model);
+ virtual ~ShelfItemDelegateManager();
+
+ // Set |item_delegate| for |id| and take an ownership.
+ void SetShelfItemDelegate(LauncherID id,
+ scoped_ptr<ShelfItemDelegate> item_delegate);
+
+ // Returns ShelfItemDelegate for |item_type|. Always returns non-NULL.
+ ShelfItemDelegate* GetShelfItemDelegate(LauncherID id);
+
+ // ShelfModelObserver overrides:
+ virtual void ShelfItemAdded(int model_index) OVERRIDE;
+ virtual void ShelfItemRemoved(int index, LauncherID id) OVERRIDE;
+ virtual void ShelfItemMoved(int start_index, int targetindex) OVERRIDE;
+ virtual void ShelfItemChanged(int index,
+ const LauncherItem& old_item) OVERRIDE;
+ virtual void ShelfStatusChanged() OVERRIDE;
+
+ private:
+ friend class test::ShelfItemDelegateManagerTestAPI;
+
+ typedef std::map<LauncherID, ShelfItemDelegate*> LauncherIDToItemDelegateMap;
+
+ // Remove and destroy ShelfItemDelegate for |id|.
+ void RemoveShelfItemDelegate(LauncherID id);
+
+ // Owned by Shell.
+ ShelfModel* model_;
+
+ LauncherIDToItemDelegateMap id_to_item_delegate_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManager);
+};
+
+} // namespace ash
+
+#endif // ASH_SHELF_SHELF_ITEM_DELEGATE_MANAGER_H_
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 566db942d2..116e014013 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -175,9 +175,8 @@ class ShelfLayoutManager::UpdateShelfObserver
}
virtual void OnImplicitAnimationsCompleted() OVERRIDE {
- if (shelf_) {
- shelf_->UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
- }
+ if (shelf_)
+ shelf_->UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
delete this;
}
@@ -319,8 +318,11 @@ void ShelfLayoutManager::UpdateVisibilityState() {
// when we are in SHELF_AUTO_HIDE_ALWAYS_HIDDEN.
WorkspaceWindowState window_state(workspace_controller_->GetWindowState());
switch (window_state) {
- case WORKSPACE_WINDOW_STATE_FULL_SCREEN:
- if (FullscreenWithHiddenShelf()) {
+ case WORKSPACE_WINDOW_STATE_FULL_SCREEN: {
+ const aura::Window* fullscreen_window = GetRootWindowController(
+ root_window_)->GetWindowForFullscreenMode();
+ if (fullscreen_window && wm::GetWindowState(fullscreen_window)->
+ hide_shelf_when_fullscreen()) {
SetState(SHELF_HIDDEN);
} else {
// The shelf is sometimes not hidden when in immersive fullscreen.
@@ -328,9 +330,12 @@ void ShelfLayoutManager::UpdateVisibilityState() {
SetState(SHELF_AUTO_HIDE);
}
break;
+ }
+
case WORKSPACE_WINDOW_STATE_MAXIMIZED:
SetState(CalculateShelfVisibility());
break;
+
case WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF:
case WORKSPACE_WINDOW_STATE_DEFAULT:
SetState(CalculateShelfVisibility());
@@ -366,7 +371,7 @@ void ShelfLayoutManager::UpdateAutoHideState() {
void ShelfLayoutManager::SetWindowOverlapsShelf(bool value) {
window_overlaps_shelf_ = value;
- UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
+ UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
void ShelfLayoutManager::AddObserver(ShelfLayoutManagerObserver* observer) {
@@ -385,7 +390,7 @@ void ShelfLayoutManager::StartGestureDrag(const ui::GestureEvent& gesture) {
gesture_drag_amount_ = 0.f;
gesture_drag_auto_hide_state_ = visibility_state() == SHELF_AUTO_HIDE ?
auto_hide_state() : SHELF_AUTO_HIDE_SHOWN;
- UpdateShelfBackground(BackgroundAnimator::CHANGE_ANIMATE);
+ UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
ShelfLayoutManager::DragState ShelfLayoutManager::UpdateGestureDrag(
@@ -538,16 +543,6 @@ bool ShelfLayoutManager::IsHorizontalAlignment() const {
GetAlignment() == SHELF_ALIGNMENT_TOP;
}
-bool ShelfLayoutManager::FullscreenWithHiddenShelf() const {
- RootWindowController* controller = GetRootWindowController(root_window_);
- if (!controller)
- return false;
- const aura::Window* window = controller->GetTopmostFullscreenWindow();
- if (!window)
- return false;
- return wm::GetWindowState(window)->hide_shelf_when_fullscreen();
-}
-
// static
ShelfLayoutManager* ShelfLayoutManager::ForLauncher(aura::Window* window) {
ShelfWidget* shelf = RootWindowController::ForLauncher(window)->shelf();
@@ -598,8 +593,7 @@ void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
State old_state = state_;
state_ = state;
- BackgroundAnimator::ChangeType change_type =
- BackgroundAnimator::CHANGE_ANIMATE;
+ BackgroundAnimatorChangeType change_type = BACKGROUND_CHANGE_ANIMATE;
bool delay_background_change = false;
// Do not animate the background when:
@@ -610,7 +604,7 @@ void ShelfLayoutManager::SetState(ShelfVisibilityState visibility_state) {
if (state.visibility_state == SHELF_VISIBLE &&
state.window_state == WORKSPACE_WINDOW_STATE_MAXIMIZED &&
old_state.visibility_state != SHELF_VISIBLE) {
- change_type = BackgroundAnimator::CHANGE_IMMEDIATE;
+ change_type = BACKGROUND_CHANGE_IMMEDIATE;
} else {
// Delay the animation when the shelf was hidden, and has just been made
// visible (e.g. using a gesture-drag).
@@ -930,8 +924,11 @@ void ShelfLayoutManager::UpdateTargetBoundsForGesture(
}
void ShelfLayoutManager::UpdateShelfBackground(
- BackgroundAnimator::ChangeType type) {
- shelf_->SetPaintsBackground(GetShelfBackgroundType(), type);
+ BackgroundAnimatorChangeType type) {
+ const ShelfBackgroundType background_type(GetShelfBackgroundType());
+ shelf_->SetPaintsBackground(background_type, type);
+ FOR_EACH_OBSERVER(ShelfLayoutManagerObserver, observers_,
+ OnBackgroundUpdated(background_type, type));
}
ShelfBackgroundType ShelfLayoutManager::GetShelfBackgroundType() const {
@@ -1134,6 +1131,7 @@ void ShelfLayoutManager::OnDockBoundsChanging(
if (dock_bounds_ != dock_bounds) {
dock_bounds_ = dock_bounds;
OnWindowResized();
+ UpdateShelfBackground(BACKGROUND_CHANGE_ANIMATE);
}
}
diff --git a/ash/shelf/shelf_layout_manager.h b/ash/shelf/shelf_layout_manager.h
index 13f90b6ccc..5d0496b8f9 100644
--- a/ash/shelf/shelf_layout_manager.h
+++ b/ash/shelf/shelf_layout_manager.h
@@ -122,6 +122,9 @@ class ASH_EXPORT ShelfLayoutManager :
// Returns the ideal bounds of the shelf assuming it is visible.
gfx::Rect GetIdealBounds();
+ // Returns the docked area bounds.
+ const gfx::Rect& dock_bounds() const { return dock_bounds_; }
+
// Stops any animations and sets the bounds of the launcher and status
// widgets.
void LayoutShelf();
@@ -212,10 +215,6 @@ class ASH_EXPORT ShelfLayoutManager :
// Is the shelf's alignment horizontal?
bool IsHorizontalAlignment() const;
- // Returns true if there is a fullscreen window and the shelf needs to be
- // hidden for the topmost fullscreen window.
- bool FullscreenWithHiddenShelf() const;
-
// Returns a ShelfLayoutManager on the display which has a launcher for
// given |window|. See RootWindowController::ForLauncher for more info.
static ShelfLayoutManager* ForLauncher(aura::Window* window);
@@ -292,7 +291,7 @@ class ASH_EXPORT ShelfLayoutManager :
void UpdateTargetBoundsForGesture(TargetBounds* target_bounds) const;
// Updates the background of the shelf.
- void UpdateShelfBackground(BackgroundAnimator::ChangeType type);
+ void UpdateShelfBackground(BackgroundAnimatorChangeType type);
// Returns how the shelf background is painted.
ShelfBackgroundType GetShelfBackgroundType() const;
@@ -331,7 +330,7 @@ class ASH_EXPORT ShelfLayoutManager :
virtual void OnKeyboardBoundsChanging(
const gfx::Rect& keyboard_bounds) OVERRIDE;
- // Overridden from dock::DockObserver:
+ // Overridden from DockedWindowLayoutManagerObserver:
virtual void OnDockBoundsChanging(
const gfx::Rect& dock_bounds,
DockedWindowLayoutManagerObserver::Reason reason) OVERRIDE;
diff --git a/ash/shelf/shelf_layout_manager_observer.h b/ash/shelf/shelf_layout_manager_observer.h
index 6209980e15..8a2d5830df 100644
--- a/ash/shelf/shelf_layout_manager_observer.h
+++ b/ash/shelf/shelf_layout_manager_observer.h
@@ -6,6 +6,7 @@
#define ASH_SHELF_SHELF_LAYOUT_MANAGER_OBSERVER_H_
#include "ash/ash_export.h"
+#include "ash/shelf/background_animator.h"
#include "ash/shelf/shelf_types.h"
namespace aura {
@@ -30,6 +31,11 @@ class ASH_EXPORT ShelfLayoutManagerObserver {
// Called when the auto hide behavior is changed.
virtual void OnAutoHideBehaviorChanged(aura::Window* root_window,
ShelfAutoHideBehavior new_behavior) {}
+
+ // Called when shelf background animation is started.
+ virtual void OnBackgroundUpdated(
+ ShelfBackgroundType background_type,
+ BackgroundAnimatorChangeType change_type) {}
};
} // namespace ash
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 1009ce98d7..8dac93d1d2 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1326,6 +1326,68 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfHiddenState) {
EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
}
+// Tests that the shelf is only hidden for a fullscreen window at the front and
+// toggles visibility when another window is activated.
+TEST_F(ShelfLayoutManagerTest, FullscreenWindowInFrontHidesShelf) {
+ ShelfLayoutManager* shelf = GetShelfLayoutManager();
+
+ // Create a window and make it full screen.
+ aura::Window* window1 = CreateTestWindow();
+ window1->SetBounds(gfx::Rect(0, 0, 100, 100));
+ window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ window1->Show();
+
+ aura::Window* window2 = CreateTestWindow();
+ window2->SetBounds(gfx::Rect(0, 0, 100, 100));
+ window2->Show();
+
+ wm::GetWindowState(window1)->Activate();
+ EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
+
+ wm::GetWindowState(window2)->Activate();
+ EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state());
+
+ wm::GetWindowState(window1)->Activate();
+ EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state());
+}
+
+// Test the behavior of the shelf when a window on one display is fullscreen
+// but the other display has the active window.
+TEST_F(ShelfLayoutManagerTest, FullscreenWindowOnSecondDisplay) {
+ if (!SupportsMultipleDisplays())
+ return;
+
+ UpdateDisplay("800x600,800x600");
+ DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+ aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+ Shell::RootWindowControllerList root_window_controllers =
+ Shell::GetAllRootWindowControllers();
+
+ // Create windows on either display.
+ aura::Window* window1 = CreateTestWindow();
+ window1->SetBoundsInScreen(
+ gfx::Rect(0, 0, 100, 100),
+ display_manager->GetDisplayAt(0));
+ window1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ window1->Show();
+
+ aura::Window* window2 = CreateTestWindow();
+ window2->SetBoundsInScreen(
+ gfx::Rect(800, 0, 100, 100),
+ display_manager->GetDisplayAt(1));
+ window2->Show();
+
+ EXPECT_EQ(root_windows[0], window1->GetRootWindow());
+ EXPECT_EQ(root_windows[1], window2->GetRootWindow());
+
+ wm::GetWindowState(window2)->Activate();
+ EXPECT_EQ(SHELF_HIDDEN,
+ root_window_controllers[0]->GetShelfLayoutManager()->visibility_state());
+ EXPECT_EQ(SHELF_VISIBLE,
+ root_window_controllers[1]->GetShelfLayoutManager()->visibility_state());
+}
+
+
#if defined(OS_WIN)
// RootWindow and Display can't resize on Windows Ash. http://crbug.com/165962
#define MAYBE_SetAlignment DISABLED_SetAlignment
diff --git a/ash/shelf/shelf_menu_model.h b/ash/shelf/shelf_menu_model.h
new file mode 100644
index 0000000000..18e1dd033e
--- /dev/null
+++ b/ash/shelf/shelf_menu_model.h
@@ -0,0 +1,29 @@
+// Copyright 2013 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.
+
+#ifndef ASH_SHELF_SHELF_MENU_MODEL_H_
+#define ASH_SHELF_SHELF_MENU_MODEL_H_
+
+#include "ash/ash_export.h"
+#include "ui/base/models/simple_menu_model.h"
+
+namespace ash {
+
+// A special menu model which keeps track of an "active" menu item.
+class ASH_EXPORT ShelfMenuModel : public ui::SimpleMenuModel {
+ public:
+ explicit ShelfMenuModel(ui::SimpleMenuModel::Delegate* delegate)
+ : ui::SimpleMenuModel(delegate) {}
+
+ // Returns |true| when the given |command_id| is active and needs to be drawn
+ // in a special state.
+ virtual bool IsCommandActive(int command_id) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShelfMenuModel);
+};
+
+} // namespace ash
+
+#endif // ASH_SHELF_SHELF_MENU_MODEL_H_
diff --git a/ash/shelf/shelf_tooltip_manager.cc b/ash/shelf/shelf_tooltip_manager.cc
index 8e5b780e23..dd20a178a2 100644
--- a/ash/shelf/shelf_tooltip_manager.cc
+++ b/ash/shelf/shelf_tooltip_manager.cc
@@ -29,7 +29,7 @@ namespace internal {
namespace {
const int kTooltipTopBottomMargin = 3;
const int kTooltipLeftRightMargin = 10;
-const int kTooltipAppearanceDelay = 200; // msec
+const int kTooltipAppearanceDelay = 1000; // msec
const int kTooltipMinHeight = 29 - 2 * kTooltipTopBottomMargin;
const SkColor kTooltipTextColor = SkColorSetRGB(0x22, 0x22, 0x22);
diff --git a/ash/shelf/shelf_util.cc b/ash/shelf/shelf_util.cc
index 2e86814f01..77031d803a 100644
--- a/ash/shelf/shelf_util.cc
+++ b/ash/shelf/shelf_util.cc
@@ -4,14 +4,20 @@
#include "ash/shelf/shelf_util.h"
-#include "ui/aura/window.h"
#include "ui/aura/window_property.h"
DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherID);
+DECLARE_WINDOW_PROPERTY_TYPE(ash::LauncherItemDetails*);
namespace ash {
-DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, 0);
+DEFINE_LOCAL_WINDOW_PROPERTY_KEY(LauncherID, kLauncherID, kInvalidLauncherID);
+
+// ash::LauncherItemDetails for kLauncherItemDetaildKey is owned by the window
+// and will be freed automatically.
+DEFINE_OWNED_WINDOW_PROPERTY_KEY(LauncherItemDetails,
+ kLauncherItemDetailsKey,
+ NULL);
void SetLauncherIDForWindow(LauncherID id, aura::Window* window) {
if (!window)
@@ -25,4 +31,20 @@ LauncherID GetLauncherIDForWindow(aura::Window* window) {
return window->GetProperty(kLauncherID);
}
+void SetLauncherItemDetailsForWindow(aura::Window* window,
+ const LauncherItemDetails& details) {
+ // |item_details| is owned by |window|.
+ LauncherItemDetails* item_details = new LauncherItemDetails(details);
+ window->SetProperty(kLauncherItemDetailsKey, item_details);
+}
+
+void ClearLauncherItemDetailsForWindow(aura::Window* window) {
+ window->ClearProperty(kLauncherItemDetailsKey);
+}
+
+const LauncherItemDetails* GetLauncherItemDetailsForWindow(
+ aura::Window* window) {
+ return window->GetProperty(kLauncherItemDetailsKey);
+}
+
} // namespace ash
diff --git a/ash/shelf/shelf_util.h b/ash/shelf/shelf_util.h
index 30953a76ce..6fb3d5efc1 100644
--- a/ash/shelf/shelf_util.h
+++ b/ash/shelf/shelf_util.h
@@ -7,6 +7,7 @@
#include "ash/ash_export.h"
#include "ash/launcher/launcher_types.h"
+#include "ui/aura/window.h"
namespace aura {
class Window;
@@ -14,6 +15,9 @@ class Window;
namespace ash {
+extern const aura::WindowProperty<ash::LauncherItemDetails*>* const
+ kLauncherItemDetailsKey;
+
// Associates LauncherItem of |id| with specified |window|.
ASH_EXPORT void SetLauncherIDForWindow(LauncherID id, aura::Window* window);
@@ -23,6 +27,21 @@ ASH_EXPORT void SetLauncherIDForWindow(LauncherID id, aura::Window* window);
// currently active tab.
ASH_EXPORT LauncherID GetLauncherIDForWindow(aura::Window* window);
+// Sets LauncherItemDetails for |window|.
+ASH_EXPORT void SetLauncherItemDetailsForWindow(
+ aura::Window* window,
+ const LauncherItemDetails& details);
+
+// Clears LauncherItemDetails for |window|.
+// If |window| has a LauncherItem by SetLauncherItemDetailsForWindow(), it will
+// be removed.
+ASH_EXPORT void ClearLauncherItemDetailsForWindow(aura::Window* window);
+
+// Returns LauncherItemDetails for |window| or NULL if it doesn't have.
+// Returned LauncherItemDetails object is owned by the |window|.
+ASH_EXPORT const LauncherItemDetails* GetLauncherItemDetailsForWindow(
+ aura::Window* window);
+
} // namespace ash
#endif // ASH_SHELF_SHELF_UTIL_H_
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 9393368234..feab088a99 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -9,9 +9,6 @@
#include "ash/ash_constants.h"
#include "ash/ash_switches.h"
#include "ash/drag_drop/drag_image_view.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
#include "ash/root_window_controller.h"
#include "ash/scoped_target_root_window.h"
#include "ash/shelf/alternate_app_list_button.h"
@@ -20,8 +17,12 @@
#include "ash/shelf/overflow_bubble_view.h"
#include "ash/shelf/overflow_button.h"
#include "ash/shelf/shelf_button.h"
+#include "ash/shelf/shelf_delegate.h"
#include "ash/shelf/shelf_icon_observer.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_layout_manager.h"
+#include "ash/shelf/shelf_menu_model.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_tooltip_manager.h"
#include "ash/shelf/shelf_widget.h"
@@ -50,7 +51,6 @@
#include "ui/views/controls/menu/menu_model_adapter.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/focus/focus_search.h"
-#include "ui/views/focus_border.h"
#include "ui/views/view_model.h"
#include "ui/views/view_model_utils.h"
#include "ui/views/widget/widget.h"
@@ -114,16 +114,18 @@ const int kRipOffDistance = 48;
// The rip off drag and drop proxy image should get scaled by this factor.
const float kDragAndDropProxyScale = 1.5f;
+// The opacity represents that this partially disappeared item will get removed.
+const float kDraggedImageOpacity = 0.5f;
+
namespace {
// The MenuModelAdapter gets slightly changed to adapt the menu appearance to
// our requirements.
-class LauncherMenuModelAdapter
- : public views::MenuModelAdapter {
+class ShelfMenuModelAdapter : public views::MenuModelAdapter {
public:
- explicit LauncherMenuModelAdapter(ash::LauncherMenuModel* menu_model);
+ explicit ShelfMenuModelAdapter(ShelfMenuModel* menu_model);
- // Overriding MenuModelAdapter's MenuDelegate implementation.
+ // views::MenuModelAdapter:
virtual const gfx::Font* GetLabelFont(int command_id) const OVERRIDE;
virtual bool IsCommandEnabled(int id) const OVERRIDE;
virtual void GetHorizontalIconMargins(int id,
@@ -140,18 +142,17 @@ class LauncherMenuModelAdapter
virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE;
private:
- ash::LauncherMenuModel* launcher_menu_model_;
+ ShelfMenuModel* menu_model_;
- DISALLOW_COPY_AND_ASSIGN(LauncherMenuModelAdapter);
+ DISALLOW_COPY_AND_ASSIGN(ShelfMenuModelAdapter);
};
-LauncherMenuModelAdapter::LauncherMenuModelAdapter(
- ash::LauncherMenuModel* menu_model)
+ShelfMenuModelAdapter::ShelfMenuModelAdapter(ShelfMenuModel* menu_model)
: MenuModelAdapter(menu_model),
- launcher_menu_model_(menu_model) {}
+ menu_model_(menu_model) {
+}
-const gfx::Font* LauncherMenuModelAdapter::GetLabelFont(
- int command_id) const {
+const gfx::Font* ShelfMenuModelAdapter::GetLabelFont(int command_id) const {
if (command_id != kCommandIdOfMenuName)
return MenuModelAdapter::GetLabelFont(command_id);
@@ -159,14 +160,13 @@ const gfx::Font* LauncherMenuModelAdapter::GetLabelFont(
return &rb.GetFont(ui::ResourceBundle::BoldFont);
}
-bool LauncherMenuModelAdapter::IsCommandEnabled(int id) const {
+bool ShelfMenuModelAdapter::IsCommandEnabled(int id) const {
return id != kCommandIdOfMenuName;
}
-bool LauncherMenuModelAdapter::GetForegroundColor(
- int command_id,
- bool is_hovered,
- SkColor* override_color) const {
+bool ShelfMenuModelAdapter::GetForegroundColor(int command_id,
+ bool is_hovered,
+ SkColor* override_color) const {
if (command_id != kCommandIdOfMenuName)
return false;
@@ -174,11 +174,10 @@ bool LauncherMenuModelAdapter::GetForegroundColor(
return true;
}
-bool LauncherMenuModelAdapter::GetBackgroundColor(
- int command_id,
- bool is_hovered,
- SkColor* override_color) const {
- if (!launcher_menu_model_->IsCommandActive(command_id))
+bool ShelfMenuModelAdapter::GetBackgroundColor(int command_id,
+ bool is_hovered,
+ SkColor* override_color) const {
+ if (!menu_model_->IsCommandActive(command_id))
return false;
*override_color = is_hovered ? kFocusedActiveListItemBackgroundColor :
@@ -186,21 +185,20 @@ bool LauncherMenuModelAdapter::GetBackgroundColor(
return true;
}
-void LauncherMenuModelAdapter::GetHorizontalIconMargins(
- int command_id,
- int icon_size,
- int* left_margin,
- int* right_margin) const {
+void ShelfMenuModelAdapter::GetHorizontalIconMargins(int command_id,
+ int icon_size,
+ int* left_margin,
+ int* right_margin) const {
*left_margin = kHorizontalIconSpacing;
*right_margin = (command_id != kCommandIdOfMenuName) ?
kHorizontalIconSpacing : -(icon_size + kHorizontalNoIconInsetSpacing);
}
-int LauncherMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
+int ShelfMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
return kMaximumAppMenuItemLength;
}
-bool LauncherMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
+bool ShelfMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
return false;
}
@@ -243,22 +241,6 @@ class LauncherFocusSearch : public views::FocusSearch {
DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch);
};
-class ShelfButtonFocusBorder : public views::FocusBorder {
- public:
- ShelfButtonFocusBorder() {}
- virtual ~ShelfButtonFocusBorder() {}
-
- private:
- // views::FocusBorder overrides:
- virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE {
- gfx::Rect rect(view.GetLocalBounds());
- rect.Inset(1, 1);
- canvas->DrawRect(rect, kFocusBorderColor);
- }
-
- DISALLOW_COPY_AND_ASSIGN(ShelfButtonFocusBorder);
-};
-
// AnimationDelegate used when inserting a new item. This steadily increases the
// opacity of the layer as the animation progress.
class FadeInAnimationDelegate
@@ -370,8 +352,8 @@ class ShelfView::StartFadeAnimationDelegate
};
ShelfView::ShelfView(ShelfModel* model,
- LauncherDelegate* delegate,
- ShelfLayoutManager* shelf_layout_manager)
+ ShelfDelegate* delegate,
+ ShelfLayoutManager* manager)
: model_(model),
delegate_(delegate),
view_model_(new views::ViewModel),
@@ -393,15 +375,17 @@ ShelfView::ShelfView(ShelfModel* model,
drag_and_drop_launcher_id_(0),
dragged_off_shelf_(false),
snap_back_from_rip_off_view_(NULL),
- item_manager_(Shell::GetInstance()->launcher_item_delegate_manager()),
- layout_manager_(shelf_layout_manager),
- overflow_mode_(false) {
+ item_manager_(Shell::GetInstance()->shelf_item_delegate_manager()),
+ layout_manager_(manager),
+ overflow_mode_(false),
+ main_shelf_(NULL),
+ dragged_off_from_overflow_to_shelf_(false) {
DCHECK(model_);
bounds_animator_.reset(new views::BoundsAnimator(this));
bounds_animator_->AddObserver(this);
set_context_menu_controller(this);
focus_search_.reset(new LauncherFocusSearch(view_model_.get()));
- tooltip_.reset(new ShelfTooltipManager(shelf_layout_manager, this));
+ tooltip_.reset(new ShelfTooltipManager(manager, this));
}
ShelfView::~ShelfView() {
@@ -602,7 +586,11 @@ bool ShelfView::StartDrag(const std::string& app_id,
// Check if the application is known and pinned - if not, we have to pin it so
// that we can re-arrange the launcher order accordingly. Note that items have
// to be pinned to give them the same (order) possibilities as a shortcut.
- if (!drag_and_drop_launcher_id_ || !delegate_->IsAppPinned(app_id)) {
+ // When an item is dragged from overflow to shelf, IsShowingOverflowBubble()
+ // returns true. At this time, we don't need to pin the item.
+ if (!IsShowingOverflowBubble() &&
+ (!drag_and_drop_launcher_id_ ||
+ !delegate_->IsAppPinned(app_id))) {
delegate_->PinAppWithID(app_id);
drag_and_drop_launcher_id_ =
delegate_->GetLauncherIDForAppID(drag_and_drop_app_id_);
@@ -695,6 +683,27 @@ void ShelfView::LayoutToIdealBounds() {
overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
}
+void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
+ // The overflow button is not shown in overflow mode.
+ overflow_button_->SetVisible(false);
+ int last_button_index = model_->FirstPanelIndex() - 1;
+ DCHECK_LT(last_visible_index_, view_model_->view_size());
+ for (int i = 0; i < view_model_->view_size(); ++i) {
+ bool visible = i >= first_visible_index_ &&
+ i <= last_visible_index_;
+ if (!ash::switches::UseAlternateShelfLayout())
+ visible &= i != last_button_index;
+
+ // To track the dragging of |drag_view_| continuously, its visibility
+ // should be always true regardless of its position.
+ if (dragged_off_from_overflow_to_shelf_ &&
+ view_model_->view_at(i) == drag_view_)
+ view_model_->view_at(i)->SetVisible(true);
+ else
+ view_model_->view_at(i)->SetVisible(visible);
+ }
+}
+
void ShelfView::CalculateIdealBounds(IdealBounds* bounds) {
int available_size = layout_manager_->PrimaryAxisValue(width(), height());
DCHECK(model_->item_count() == view_model_->view_size());
@@ -730,16 +739,7 @@ void ShelfView::CalculateIdealBounds(IdealBounds* bounds) {
}
if (is_overflow_mode()) {
- // The overflow button is not shown in overflow mode.
- overflow_button_->SetVisible(false);
- DCHECK_LT(last_visible_index_, view_model_->view_size());
- for (int i = 0; i < view_model_->view_size(); ++i) {
- bool visible = i >= first_visible_index_ &&
- i <= last_visible_index_;
- if (!ash::switches::UseAlternateShelfLayout())
- visible &= i != last_button_index;
- view_model_->view_at(i)->SetVisible(visible);
- }
+ UpdateAllButtonsVisibilityInOverflowMode();
return;
}
@@ -952,7 +952,6 @@ views::View* ShelfView::CreateViewForItem(const LauncherItem& item) {
break;
}
view->set_context_menu_controller(this);
- view->set_focus_border(new ShelfButtonFocusBorder);
DCHECK(view);
ConfigureChildView(view);
@@ -979,7 +978,7 @@ void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
}
// If the item is no longer draggable, bail out.
- LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+ ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
model_->items()[start_drag_index_].id);
if (!item_delegate->IsDraggable()) {
CancelDrag(-1);
@@ -997,7 +996,7 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
int current_index = view_model_->GetIndexOfView(drag_view_);
DCHECK_NE(-1, current_index);
- LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+ ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
model_->items()[current_index].id);
if (!item_delegate->IsDraggable()) {
CancelDrag(-1);
@@ -1069,6 +1068,8 @@ void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
int current_index = view_model_->GetIndexOfView(drag_view_);
DCHECK_NE(-1, current_index);
+ std::string dragged_app_id =
+ delegate_->GetAppIDForLauncherID(model_->items()[current_index].id);
gfx::Point screen_location = event.root_location();
ash::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(),
@@ -1080,17 +1081,47 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
// If the shelf/overflow bubble bounds contains |screen_location| we insert
// the item back into the shelf.
if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
+ if (dragged_off_from_overflow_to_shelf_) {
+ // During the dragging an item from Shelf to Overflow, it can enter here
+ // directly because both are located very closly.
+ main_shelf_->EndDrag(true);
+ // Stops the animation of |drag_view_| and sets its bounds explicitly
+ // becase ContinueDrag() stops its animation. Without this, unexpected
+ // bounds will be set.
+ bounds_animator_->StopAnimatingView(drag_view_);
+ int drag_view_index = view_model_->GetIndexOfView(drag_view_);
+ drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
+ dragged_off_from_overflow_to_shelf_ = false;
+ }
// Destroy our proxy view item.
DestroyDragIconProxy();
// Re-insert the item and return simply false since the caller will handle
// the move as in any normal case.
dragged_off_shelf_ = false;
drag_view_->layer()->SetOpacity(1.0f);
- // Overflow bubble should be enlarged immediately when an item is
- // re-inserted.
+ // The size of Overflow bubble should be updated immediately when an item
+ // is re-inserted.
if (is_overflow_mode())
PreferredSizeChanged();
return false;
+ } else if (is_overflow_mode() &&
+ main_shelf_->GetBoundsForDragInsertInScreen().Contains(
+ screen_location)) {
+ if (!dragged_off_from_overflow_to_shelf_) {
+ dragged_off_from_overflow_to_shelf_ = true;
+ drag_image_->SetOpacity(1.0f);
+ main_shelf_->StartDrag(dragged_app_id, screen_location);
+ } else {
+ main_shelf_->Drag(screen_location);
+ }
+ } else if (dragged_off_from_overflow_to_shelf_) {
+ // Makes the |drag_image_| partially disappear again.
+ dragged_off_from_overflow_to_shelf_ = false;
+ drag_image_->SetOpacity(kDraggedImageOpacity);
+ main_shelf_->EndDrag(true);
+ bounds_animator_->StopAnimatingView(drag_view_);
+ int drag_view_index = view_model_->GetIndexOfView(drag_view_);
+ drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
}
// Move our proxy view item.
UpdateDragIconProxy(screen_location);
@@ -1122,7 +1153,7 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
}
// Make the item partially disappear to show that it will get removed if
// dropped.
- drag_image_->SetOpacity(0.5f);
+ drag_image_->SetOpacity(kDraggedImageOpacity);
}
return true;
}
@@ -1150,9 +1181,13 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) {
bool snap_back = false;
// Items which cannot be dragged off will be handled as a cancel.
if (!cancel) {
- // Make sure we do not try to remove un-removable items like items which
- // were not pinned or have to be always there.
- if (RemovableByRipOff(current_index) != REMOVABLE) {
+ if (dragged_off_from_overflow_to_shelf_) {
+ dragged_off_from_overflow_to_shelf_ = false;
+ main_shelf_->EndDrag(false);
+ drag_view_->layer()->SetOpacity(1.0f);
+ } else if (RemovableByRipOff(current_index) != REMOVABLE) {
+ // Make sure we do not try to remove un-removable items like items which
+ // were not pinned or have to be always there.
cancel = true;
snap_back = true;
} else {
@@ -1164,7 +1199,12 @@ void ShelfView::FinalizeRipOffDrag(bool cancel) {
}
}
if (cancel || snap_back) {
- if (!cancelling_drag_model_changed_) {
+ if (dragged_off_from_overflow_to_shelf_) {
+ dragged_off_from_overflow_to_shelf_ = false;
+ // Main shelf handles revert of dragged item.
+ main_shelf_->EndDrag(true);
+ drag_view_->layer()->SetOpacity(1.0f);
+ } else if (!cancelling_drag_model_changed_) {
// Only do something if the change did not come through a model change.
gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
gfx::Point relative_to = GetBoundsInScreen().origin();
@@ -1253,6 +1293,7 @@ void ShelfView::ToggleOverflowBubble() {
overflow_view->Init();
overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
overflow_view->OnShelfAlignmentChanged();
+ overflow_view->main_shelf_ = this;
UpdateOverflowRange(overflow_view);
overflow_bubble_->Show(overflow_button_, overflow_view);
@@ -1421,7 +1462,11 @@ gfx::Size ShelfView::GetPreferredSize() {
// When an item is dragged off from the overflow bubble, it is moved to last
// position and and changed to invisible. Overflow bubble size should be
// shrunk to fit only for visible items.
- if (is_overflow_mode() && dragged_off_shelf_ &&
+ // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
+ // items in the shelf.
+ if (is_overflow_mode() &&
+ dragged_off_shelf_ &&
+ !dragged_off_from_overflow_to_shelf_ &&
RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
last_button_index--;
@@ -1614,7 +1659,7 @@ void ShelfView::PointerPressedOnButton(views::View* view,
if (index == -1)
return;
- LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+ ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
model_->items()[index].id);
if (view_model_->view_size() <= 1 || !item_delegate->IsDraggable())
return; // View is being deleted or not draggable, ignore request.
@@ -1690,7 +1735,7 @@ base::string16 ShelfView::GetAccessibleName(const views::View* view) {
if (view_index == -1)
return base::string16();
- LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+ ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
model_->items()[view_index].id);
return item_delegate->GetTitle();
}
@@ -1748,9 +1793,8 @@ void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) {
break;
}
- LauncherItemDelegate* item_delegate =
- item_manager_->GetLauncherItemDelegate(
- model_->items()[view_index].id);
+ ShelfItemDelegate* item_delegate =
+ item_manager_->GetShelfItemDelegate(model_->items()[view_index].id);
if (!item_delegate->ItemSelected(event))
ShowListMenuForView(model_->items()[view_index], sender, event);
}
@@ -1759,9 +1803,9 @@ void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) {
bool ShelfView::ShowListMenuForView(const LauncherItem& item,
views::View* source,
const ui::Event& event) {
- scoped_ptr<ash::LauncherMenuModel> menu_model;
- LauncherItemDelegate* item_delegate =
- item_manager_->GetLauncherItemDelegate(item.id);
+ scoped_ptr<ShelfMenuModel> menu_model;
+ ShelfItemDelegate* item_delegate =
+ item_manager_->GetShelfItemDelegate(item.id);
menu_model.reset(item_delegate->CreateApplicationMenu(event.flags()));
// Make sure we have a menu and it has at least two items in addition to the
@@ -1770,7 +1814,7 @@ bool ShelfView::ShowListMenuForView(const LauncherItem& item,
return false;
ShowMenu(scoped_ptr<views::MenuModelAdapter>(
- new LauncherMenuModelAdapter(menu_model.get())),
+ new ShelfMenuModelAdapter(menu_model.get())),
source,
gfx::Point(),
false,
@@ -1783,9 +1827,8 @@ void ShelfView::ShowContextMenuForView(views::View* source,
ui::MenuSourceType source_type) {
int view_index = view_model_->GetIndexOfView(source);
// TODO(simon.hong81): Create LauncherContextMenu for applist in its
- // LauncherItemDelegate.
- if (view_index != -1 &&
- model_->items()[view_index].type == TYPE_APP_LIST) {
+ // ShelfItemDelegate.
+ if (view_index != -1 && model_->items()[view_index].type == TYPE_APP_LIST) {
view_index = -1;
}
@@ -1794,7 +1837,7 @@ void ShelfView::ShowContextMenuForView(views::View* source,
return;
}
scoped_ptr<ui::MenuModel> menu_model;
- LauncherItemDelegate* item_delegate = item_manager_->GetLauncherItemDelegate(
+ ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
model_->items()[view_index].id);
menu_model.reset(item_delegate->CreateContextMenu(
source->GetWidget()->GetNativeView()->GetRootWindow()));
@@ -1955,8 +1998,8 @@ bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
const LauncherItem* item = LauncherItemForView(view);
if (!item)
return true;
- LauncherItemDelegate* item_delegate =
- item_manager_->GetLauncherItemDelegate(item->id);
+ ShelfItemDelegate* item_delegate =
+ item_manager_->GetShelfItemDelegate(item->id);
return item_delegate->ShouldShowTooltip();
}
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 4d6545e3a2..1f090e612a 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -32,11 +32,11 @@ namespace test {
class ShelfViewTestAPI;
}
-class LauncherDelegate;
-struct LauncherItem;
-class LauncherItemDelegateManager;
+class ShelfDelegate;
class ShelfIconObserver;
+class ShelfItemDelegateManager;
class ShelfModel;
+struct LauncherItem;
namespace internal {
@@ -62,8 +62,8 @@ class ASH_EXPORT ShelfView : public views::View,
public app_list::ApplicationDragAndDropHost {
public:
ShelfView(ShelfModel* model,
- LauncherDelegate* delegate,
- ShelfLayoutManager* shelf_layout_manager);
+ ShelfDelegate* delegate,
+ ShelfLayoutManager* manager);
virtual ~ShelfView();
ShelfTooltipManager* tooltip_manager() { return tooltip_.get(); }
@@ -168,6 +168,9 @@ class ASH_EXPORT ShelfView : public views::View,
// Sets the bounds of each view to its ideal bounds.
void LayoutToIdealBounds();
+ // Update all button's visibility in overflow.
+ void UpdateAllButtonsVisibilityInOverflowMode();
+
// Calculates the ideal bounds. The bounds of each button corresponding to an
// item in the model is set in |view_model_|.
void CalculateIdealBounds(IdealBounds* bounds);
@@ -335,7 +338,7 @@ class ASH_EXPORT ShelfView : public views::View,
ShelfModel* model_;
// Delegate; owned by Launcher.
- LauncherDelegate* delegate_;
+ ShelfDelegate* delegate_;
// Used to manage the set of active launcher buttons. There is a view per
// item in |model_|.
@@ -431,8 +434,8 @@ class ASH_EXPORT ShelfView : public views::View,
// The rip off view when a snap back operation is underway.
views::View* snap_back_from_rip_off_view_;
- // Holds LauncherItemDelegateManager.
- LauncherItemDelegateManager* item_manager_;
+ // Holds ShelfItemDelegateManager.
+ ShelfItemDelegateManager* item_manager_;
// Holds ShelfLayoutManager.
ShelfLayoutManager* layout_manager_;
@@ -440,6 +443,12 @@ class ASH_EXPORT ShelfView : public views::View,
// True when this ShelfView is used for Overflow Bubble.
bool overflow_mode_;
+ // Holds a pointer to main ShelfView when a ShelfView is in overflow mode.
+ ShelfView* main_shelf_;
+
+ // True when ripped item from overflow bubble is entered into Shelf.
+ bool dragged_off_from_overflow_to_shelf_;
+
DISALLOW_COPY_AND_ASSIGN(ShelfView);
};
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 28812d192d..d3e2b1d2e6 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -9,13 +9,13 @@
#include "ash/ash_switches.h"
#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
#include "ash/launcher/launcher_types.h"
#include "ash/root_window_controller.h"
#include "ash/shelf/overflow_bubble.h"
#include "ash/shelf/overflow_bubble_view.h"
#include "ash/shelf/shelf_button.h"
#include "ash/shelf/shelf_icon_observer.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_tooltip_manager.h"
@@ -27,12 +27,13 @@
#include "ash/test/overflow_bubble_view_test_api.h"
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
-#include "ash/test/test_launcher_item_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
+#include "ash/test/test_shelf_item_delegate.h"
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
#include "grit/ash_resources.h"
#include "ui/aura/root_window.h"
#include "ui/aura/test/aura_test_base.h"
@@ -81,7 +82,7 @@ class TestShelfIconObserver : public ShelfIconObserver {
DISALLOW_COPY_AND_ASSIGN(TestShelfIconObserver);
};
-class ShelfViewIconObserverTest : public ash::test::AshTestBase {
+class ShelfViewIconObserverTest : public AshTestBase {
public:
ShelfViewIconObserverTest() {}
virtual ~ShelfViewIconObserverTest() {}
@@ -119,9 +120,8 @@ class ShelfViewIconObserverTest : public ash::test::AshTestBase {
};
TEST_F(ShelfViewIconObserverTest, AddRemove) {
- ash::test::TestLauncherDelegate* launcher_delegate =
- ash::test::TestLauncherDelegate::instance();
- ASSERT_TRUE(launcher_delegate);
+ TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance();
+ ASSERT_TRUE(shelf_delegate);
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -130,7 +130,7 @@ TEST_F(ShelfViewIconObserverTest, AddRemove) {
scoped_ptr<views::Widget> widget(new views::Widget());
widget->Init(params);
- launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
+ shelf_delegate->AddLauncherItem(widget->GetNativeWindow());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(observer()->change_notified());
observer()->Reset();
@@ -156,9 +156,8 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) {
UpdateDisplay("400x400,400x400");
TestShelfIconObserver second_observer(LauncherForSecondaryDisplay());
- ash::test::TestLauncherDelegate* launcher_delegate =
- ash::test::TestLauncherDelegate::instance();
- ASSERT_TRUE(launcher_delegate);
+ TestShelfDelegate* shelf_delegate = TestShelfDelegate::instance();
+ ASSERT_TRUE(shelf_delegate);
views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -167,7 +166,7 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) {
scoped_ptr<views::Widget> widget(new views::Widget());
widget->Init(params);
- launcher_delegate->AddLauncherItem(widget->GetNativeWindow());
+ shelf_delegate->AddLauncherItem(widget->GetNativeWindow());
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
EXPECT_TRUE(observer()->change_notified());
EXPECT_TRUE(second_observer.change_notified());
@@ -184,7 +183,7 @@ TEST_F(ShelfViewIconObserverTest, MAYBE_AddRemoveWithMultipleDisplays) {
}
TEST_F(ShelfViewIconObserverTest, BoundsChanged) {
- ash::ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf();
+ ShelfWidget* shelf = Shell::GetPrimaryRootWindowController()->shelf();
Launcher* launcher = Launcher::ForPrimaryDisplay();
gfx::Size shelf_size =
shelf->GetWindowBoundsInScreen().size();
@@ -199,24 +198,63 @@ TEST_F(ShelfViewIconObserverTest, BoundsChanged) {
////////////////////////////////////////////////////////////////////////////////
// ShelfView tests.
-// LauncherItemDelegate for ShelfViewTest.OverflowBubbleSize only.
-// This class should only be used for re-insert test because it cannot handle
-// unpin request.
-class TestLauncherDelegateForShelfView : public TestLauncherDelegate {
+// Simple ShelfDelegate implmentation for ShelfViewTest.OverflowBubbleSize
+// and CheckDragAndDropFromOverflowBubbleToShelf
+class TestShelfDelegateForShelfView : public ShelfDelegate {
public:
- explicit TestLauncherDelegateForShelfView(ShelfModel* model)
- : TestLauncherDelegate(model) {}
- virtual ~TestLauncherDelegateForShelfView() {}
+ explicit TestShelfDelegateForShelfView(ShelfModel* model)
+ : model_(model) {}
+ virtual ~TestShelfDelegateForShelfView() {}
+
+ // ShelfDelegate overrides:
+ virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE {}
+
+ virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE {}
+
+ virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE {
+ LauncherID id = 0;
+ EXPECT_TRUE(base::StringToInt(app_id, &id));
+ return id;
+ }
+
+ virtual const std::string& GetAppIDForLauncherID(LauncherID id) OVERRIDE {
+ // Use |app_id_| member variable because returning a reference to local
+ // variable is not allowed.
+ app_id_ = base::IntToString(id);
+ return app_id_;
+ }
+
+ virtual void PinAppWithID(const std::string& app_id) OVERRIDE {
+ }
- // TestLauncherDelegate overrides:
virtual bool IsAppPinned(const std::string& app_id) OVERRIDE {
// Returns true for ShelfViewTest.OverflowBubbleSize. To test ripping off in
// that test, an item is already pinned state.
return true;
}
+ virtual bool CanPin() const OVERRIDE {
+ return true;
+ }
+
+ virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE {
+ LauncherID id = 0;
+ EXPECT_TRUE(base::StringToInt(app_id, &id));
+ ASSERT_GT(id, 0);
+ int index = model_->ItemIndexByID(id);
+ ASSERT_GE(index, 0);
+
+ model_->RemoveItemAt(index);
+ }
+
private:
- DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegateForShelfView);
+ ShelfModel* model_;
+
+ // Temp member variable for returning a value. See the comment in the
+ // GetAppIDForLauncherID().
+ std::string app_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestShelfDelegateForShelfView);
};
class ShelfViewTest : public AshTestBase {
@@ -238,8 +276,7 @@ class ShelfViewTest : public AshTestBase {
test_api_.reset(new ShelfViewTestAPI(shelf_view_));
test_api_->SetAnimationDuration(1); // Speeds up animation for test.
- item_manager_ =
- ash::Shell::GetInstance()->launcher_item_delegate_manager();
+ item_manager_ = Shell::GetInstance()->shelf_item_delegate_manager();
DCHECK(item_manager_);
// Add browser shortcut launcher item at index 0 for test.
@@ -252,10 +289,9 @@ class ShelfViewTest : public AshTestBase {
}
protected:
- void CreateAndSetLauncherItemDelegateForID(LauncherID id) {
- scoped_ptr<LauncherItemDelegate> delegate(
- new ash::test::TestLauncherItemDelegate(NULL));
- item_manager_->SetLauncherItemDelegate(id, delegate.Pass());
+ void CreateAndSetShelfItemDelegateForID(LauncherID id) {
+ scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(NULL));
+ item_manager_->SetShelfItemDelegate(id, delegate.Pass());
}
LauncherID AddBrowserShortcut() {
@@ -264,7 +300,7 @@ class ShelfViewTest : public AshTestBase {
LauncherID id = model_->next_id();
model_->AddAt(browser_index_, browser_shortcut);
- CreateAndSetLauncherItemDelegateForID(id);
+ CreateAndSetShelfItemDelegateForID(id);
test_api_->RunMessageLoopUntilAnimationsDone();
return id;
}
@@ -276,7 +312,7 @@ class ShelfViewTest : public AshTestBase {
LauncherID id = model_->next_id();
model_->Add(item);
- CreateAndSetLauncherItemDelegateForID(id);
+ CreateAndSetShelfItemDelegateForID(id);
test_api_->RunMessageLoopUntilAnimationsDone();
return id;
}
@@ -294,7 +330,7 @@ class ShelfViewTest : public AshTestBase {
LauncherID id = model_->next_id();
model_->Add(item);
- CreateAndSetLauncherItemDelegateForID(id);
+ CreateAndSetShelfItemDelegateForID(id);
return id;
}
@@ -305,7 +341,7 @@ class ShelfViewTest : public AshTestBase {
LauncherID id = model_->next_id();
model_->Add(item);
- CreateAndSetLauncherItemDelegateForID(id);
+ CreateAndSetShelfItemDelegateForID(id);
return id;
}
@@ -336,8 +372,8 @@ class ShelfViewTest : public AshTestBase {
for (size_t model_index = 0;
model_index < model_->items().size();
++model_index) {
- ash::LauncherItem item = model_->items()[model_index];
- ash::LauncherID id = item.id;
+ LauncherItem item = model_->items()[model_index];
+ LauncherID id = item.id;
EXPECT_EQ(id_map[map_index].first, id);
EXPECT_EQ(id_map[map_index].second, GetButtonByID(id));
++map_index;
@@ -419,14 +455,115 @@ class ShelfViewTest : public AshTestBase {
return shelf_view_->tooltip_manager()->anchor_;
}
+ void AddButtonsUntilOverflow() {
+ int items_added = 0;
+ while (!test_api_->IsOverflowButtonVisible()) {
+ AddAppShortcut();
+ ++items_added;
+ ASSERT_LT(items_added, 10000);
+ }
+ }
+
void ShowTooltip() {
shelf_view_->tooltip_manager()->ShowInternal();
}
+ void TestDraggingAnItemFromOverflowToShelf(bool cancel) {
+ test_api_->ShowOverflowBubble();
+ ASSERT_TRUE(test_api_->overflow_bubble() &&
+ test_api_->overflow_bubble()->IsShowing());
+
+ ash::test::ShelfViewTestAPI test_api_for_overflow(
+ test_api_->overflow_bubble()->shelf_view());
+
+ int total_item_count = model_->item_count();
+
+ int last_visible_item_id_in_shelf =
+ model_->items()[test_api_->GetLastVisibleIndex()].id;
+ int second_last_visible_item_id_in_shelf =
+ model_->items()[test_api_->GetLastVisibleIndex() - 1].id;
+ int first_visible_item_id_in_overflow =
+ model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id;
+ int second_last_visible_item_id_in_overflow =
+ model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id;
+
+ int drag_item_index =
+ test_api_for_overflow.GetLastVisibleIndex();
+ LauncherID drag_item_id = model_->items()[drag_item_index].id;
+ internal::ShelfButton* drag_button =
+ test_api_for_overflow.GetButton(drag_item_index);
+ gfx::Point center_point_of_drag_item =
+ drag_button->GetBoundsInScreen().CenterPoint();
+
+ aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(),
+ center_point_of_drag_item);
+ // Rip an item off to OverflowBubble.
+ generator.PressLeftButton();
+ gfx::Point rip_off_point(center_point_of_drag_item.x(), 0);
+ generator.MoveMouseTo(rip_off_point);
+ test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+ ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem());
+ ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+ // Move a dragged item into Shelf at |drop_index|.
+ int drop_index = 1;
+ gfx::Point drop_point =
+ test_api_->GetButton(drop_index)->GetBoundsInScreen().CenterPoint();
+ int item_width = test_api_for_overflow.GetButtonSize();
+ // To insert at |drop_index|, more smaller x-axis value of |drop_point|
+ // should be used.
+ gfx::Point modified_drop_point(drop_point.x() - item_width / 4,
+ drop_point.y());
+ generator.MoveMouseTo(modified_drop_point);
+ test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+ test_api_->RunMessageLoopUntilAnimationsDone();
+ ASSERT_TRUE(test_api_for_overflow.IsDraggingShelfItem());
+ ASSERT_TRUE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+ if (cancel)
+ drag_button->OnMouseCaptureLost();
+ else
+ generator.ReleaseLeftButton();
+
+ test_api_for_overflow.RunMessageLoopUntilAnimationsDone();
+ test_api_->RunMessageLoopUntilAnimationsDone();
+ ASSERT_FALSE(test_api_for_overflow.IsDraggingShelfItem());
+ ASSERT_FALSE(test_api_for_overflow.DraggedItemFromOverflowToShelf());
+
+ // Compare pre-stored items' id with newly positioned items' after dragging
+ // is canceled or finished.
+ if (cancel) {
+ EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id,
+ last_visible_item_id_in_shelf);
+ EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex() - 1].id,
+ second_last_visible_item_id_in_shelf);
+ EXPECT_EQ(
+ model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id,
+ first_visible_item_id_in_overflow);
+ EXPECT_EQ(
+ model_->items()[test_api_for_overflow.GetLastVisibleIndex() - 1].id,
+ second_last_visible_item_id_in_overflow);
+ } else {
+ LauncherID drop_item_id = model_->items()[drop_index].id;
+ EXPECT_EQ(drop_item_id, drag_item_id);
+ EXPECT_EQ(model_->item_count(), total_item_count);
+ EXPECT_EQ(
+ model_->items()[test_api_for_overflow.GetFirstVisibleIndex()].id,
+ last_visible_item_id_in_shelf);
+ EXPECT_EQ(model_->items()[test_api_->GetLastVisibleIndex()].id,
+ second_last_visible_item_id_in_shelf);
+ EXPECT_EQ(
+ model_->items()[test_api_for_overflow.GetFirstVisibleIndex() + 1].id,
+ first_visible_item_id_in_overflow);
+ EXPECT_EQ(model_->items()[test_api_for_overflow.GetLastVisibleIndex()].id,
+ second_last_visible_item_id_in_overflow);
+ }
+ }
+
ShelfModel* model_;
internal::ShelfView* shelf_view_;
int browser_index_;
- LauncherItemDelegateManager* item_manager_;
+ ShelfItemDelegateManager* item_manager_;
scoped_ptr<ShelfViewTestAPI> test_api_;
@@ -1018,10 +1155,10 @@ TEST_F(ShelfViewTest, LauncherItemStatus) {
int index = model_->ItemIndexByID(last_added);
internal::ShelfButton* button = GetButtonByID(last_added);
ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state());
- item.status = ash::STATUS_ACTIVE;
+ item.status = STATUS_ACTIVE;
model_->Set(index, item);
ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state());
- item.status = ash::STATUS_ATTENTION;
+ item.status = STATUS_ATTENTION;
model_->Set(index, item);
ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state());
}
@@ -1071,10 +1208,10 @@ TEST_F(ShelfViewTest, LauncherItemStatusPlatformApp) {
int index = model_->ItemIndexByID(last_added);
internal::ShelfButton* button = GetButtonByID(last_added);
ASSERT_EQ(internal::ShelfButton::STATE_RUNNING, button->state());
- item.status = ash::STATUS_ACTIVE;
+ item.status = STATUS_ACTIVE;
model_->Set(index, item);
ASSERT_EQ(internal::ShelfButton::STATE_ACTIVE, button->state());
- item.status = ash::STATUS_ATTENTION;
+ item.status = STATUS_ATTENTION;
model_->Set(index, item);
ASSERT_EQ(internal::ShelfButton::STATE_ATTENTION, button->state());
}
@@ -1160,9 +1297,8 @@ TEST_F(ShelfViewTest, RemovingItemClosesTooltip) {
EXPECT_FALSE(tooltip_manager->IsVisible());
// Change the shelf layout. This should not crash.
- ash::Shell::GetInstance()->SetShelfAlignment(
- ash::SHELF_ALIGNMENT_LEFT,
- ash::Shell::GetPrimaryRootWindow());
+ Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT,
+ Shell::GetPrimaryRootWindow());
}
// Changing the shelf alignment closes any open tooltip.
@@ -1181,9 +1317,8 @@ TEST_F(ShelfViewTest, ShelfAlignmentClosesTooltip) {
EXPECT_TRUE(tooltip_manager->IsVisible());
// Changing shelf alignment hides the tooltip.
- ash::Shell::GetInstance()->SetShelfAlignment(
- ash::SHELF_ALIGNMENT_LEFT,
- ash::Shell::GetPrimaryRootWindow());
+ Shell::GetInstance()->SetShelfAlignment(SHELF_ALIGNMENT_LEFT,
+ Shell::GetPrimaryRootWindow());
EXPECT_FALSE(tooltip_manager->IsVisible());
}
@@ -1339,29 +1474,23 @@ TEST_F(ShelfViewTest, ResizeDuringOverflowAddAnimation) {
// Checks the overflow bubble size when an item is ripped off and re-inserted.
TEST_F(ShelfViewTest, OverflowBubbleSize) {
- // Replace LauncherDelegate.
+ // Replace ShelfDelegate.
test::ShellTestApi test_api(Shell::GetInstance());
- test_api.SetLauncherDelegate(NULL);
- LauncherDelegate *delegate = new TestLauncherDelegateForShelfView(model_);
- test_api.SetLauncherDelegate(delegate);
+ test_api.SetShelfDelegate(NULL);
+ ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_);
+ test_api.SetShelfDelegate(delegate);
test::LauncherTestAPI(
- Launcher::ForPrimaryDisplay()).SetLauncherDelegate(delegate);
- test_api_->SetLauncherDelegate(delegate);
+ Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate);
+ test_api_->SetShelfDelegate(delegate);
- // Add buttons until overflow.
- int items_added = 0;
- while (!test_api_->IsOverflowButtonVisible()) {
- AddAppShortcut();
- ++items_added;
- ASSERT_LT(items_added, 10000);
- }
+ AddButtonsUntilOverflow();
// Show overflow bubble.
test_api_->ShowOverflowBubble();
ASSERT_TRUE(test_api_->overflow_bubble() &&
test_api_->overflow_bubble()->IsShowing());
- ash::test::ShelfViewTestAPI test_for_overflow_view(
+ ShelfViewTestAPI test_for_overflow_view(
test_api_->overflow_bubble()->shelf_view());
int ripped_index = test_for_overflow_view.GetLastVisibleIndex();
@@ -1369,9 +1498,9 @@ TEST_F(ShelfViewTest, OverflowBubbleSize) {
int item_width = test_for_overflow_view.GetButtonSize() +
test_for_overflow_view.GetButtonSpacing();
- aura::test::EventGenerator generator(ash::Shell::GetPrimaryRootWindow(),
+ aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
gfx::Point());
- ash::internal::ShelfButton* button =
+ internal::ShelfButton* button =
test_for_overflow_view.GetButton(ripped_index);
// Rip off the last visible item.
gfx::Point start_point = button->GetBoundsInScreen().CenterPoint();
@@ -1423,13 +1552,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsOfScrolledOverflowBubble) {
EXPECT_EQ(2, model_->item_count());
- // Add buttons until overflow.
- int items_added = 0;
- while (!test_api_->IsOverflowButtonVisible()) {
- AddAppShortcut();
- ++items_added;
- ASSERT_LT(items_added, 10000);
- }
+ AddButtonsUntilOverflow();
// Show overflow bubble.
test_api_->ShowOverflowBubble();
@@ -1451,14 +1574,14 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsOfScrolledOverflowBubble) {
ASSERT_TRUE(test_api_->overflow_bubble() &&
test_api_->overflow_bubble()->IsShowing());
- ash::test::ShelfViewTestAPI test_for_overflow_view(
+ ShelfViewTestAPI test_for_overflow_view(
test_api_->overflow_bubble()->shelf_view());
int first_index = test_for_overflow_view.GetFirstVisibleIndex();
int last_index = test_for_overflow_view.GetLastVisibleIndex();
- ash::internal::ShelfButton* first_button =
+ internal::ShelfButton* first_button =
test_for_overflow_view.GetButton(first_index);
- ash::internal::ShelfButton* last_button =
+ internal::ShelfButton* last_button =
test_for_overflow_view.GetButton(last_index);
gfx::Point first_point = first_button->GetBoundsInScreen().CenterPoint();
gfx::Point last_point = last_button->GetBoundsInScreen().CenterPoint();
@@ -1485,7 +1608,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) {
UpdateDisplay("800x600,800x600");
Launcher* secondary_launcher =
- Launcher::ForWindow(ash::Shell::GetAllRootWindows()[1]);
+ Launcher::ForWindow(Shell::GetAllRootWindows()[1]);
internal::ShelfView* shelf_view_for_secondary =
test::LauncherTestAPI(secondary_launcher).shelf_view();
@@ -1497,13 +1620,7 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) {
// Speeds up animation for test.
test_api_for_secondary.SetAnimationDuration(1);
- // Add buttons until overflow.
- int items_added = 0;
- while (!test_api_->IsOverflowButtonVisible()) {
- AddAppShortcut();
- ++items_added;
- ASSERT_LT(items_added, 10000);
- }
+ AddButtonsUntilOverflow();
// Test #1: Test drag insertion bounds of primary shelf.
// Show overflow bubble.
@@ -1511,10 +1628,10 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) {
ASSERT_TRUE(test_api_->overflow_bubble() &&
test_api_->overflow_bubble()->IsShowing());
- ash::test::ShelfViewTestAPI test_api_for_overflow_view(
+ ShelfViewTestAPI test_api_for_overflow_view(
test_api_->overflow_bubble()->shelf_view());
- ash::internal::ShelfButton* button = test_api_for_overflow_view.GetButton(
+ internal::ShelfButton* button = test_api_for_overflow_view.GetButton(
test_api_for_overflow_view.GetLastVisibleIndex());
// Checks that a point in shelf is contained in drag insert bounds.
@@ -1532,10 +1649,10 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) {
ASSERT_TRUE(test_api_for_secondary.overflow_bubble() &&
test_api_for_secondary.overflow_bubble()->IsShowing());
- ash::test::ShelfViewTestAPI test_api_for_overflow_view_of_secondary(
+ ShelfViewTestAPI test_api_for_overflow_view_of_secondary(
test_api_for_secondary.overflow_bubble()->shelf_view());
- ash::internal::ShelfButton* button_in_secondary =
+ internal::ShelfButton* button_in_secondary =
test_api_for_overflow_view_of_secondary.GetButton(
test_api_for_overflow_view_of_secondary.GetLastVisibleIndex());
@@ -1554,6 +1671,23 @@ TEST_F(ShelfViewTest, CheckDragInsertBoundsWithMultiMonitor) {
EXPECT_FALSE(drag_reinsert_bounds_in_secondary.Contains(point_in_shelf_view));
}
+// Checks various drag and drop operations from OverflowBubble to Shelf.
+TEST_F(ShelfViewTest, CheckDragAndDropFromOverflowBubbleToShelf) {
+ // Replace LauncherDelegate.
+ test::ShellTestApi test_api(Shell::GetInstance());
+ test_api.SetShelfDelegate(NULL);
+ ShelfDelegate *delegate = new TestShelfDelegateForShelfView(model_);
+ test_api.SetShelfDelegate(delegate);
+ test::LauncherTestAPI(
+ Launcher::ForPrimaryDisplay()).SetShelfDelegate(delegate);
+ test_api_->SetShelfDelegate(delegate);
+
+ AddButtonsUntilOverflow();
+
+ TestDraggingAnItemFromOverflowToShelf(false);
+ TestDraggingAnItemFromOverflowToShelf(true);
+}
+
class ShelfViewVisibleBoundsTest : public ShelfViewTest,
public testing::WithParamInterface<bool> {
public:
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 8dc737ab54..d483c07628 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -6,9 +6,9 @@
#include "ash/ash_switches.h"
#include "ash/focus_cycler.h"
-#include "ash/launcher/launcher_delegate.h"
#include "ash/root_window_controller.h"
#include "ash/session_state_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_navigator.h"
@@ -46,7 +46,6 @@ const int kDimAlpha = 128;
// The time to dim and un-dim.
const int kTimeToDimMs = 3000; // Slow in dimming.
const int kTimeToUnDimMs = 200; // Fast in activating.
-const int kTimeToSwitchBackgroundMs = 1000;
// Class used to slightly dim shelf items when maximized and visible.
class DimmerView : public views::View,
@@ -145,7 +144,7 @@ DimmerView::DimmerView(ash::ShelfWidget* shelf_widget,
// Make sure it is undimmed at the beginning and then fire off the dimming
// animation.
background_animator_.SetPaintsBackground(false,
- ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE);
+ ash::BACKGROUND_CHANGE_IMMEDIATE);
SetHovered(false);
}
@@ -161,8 +160,7 @@ void DimmerView::SetHovered(bool hovered) {
background_animator_.SetDuration(hovered ? kTimeToUnDimMs : kTimeToDimMs);
background_animator_.SetPaintsBackground(!hovered,
disable_dimming_animations_for_test_ ?
- ash::internal::BackgroundAnimator::CHANGE_IMMEDIATE :
- ash::internal::BackgroundAnimator::CHANGE_ANIMATE);
+ ash::BACKGROUND_CHANGE_IMMEDIATE : ash::BACKGROUND_CHANGE_ANIMATE);
}
void DimmerView::ForceUndimming(bool force) {
@@ -391,22 +389,55 @@ void ShelfWidget::DelegateView::OnPaintBackground(gfx::Canvas* canvas) {
SkBitmapOperations::ROTATION_90_CW,
SkBitmapOperations::ROTATION_270_CW,
SkBitmapOperations::ROTATION_180_CW));
-
- gfx::Rect black_rect =
- shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
- gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
- gfx::Rect(0, 0, kNumBlackPixels, height()),
- gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()),
- gfx::Rect(0, 0, width(), kNumBlackPixels));
-
+ const gfx::Rect dock_bounds(shelf_->shelf_layout_manager()->dock_bounds());
SkPaint paint;
paint.setAlpha(alpha_);
canvas->DrawImageInt(
launcher_background,
0, 0, launcher_background.width(), launcher_background.height(),
- 0, 0, width(), height(),
+ (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
+ dock_bounds.x() == 0 && dock_bounds.width() > 0) ?
+ dock_bounds.width() : 0, 0,
+ SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() ?
+ width() - dock_bounds.width() : width(), height(),
false,
paint);
+ if (SHELF_ALIGNMENT_BOTTOM == shelf_->GetAlignment() &&
+ dock_bounds.width() > 0) {
+ // The part of the shelf background that is in the corner below the docked
+ // windows close to the work area is an arched gradient that blends
+ // vertically oriented docked background and horizontal shelf.
+ gfx::ImageSkia launcher_corner =
+ *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_CORNER);
+ if (dock_bounds.x() == 0) {
+ launcher_corner = gfx::ImageSkiaOperations::CreateRotatedImage(
+ launcher_corner, SkBitmapOperations::ROTATION_90_CW);
+ }
+ canvas->DrawImageInt(
+ launcher_corner,
+ 0, 0, launcher_corner.width(), launcher_corner.height(),
+ dock_bounds.x() > 0 ? dock_bounds.x() : dock_bounds.width() - height(),
+ 0,
+ height(), height(),
+ false,
+ paint);
+ // The part of the shelf background that is just below the docked windows
+ // is drawn using the last (lowest) 1-pixel tall strip of the image asset.
+ // This avoids showing the border 3D shadow between the shelf and the dock.
+ canvas->DrawImageInt(
+ launcher_background,
+ 0, launcher_background.height() - 1, launcher_background.width(), 1,
+ dock_bounds.x() > 0 ? dock_bounds.x() + height() : 0, 0,
+ dock_bounds.width() - height(), height(),
+ false,
+ paint);
+ }
+ gfx::Rect black_rect =
+ shelf_->shelf_layout_manager()->SelectValueForShelfAlignment(
+ gfx::Rect(0, height() - kNumBlackPixels, width(), kNumBlackPixels),
+ gfx::Rect(0, 0, kNumBlackPixels, height()),
+ gfx::Rect(width() - kNumBlackPixels, 0, kNumBlackPixels, height()),
+ gfx::Rect(0, 0, width(), kNumBlackPixels));
canvas->FillRect(black_rect, SK_ColorBLACK);
}
@@ -521,12 +552,12 @@ ShelfWidget::~ShelfWidget() {
void ShelfWidget::SetPaintsBackground(
ShelfBackgroundType background_type,
- internal::BackgroundAnimator::ChangeType change_type) {
+ BackgroundAnimatorChangeType change_type) {
ui::Layer* opaque_background = delegate_view_->opaque_background();
float target_opacity =
(background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
- if (change_type != internal::BackgroundAnimator::CHANGE_IMMEDIATE) {
+ if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
opaque_background->GetAnimator()));
opaque_background_animation->SetTransitionDuration(
@@ -536,9 +567,11 @@ void ShelfWidget::SetPaintsBackground(
// TODO(mukai): use ui::Layer on both opaque_background and normal background
// retire background_animator_ at all. It would be simpler.
+ // See also DockedBackgroundWidget::SetPaintsBackground.
background_animator_.SetPaintsBackground(
background_type != SHELF_BACKGROUND_DEFAULT,
change_type);
+ delegate_view_->SchedulePaint();
}
ShelfBackgroundType ShelfWidget::GetBackgroundType() const {
@@ -605,12 +638,12 @@ void ShelfWidget::CreateLauncher() {
Shell* shell = Shell::GetInstance();
// This needs to be called before shelf_model().
- LauncherDelegate* launcher_delegate = shell->GetLauncherDelegate();
- if (!launcher_delegate)
+ ShelfDelegate* shelf_delegate = shell->GetShelfDelegate();
+ if (!shelf_delegate)
return; // Not ready to create Launcher
launcher_.reset(new Launcher(shell->shelf_model(),
- shell->GetLauncherDelegate(),
+ shell->GetShelfDelegate(),
this));
SetFocusCycler(shell->focus_cycler());
diff --git a/ash/shelf/shelf_widget.h b/ash/shelf/shelf_widget.h
index c7adb74255..83d4a8d701 100644
--- a/ash/shelf/shelf_widget.h
+++ b/ash/shelf/shelf_widget.h
@@ -43,12 +43,11 @@ class ASH_EXPORT ShelfWidget : public views::Widget,
ShelfAlignment GetAlignment() const;
// Sets the shelf's background type.
- void SetPaintsBackground(
- ShelfBackgroundType background_type,
- internal::BackgroundAnimator::ChangeType change_type);
+ void SetPaintsBackground(ShelfBackgroundType background_type,
+ BackgroundAnimatorChangeType change_type);
ShelfBackgroundType GetBackgroundType() const;
- // Causes shelf items to be slightly dimmed (eg when a window is maximized).
+ // Causes shelf items to be slightly dimmed (e.g. when a window is maximized).
void SetDimsShelf(bool dimming);
bool GetDimsShelf() const;
diff --git a/ash/shelf/shelf_window_watcher.cc b/ash/shelf/shelf_window_watcher.cc
new file mode 100644
index 0000000000..f177e48c29
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher.cc
@@ -0,0 +1,214 @@
+// Copyright 2013 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/shelf/shelf_window_watcher.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/activation_client.h"
+#include "ui/aura/window.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/gfx/screen.h"
+
+namespace {
+
+// Sets LauncherItem property by using the value of |details|.
+void SetLauncherItemDetailsForLauncherItem(
+ ash::LauncherItem* item,
+ const ash::LauncherItemDetails& details) {
+ item->type = details.type;
+ if (details.image_resource_id != ash::kInvalidImageResourceID) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ item->image = *rb.GetImageSkiaNamed(details.image_resource_id);
+ }
+}
+
+// Returns true if |window| has a LauncherItem added by ShelfWindowWatcher.
+bool HasLauncherItemForWindow(aura::Window* window) {
+ if (ash::GetLauncherItemDetailsForWindow(window) != NULL &&
+ ash::GetLauncherIDForWindow(window) != ash::kInvalidLauncherID)
+ return true;
+ return false;
+}
+
+} // namespace
+
+namespace ash {
+namespace internal {
+
+ShelfWindowWatcher::RootWindowObserver::RootWindowObserver(
+ ShelfWindowWatcher* window_watcher)
+ : window_watcher_(window_watcher) {
+}
+
+ShelfWindowWatcher::RootWindowObserver::~RootWindowObserver() {
+}
+
+void ShelfWindowWatcher::RootWindowObserver::OnWindowDestroying(
+ aura::Window* window) {
+ window_watcher_->OnRootWindowRemoved(window);
+}
+
+ShelfWindowWatcher::ShelfWindowWatcher(ShelfModel* model)
+ : model_(model),
+ root_window_observer_(this),
+ observed_windows_(this),
+ observed_root_windows_(&root_window_observer_),
+ observed_activation_clients_(this) {
+ // We can't assume all RootWindows have the same ActivationClient.
+ // Add a RootWindow and its ActivationClient to the observed list.
+ aura::Window::Windows root_windows = Shell::GetAllRootWindows();
+ for (aura::Window::Windows::const_iterator it = root_windows.begin();
+ it != root_windows.end(); ++it)
+ OnRootWindowAdded(*it);
+
+ Shell::GetScreen()->AddObserver(this);
+}
+
+ShelfWindowWatcher::~ShelfWindowWatcher() {
+ Shell::GetScreen()->RemoveObserver(this);
+}
+
+void ShelfWindowWatcher::AddLauncherItem(aura::Window* window) {
+ const LauncherItemDetails* item_details =
+ GetLauncherItemDetailsForWindow(window);
+ LauncherItem item;
+ item.status = ash::wm::IsActiveWindow(window) ? STATUS_ACTIVE: STATUS_RUNNING;
+ SetLauncherItemDetailsForLauncherItem(&item, *item_details);
+ SetLauncherIDForWindow(model_->next_id(), window);
+ // TODO(simonhong): Create LauncherItemDelegate for LauncherItem.
+ model_->Add(item);
+}
+
+void ShelfWindowWatcher::RemoveLauncherItem(aura::Window* window) {
+ model_->RemoveItemAt(model_->ItemIndexByID(GetLauncherIDForWindow(window)));
+ SetLauncherIDForWindow(kInvalidLauncherID, window);
+}
+
+void ShelfWindowWatcher::OnRootWindowAdded(aura::Window* root_window) {
+ // |observed_activation_clients_| can have the same ActivationClient multiple
+ // times - which would be handled by the |observed_activation_clients_|.
+ observed_activation_clients_.Add(
+ aura::client::GetActivationClient(root_window));
+ observed_root_windows_.Add(root_window);
+
+ aura::Window* default_container = Shell::GetContainer(
+ root_window,
+ kShellWindowId_DefaultContainer);
+ observed_windows_.Add(default_container);
+ for (size_t i = 0; i < default_container->children().size(); ++i)
+ observed_windows_.Add(default_container->children()[i]);
+}
+
+void ShelfWindowWatcher::OnRootWindowRemoved(aura::Window* root_window) {
+ observed_root_windows_.Remove(root_window);
+ observed_activation_clients_.Remove(
+ aura::client::GetActivationClient(root_window));
+}
+
+void ShelfWindowWatcher::UpdateLauncherItemStatus(aura::Window* window,
+ bool is_active) {
+ int index = GetLauncherItemIndexForWindow(window);
+ DCHECK_GE(index, 0);
+
+ LauncherItem item = model_->items()[index];
+ item.status = is_active ? STATUS_ACTIVE : STATUS_RUNNING;
+ model_->Set(index, item);
+}
+
+int ShelfWindowWatcher::GetLauncherItemIndexForWindow(
+ aura::Window* window) const {
+ return model_->ItemIndexByID(GetLauncherIDForWindow(window));
+}
+
+void ShelfWindowWatcher::OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) {
+ if (gained_active && HasLauncherItemForWindow(gained_active))
+ UpdateLauncherItemStatus(gained_active, true);
+ if (lost_active && HasLauncherItemForWindow(lost_active))
+ UpdateLauncherItemStatus(lost_active, false);
+}
+
+void ShelfWindowWatcher::OnWindowAdded(aura::Window* window) {
+ observed_windows_.Add(window);
+ // Add LauncherItem if |window| already has a LauncherItemDetails when it is
+ // created. Don't make a new LauncherItem for the re-parented |window| that
+ // already has a LauncherItem.
+ if (GetLauncherIDForWindow(window) == ash::kInvalidLauncherID &&
+ GetLauncherItemDetailsForWindow(window))
+ AddLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnWillRemoveWindow(aura::Window* window) {
+ // Remove a child window of default container and its item if it has.
+ if (observed_windows_.IsObserving(window))
+ observed_windows_.Remove(window);
+
+ if (HasLauncherItemForWindow(window))
+ RemoveLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnWindowDestroying(aura::Window* window) {
+ // Remove the default container.
+ if (observed_windows_.IsObserving(window))
+ observed_windows_.Remove(window);
+}
+
+void ShelfWindowWatcher::OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) {
+ if (key != kLauncherItemDetailsKey)
+ return;
+
+ if (GetLauncherItemDetailsForWindow(window) == NULL) {
+ // Removes LauncherItem for |window| when it has a LauncherItem.
+ if (reinterpret_cast<LauncherItemDetails*>(old) != NULL)
+ RemoveLauncherItem(window);
+ return;
+ }
+
+ // When LauncherItemDetails is changed, update LauncherItem.
+ if (HasLauncherItemForWindow(window)) {
+ int index = GetLauncherItemIndexForWindow(window);
+ DCHECK_GE(index, 0);
+ LauncherItem item = model_->items()[index];
+ const LauncherItemDetails* details =
+ GetLauncherItemDetailsForWindow(window);
+ SetLauncherItemDetailsForLauncherItem(&item, *details);
+ model_->Set(index, item);
+ return;
+ }
+
+ // Creates a new LauncherItem for |window|.
+ AddLauncherItem(window);
+}
+
+void ShelfWindowWatcher::OnDisplayBoundsChanged(const gfx::Display& display) {
+}
+
+void ShelfWindowWatcher::OnDisplayAdded(const gfx::Display& new_display) {
+ // Add a new RootWindow and its ActivationClient to observed list.
+ aura::Window* root_window = Shell::GetInstance()->display_controller()->
+ GetRootWindowForDisplayId(new_display.id());
+
+ // When the primary root window's display get removed, the existing root
+ // window is taken over by the new display and the observer is already set.
+ if (!observed_root_windows_.IsObserving(root_window))
+ OnRootWindowAdded(root_window);
+}
+
+void ShelfWindowWatcher::OnDisplayRemoved(const gfx::Display& old_display) {
+ // When this is called, RootWindow of |old_display| is already removed.
+ // Instead, we remove an observer from RootWindow and ActivationClient in the
+ // OnRootWindowDestroyed().
+ // Do nothing here.
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/shelf/shelf_window_watcher.h b/ash/shelf/shelf_window_watcher.h
new file mode 100644
index 0000000000..d63c6582ad
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher.h
@@ -0,0 +1,114 @@
+// Copyright 2013 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.
+
+#ifndef ASH_SHELF_SHELF_WINDOW_WATCHER_H_
+#define ASH_SHELF_SHELF_WINDOW_WATCHER_H_
+
+#include "ash/shelf/scoped_observer_with_duplicated_sources.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/scoped_observer.h"
+#include "ui/aura/client/activation_change_observer.h"
+#include "ui/aura/window_observer.h"
+#include "ui/gfx/display_observer.h"
+
+namespace aura {
+
+class Window;
+
+namespace client {
+class ActivationClient;
+}
+
+} // namespace aura
+
+namespace ash {
+
+class ShelfModel;
+
+namespace internal {
+
+// ShelfWindowWatcher creates and handles a LauncherItem for windows that have
+// a LauncherItemDetails property in the default container.
+class ShelfWindowWatcher : public aura::client::ActivationChangeObserver,
+ public aura::WindowObserver,
+ public gfx::DisplayObserver {
+ public:
+ ShelfWindowWatcher(ShelfModel* model);
+ virtual ~ShelfWindowWatcher();
+
+ private:
+ class RootWindowObserver : public aura::WindowObserver {
+ public:
+ explicit RootWindowObserver(ShelfWindowWatcher* window_watcher);
+ virtual ~RootWindowObserver();
+
+ private:
+ // aura::WindowObserver overrides:
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+
+ // Owned by Shell.
+ ShelfWindowWatcher* window_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(RootWindowObserver);
+ };
+
+ // Creates a LauncherItem for |window| that has LauncherItemDetails.
+ void AddLauncherItem(aura::Window* window);
+
+ // Removes a LauncherItem for |window|.
+ void RemoveLauncherItem(aura::Window* window);
+
+ // Adds observer to default container and ActivationClient of |root_window|.
+ void OnRootWindowAdded(aura::Window* root_window);
+
+ // Removes observer from ActivationClient of |root_window|.
+ void OnRootWindowRemoved(aura::Window* root_window);
+
+ // Updates the status of LauncherItem for |window|.
+ void UpdateLauncherItemStatus(aura::Window* window, bool is_active);
+
+ // Returns the index of LauncherItem associated with |window|.
+ int GetLauncherItemIndexForWindow(aura::Window* window) const;
+
+ // aura::client::ActivationChangeObserver overrides:
+ virtual void OnWindowActivated(aura::Window* gained_active,
+ aura::Window* lost_active) OVERRIDE;
+
+ // aura::WindowObserver overrides:
+ virtual void OnWindowAdded(aura::Window* window) OVERRIDE;
+ virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
+ virtual void OnWindowPropertyChanged(aura::Window* window,
+ const void* key,
+ intptr_t old) OVERRIDE;
+
+ // gfx::DisplayObserver overrides:
+ virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
+ virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE;
+ virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE;
+
+ // Owned by Shell.
+ ShelfModel* model_;
+
+ RootWindowObserver root_window_observer_;
+
+ // Holds all observed windows.
+ ScopedObserver<aura::Window, aura::WindowObserver> observed_windows_;
+
+ // Holds all observed root windows.
+ ScopedObserver<aura::Window, aura::WindowObserver> observed_root_windows_;
+
+ // Holds all observed activation clients.
+ ScopedObserverWithDuplicatedSources<aura::client::ActivationClient,
+ aura::client::ActivationChangeObserver> observed_activation_clients_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcher);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_SHELF_SHELF_WINDOW_WATCHER_H_
diff --git a/ash/shelf/shelf_window_watcher_unittest.cc b/ash/shelf/shelf_window_watcher_unittest.cc
new file mode 100644
index 0000000000..1217aae2e5
--- /dev/null
+++ b/ash/shelf/shelf_window_watcher_unittest.cc
@@ -0,0 +1,175 @@
+// Copyright 2013 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/shelf/shelf_window_watcher.h"
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/shell_test_api.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace internal {
+
+class ShelfWindowWatcherTest : public test::AshTestBase {
+ public:
+ ShelfWindowWatcherTest() : model_(NULL) {}
+ virtual ~ShelfWindowWatcherTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ test::AshTestBase::SetUp();
+ model_ = test::ShellTestApi(Shell::GetInstance()).shelf_model();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ model_ = NULL;
+ test::AshTestBase::TearDown();
+ }
+
+ ash::LauncherID CreateLauncherItem(aura::Window* window) {
+ LauncherID id = model_->next_id();
+ ash::LauncherItemDetails item_details;
+ item_details.type = TYPE_PLATFORM_APP;
+ SetLauncherItemDetailsForWindow(window, item_details);
+ return id;
+ }
+
+ void UpdateLauncherItem(aura::Window* window) {
+ }
+
+ protected:
+ ShelfModel* model_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ShelfWindowWatcherTest);
+};
+
+TEST_F(ShelfWindowWatcherTest, CreateAndRemoveLauncherItem) {
+ // ShelfModel only has an APP_LIST item.
+ EXPECT_EQ(1, model_->item_count());
+
+ scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
+ scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));
+
+ // Create a LauncherItem for w1.
+ LauncherID id_w1 = CreateLauncherItem(w1.get());
+ EXPECT_EQ(2, model_->item_count());
+
+ int index_w1 = model_->ItemIndexByID(id_w1);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+
+ // Create a LauncherItem for w2.
+ LauncherID id_w2 = CreateLauncherItem(w2.get());
+ EXPECT_EQ(3, model_->item_count());
+
+ int index_w2 = model_->ItemIndexByID(id_w2);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);
+
+ // LauncherItem is removed when assoicated window is destroyed.
+ ClearLauncherItemDetailsForWindow(w1.get());
+ EXPECT_EQ(2, model_->item_count());
+ ClearLauncherItemDetailsForWindow(w2.get());
+ EXPECT_EQ(1, model_->item_count());
+ // Clears twice doesn't do anything.
+ ClearLauncherItemDetailsForWindow(w2.get());
+ EXPECT_EQ(1, model_->item_count());
+
+}
+
+TEST_F(ShelfWindowWatcherTest, ActivateWindow) {
+ // ShelfModel only have APP_LIST item.
+ EXPECT_EQ(1, model_->item_count());
+ scoped_ptr<aura::Window> w1(CreateTestWindowInShellWithId(0));
+ scoped_ptr<aura::Window> w2(CreateTestWindowInShellWithId(0));
+
+ // Create a LauncherItem for w1.
+ LauncherID id_w1 = CreateLauncherItem(w1.get());
+ EXPECT_EQ(2, model_->item_count());
+ int index_w1 = model_->ItemIndexByID(id_w1);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+
+ // Create a LauncherItem for w2.
+ LauncherID id_w2 = CreateLauncherItem(w2.get());
+ EXPECT_EQ(3, model_->item_count());
+ int index_w2 = model_->ItemIndexByID(id_w2);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w2].status);
+
+ // LauncherItem for w1 is active when w1 is activated.
+ wm::ActivateWindow(w1.get());
+ EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w1].status);
+
+ // LauncherItem for w2 is active state when w2 is activated.
+ wm::ActivateWindow(w2.get());
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index_w1].status);
+ EXPECT_EQ(STATUS_ACTIVE, model_->items()[index_w2].status);
+}
+
+TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
+ // ShelfModel only has an APP_LIST item.
+ EXPECT_EQ(1, model_->item_count());
+
+ scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
+
+ // Create a LauncherItem for |window|.
+ LauncherID id = CreateLauncherItem(window.get());
+ EXPECT_EQ(2, model_->item_count());
+
+ int index = model_->ItemIndexByID(id);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);
+
+ // Update LauncherItem for |window|.
+ LauncherItemDetails details;
+ details.type = TYPE_PLATFORM_APP;
+
+ SetLauncherItemDetailsForWindow(window.get(), details);
+ // No new item is created after updating a launcher item.
+ EXPECT_EQ(2, model_->item_count());
+ // index and id are not changed after updating a launcher item.
+ EXPECT_EQ(index, model_->ItemIndexByID(id));
+ EXPECT_EQ(id, model_->items()[index].id);
+}
+
+TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
+ // ShelfModel only has an APP_LIST item.
+ EXPECT_EQ(1, model_->item_count());
+
+ scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
+ wm::WindowState* window_state = wm::GetWindowState(window.get());
+
+ // Create a LauncherItem for |window|.
+ LauncherID id = CreateLauncherItem(window.get());
+ EXPECT_EQ(2, model_->item_count());
+
+ int index = model_->ItemIndexByID(id);
+ EXPECT_EQ(STATUS_RUNNING, model_->items()[index].status);
+
+ // Maximize window |window|.
+ EXPECT_FALSE(window_state->IsMaximized());
+ window_state->Maximize();
+ EXPECT_TRUE(window_state->IsMaximized());
+ // No new item is created after maximizing a window |window|.
+ EXPECT_EQ(2, model_->item_count());
+ // index and id are not changed after maximizing a window |window|.
+ EXPECT_EQ(index, model_->ItemIndexByID(id));
+ EXPECT_EQ(id, model_->items()[index].id);
+
+ // Restore window |window|.
+ window_state->Restore();
+ EXPECT_FALSE(window_state->IsMaximized());
+ // No new item is created after restoring a window |window|.
+ EXPECT_EQ(2, model_->item_count());
+ // index and id are not changed after maximizing a window |window|.
+ EXPECT_EQ(index, model_->ItemIndexByID(id));
+ EXPECT_EQ(id, model_->items()[index].id);
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/shell.cc b/ash/shell.cc
index d5428cff55..4cd9fd9932 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -30,9 +30,6 @@
#include "ash/high_contrast/high_contrast_controller.h"
#include "ash/host/root_window_host_factory.h"
#include "ash/keyboard_uma_event_filter.h"
-#include "ash/launcher/launcher_delegate.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
#include "ash/magnifier/magnification_controller.h"
#include "ash/magnifier/partial_magnification_controller.h"
#include "ash/media_delegate.h"
@@ -41,9 +38,13 @@
#include "ash/screen_ash.h"
#include "ash/session_state_delegate.h"
#include "ash/shelf/app_list_shelf_item_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_widget.h"
+#include "ash/shelf/shelf_window_watcher.h"
#include "ash/shell_delegate.h"
#include "ash/shell_factory.h"
#include "ash/shell_window_ids.h"
@@ -91,6 +92,7 @@
#include "ui/base/ui_base_switches.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
+#include "ui/events/event_target_iterator.h"
#include "ui/gfx/display.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/screen.h"
@@ -496,29 +498,29 @@ SystemTray* Shell::GetPrimarySystemTray() {
return GetPrimaryRootWindowController()->GetSystemTray();
}
-LauncherDelegate* Shell::GetLauncherDelegate() {
- if (!launcher_delegate_) {
+ShelfDelegate* Shell::GetShelfDelegate() {
+ if (!shelf_delegate_) {
shelf_model_.reset(new ShelfModel);
- // Creates LauncherItemDelegateManager before LauncherDelegate.
- launcher_item_delegate_manager_.reset(
- new LauncherItemDelegateManager(shelf_model_.get()));
+ // Creates ShelfItemDelegateManager before ShelfDelegate.
+ shelf_item_delegate_manager_.reset(
+ new ShelfItemDelegateManager(shelf_model_.get()));
- launcher_delegate_.reset(
- delegate_->CreateLauncherDelegate(shelf_model_.get()));
- scoped_ptr<LauncherItemDelegate> controller(
+ shelf_delegate_.reset(delegate_->CreateShelfDelegate(shelf_model_.get()));
+ scoped_ptr<ShelfItemDelegate> controller(
new internal::AppListShelfItemDelegate);
// Finding the shelf model's location of the app list and setting its
- // LauncherItemDelegate.
+ // ShelfItemDelegate.
int app_list_index = shelf_model_->GetItemIndexForType(TYPE_APP_LIST);
DCHECK_GE(app_list_index, 0);
LauncherID app_list_id = shelf_model_->items()[app_list_index].id;
DCHECK(app_list_id);
- launcher_item_delegate_manager_->SetLauncherItemDelegate(
- app_list_id,
- controller.Pass());
+ shelf_item_delegate_manager_->SetShelfItemDelegate(app_list_id,
+ controller.Pass());
+ shelf_window_watcher_.reset(
+ new internal::ShelfWindowWatcher(shelf_model_.get()));
}
- return launcher_delegate_.get();
+ return shelf_delegate_.get();
}
void Shell::SetTouchHudProjectionEnabled(bool enabled) {
@@ -629,42 +631,57 @@ Shell::~Shell() {
// Drag-and-drop must be canceled prior to close all windows.
drag_drop_controller_.reset();
+ // Controllers who have WindowObserver added must be deleted
+ // before |display_controller_| is deleted.
+
+#if defined(OS_CHROMEOS)
+ // VideoActivityNotifier must be deleted before |video_detector_| is
+ // deleted because it's observing video activity through
+ // VideoDetectorObserver interface.
+ video_activity_notifier_.reset();
+#endif // defined(OS_CHROMEOS)
+ video_detector_.reset();
+
+ shadow_controller_.reset();
+ resize_shadow_controller_.reset();
+
+ window_cycle_controller_.reset();
+ mru_window_tracker_.reset();
+
+ // |shelf_window_watcher_| has a weak pointer to |shelf_Model_|
+ // and has window observers.
+ shelf_window_watcher_.reset();
+
// Destroy all child windows including widgets.
display_controller_->CloseChildWindows();
display_controller_->CloseNonDesktopDisplay();
+ // Chrome implementation of shelf delegate depends on FocusClient,
+ // so must be deleted before |focus_client_|.
+ shelf_delegate_.reset();
+ focus_client_.reset();
+
// Destroy SystemTrayNotifier after destroying SystemTray as TrayItems
// needs to remove observers from it.
system_tray_notifier_.reset();
-#if defined(OS_CHROMEOS)
- // Destroy VideoActivityNotifier before destroying VideoDetector.
- video_activity_notifier_.reset();
-#endif // defined(OS_CHROMEOS)
-
// These need a valid Shell instance to clean up properly, so explicitly
// delete them before invalidating the instance.
// Alphabetical. TODO(oshima): sort.
magnification_controller_.reset();
partial_magnification_controller_.reset();
- resize_shadow_controller_.reset();
- shadow_controller_.reset();
tooltip_controller_.reset();
event_client_.reset();
- window_cycle_controller_.reset();
nested_dispatcher_controller_.reset();
user_action_client_.reset();
visibility_controller_.reset();
- launcher_delegate_.reset();
- // |launcher_item_delegate_manager_| observes |shelf_model_|. It must be
+ // |shelf_item_delegate_manager_| observes |shelf_model_|. It must be
// destroyed before |shelf_model_| is destroyed.
- launcher_item_delegate_manager_.reset();
+ shelf_item_delegate_manager_.reset();
shelf_model_.reset();
- video_detector_.reset();
power_button_controller_.reset();
lock_state_controller_.reset();
- mru_window_tracker_.reset();
resolution_notification_controller_.reset();
desktop_background_controller_.reset();
@@ -996,6 +1013,15 @@ ui::EventTarget* Shell::GetParentTarget() {
return aura::Env::GetInstance();
}
+scoped_ptr<ui::EventTargetIterator> Shell::GetChildIterator() const {
+ return scoped_ptr<ui::EventTargetIterator>();
+}
+
+ui::EventTargeter* Shell::GetEventTargeter() {
+ NOTREACHED();
+ return NULL;
+}
+
void Shell::OnEvent(ui::Event* event) {
}
diff --git a/ash/shell.h b/ash/shell.h
index 1c4e779ac4..5c49f6b8bb 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -85,9 +85,7 @@ class DisplayController;
class FirstRunHelper;
class HighContrastController;
class Launcher;
-class LauncherDelegate;
-class LauncherItemDelegate;
-class LauncherItemDelegateManager;
+class ShelfDelegate;
class LockStateController;
class MagnificationController;
class MediaDelegate;
@@ -99,6 +97,7 @@ class PowerButtonController;
class RootWindowHostFactory;
class ScreenAsh;
class SessionStateDelegate;
+class ShelfItemDelegateManager;
class ShelfModel;
class ShellDelegate;
class ShellObserver;
@@ -137,6 +136,7 @@ class ResolutionNotificationController;
class RootWindowController;
class ScopedTargetRootWindow;
class ScreenPositionController;
+class ShelfWindowWatcher;
class SlowAnimationEventFilter;
class StatusAreaWidget;
class SystemGestureEventFilter;
@@ -408,8 +408,8 @@ class ASH_EXPORT Shell
return activation_client_;
}
- LauncherItemDelegateManager* launcher_item_delegate_manager() {
- return launcher_item_delegate_manager_.get();
+ ShelfItemDelegateManager* shelf_item_delegate_manager() {
+ return shelf_item_delegate_manager_.get();
}
ScreenAsh* screen() { return screen_; }
@@ -512,7 +512,7 @@ class ASH_EXPORT Shell
}
// Returns the launcher delegate, creating if necesary.
- LauncherDelegate* GetLauncherDelegate();
+ ShelfDelegate* GetShelfDelegate();
void SetTouchHudProjectionEnabled(bool enabled);
@@ -559,6 +559,8 @@ class ASH_EXPORT Shell
// Overridden from ui::EventTarget:
virtual bool CanAcceptEvent(const ui::Event& event) OVERRIDE;
virtual EventTarget* GetParentTarget() OVERRIDE;
+ virtual scoped_ptr<ui::EventTargetIterator> GetChildIterator() const OVERRIDE;
+ virtual ui::EventTargeter* GetEventTargeter() OVERRIDE;
virtual void OnEvent(ui::Event* event) OVERRIDE;
// Overridden from aura::client::ActivationChangeObserver:
@@ -598,8 +600,9 @@ class ASH_EXPORT Shell
scoped_ptr<AccessibilityDelegate> accessibility_delegate_;
scoped_ptr<NewWindowDelegate> new_window_delegate_;
scoped_ptr<MediaDelegate> media_delegate_;
- scoped_ptr<LauncherDelegate> launcher_delegate_;
- scoped_ptr<LauncherItemDelegateManager> launcher_item_delegate_manager_;
+ scoped_ptr<ShelfDelegate> shelf_delegate_;
+ scoped_ptr<ShelfItemDelegateManager> shelf_item_delegate_manager_;
+ scoped_ptr<internal::ShelfWindowWatcher> shelf_window_watcher_;
scoped_ptr<ShelfModel> shelf_model_;
scoped_ptr<WindowPositioner> window_positioner_;
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc
index 07f888eb7b..1359826eeb 100644
--- a/ash/shell/app_list.cc
+++ b/ash/shell/app_list.cc
@@ -23,6 +23,7 @@
#include "ui/app_list/app_list_view_delegate.h"
#include "ui/app_list/search_box_model.h"
#include "ui/app_list/search_result.h"
+#include "ui/app_list/speech_ui_model.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/image/image_skia.h"
@@ -251,6 +252,10 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate {
return NULL;
}
+ virtual app_list::SpeechUIModel* GetSpeechUI() OVERRIDE {
+ return &speech_ui_;
+ }
+
virtual void GetShortcutPathForApp(
const std::string& app_id,
const base::Callback<void(const base::FilePath&)>& callback) OVERRIDE {
@@ -338,6 +343,7 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate {
}
scoped_ptr<app_list::AppListModel> model_;
+ app_list::SpeechUIModel speech_ui_;
Users users_;
DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate);
diff --git a/ash/shell/launcher_delegate_impl.cc b/ash/shell/launcher_delegate_impl.cc
deleted file mode 100644
index 9eba738f5a..0000000000
--- a/ash/shell/launcher_delegate_impl.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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/shell/launcher_delegate_impl.h"
-
-#include "ash/shell.h"
-#include "ash/shell/toplevel_window.h"
-#include "ash/shell/window_watcher.h"
-#include "ash/wm/window_util.h"
-#include "base/strings/string_util.h"
-#include "grit/ash_resources.h"
-
-namespace ash {
-namespace shell {
-
-LauncherDelegateImpl::LauncherDelegateImpl(WindowWatcher* watcher)
- : watcher_(watcher) {
-}
-
-LauncherDelegateImpl::~LauncherDelegateImpl() {
-}
-
-void LauncherDelegateImpl::OnLauncherCreated(Launcher* launcher) {
-}
-
-void LauncherDelegateImpl::OnLauncherDestroyed(Launcher* launcher) {
-}
-
-LauncherID LauncherDelegateImpl::GetLauncherIDForAppID(
- const std::string& app_id) {
- return 0;
-}
-
-const std::string& LauncherDelegateImpl::GetAppIDForLauncherID(LauncherID id) {
- return EmptyString();
-}
-
-void LauncherDelegateImpl::PinAppWithID(const std::string& app_id) {
-}
-
-bool LauncherDelegateImpl::IsAppPinned(const std::string& app_id) {
- return false;
-}
-
-bool LauncherDelegateImpl::CanPin() const {
- return false;
-}
-
-void LauncherDelegateImpl::UnpinAppWithID(const std::string& app_id) {
-}
-
-} // namespace shell
-} // namespace ash
diff --git a/ash/shell/shelf_delegate_impl.cc b/ash/shell/shelf_delegate_impl.cc
new file mode 100644
index 0000000000..0112009c37
--- /dev/null
+++ b/ash/shell/shelf_delegate_impl.cc
@@ -0,0 +1,53 @@
+// Copyright 2013 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/shell/shelf_delegate_impl.h"
+
+#include "ash/shell.h"
+#include "ash/shell/toplevel_window.h"
+#include "ash/shell/window_watcher.h"
+#include "ash/wm/window_util.h"
+#include "base/strings/string_util.h"
+#include "grit/ash_resources.h"
+
+namespace ash {
+namespace shell {
+
+ShelfDelegateImpl::ShelfDelegateImpl(WindowWatcher* watcher)
+ : watcher_(watcher) {
+}
+
+ShelfDelegateImpl::~ShelfDelegateImpl() {
+}
+
+void ShelfDelegateImpl::OnLauncherCreated(Launcher* launcher) {
+}
+
+void ShelfDelegateImpl::OnLauncherDestroyed(Launcher* launcher) {
+}
+
+LauncherID ShelfDelegateImpl::GetLauncherIDForAppID(const std::string& app_id) {
+ return 0;
+}
+
+const std::string& ShelfDelegateImpl::GetAppIDForLauncherID(LauncherID id) {
+ return base::EmptyString();
+}
+
+void ShelfDelegateImpl::PinAppWithID(const std::string& app_id) {
+}
+
+bool ShelfDelegateImpl::IsAppPinned(const std::string& app_id) {
+ return false;
+}
+
+bool ShelfDelegateImpl::CanPin() const {
+ return false;
+}
+
+void ShelfDelegateImpl::UnpinAppWithID(const std::string& app_id) {
+}
+
+} // namespace shell
+} // namespace ash
diff --git a/ash/shell/launcher_delegate_impl.h b/ash/shell/shelf_delegate_impl.h
index c7b54ed2f5..ed6dc6d7d3 100644
--- a/ash/shell/launcher_delegate_impl.h
+++ b/ash/shell/shelf_delegate_impl.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
-#ifndef ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
-#define ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
+#ifndef ASH_SHELL_SHELF_DELEGATE_IMPL_H_
+#define ASH_SHELL_SHELF_DELEGATE_IMPL_H_
-#include "ash/launcher/launcher_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
#include "base/compiler_specific.h"
namespace aura {
@@ -17,14 +17,14 @@ namespace shell {
class WindowWatcher;
-class LauncherDelegateImpl : public ash::LauncherDelegate {
+class ShelfDelegateImpl : public ShelfDelegate {
public:
- explicit LauncherDelegateImpl(WindowWatcher* watcher);
- virtual ~LauncherDelegateImpl();
+ explicit ShelfDelegateImpl(WindowWatcher* watcher);
+ virtual ~ShelfDelegateImpl();
void set_watcher(WindowWatcher* watcher) { watcher_ = watcher; }
- // LauncherDelegate overrides:
+ // ShelfDelegate overrides:
virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
@@ -38,10 +38,10 @@ class LauncherDelegateImpl : public ash::LauncherDelegate {
// Used to update Launcher. Owned by main.
WindowWatcher* watcher_;
- DISALLOW_COPY_AND_ASSIGN(LauncherDelegateImpl);
+ DISALLOW_COPY_AND_ASSIGN(ShelfDelegateImpl);
};
} // namespace shell
} // namespace ash
-#endif // ASH_SHELL_LAUNCHER_DELEGATE_IMPL_H_
+#endif // ASH_SHELL_SHELF_DELEGATE_IMPL_H_
diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc
index acc8e3d0df..5dfe7b2a42 100644
--- a/ash/shell/shell_delegate_impl.cc
+++ b/ash/shell/shell_delegate_impl.cc
@@ -16,7 +16,7 @@
#include "ash/shell/context_menu.h"
#include "ash/shell/example_factory.h"
#include "ash/shell/keyboard_controller_proxy_stub.h"
-#include "ash/shell/launcher_delegate_impl.h"
+#include "ash/shell/shelf_delegate_impl.h"
#include "ash/shell/toplevel_window.h"
#include "ash/shell_window_ids.h"
#include "ash/system/tray/default_system_tray_delegate.h"
@@ -69,7 +69,7 @@ class MediaDelegateImpl : public MediaDelegate {
ShellDelegateImpl::ShellDelegateImpl()
: watcher_(NULL),
- launcher_delegate_(NULL),
+ shelf_delegate_(NULL),
browser_context_(NULL) {
}
@@ -78,8 +78,8 @@ ShellDelegateImpl::~ShellDelegateImpl() {
void ShellDelegateImpl::SetWatcher(WindowWatcher* watcher) {
watcher_ = watcher;
- if (launcher_delegate_)
- launcher_delegate_->set_watcher(watcher);
+ if (shelf_delegate_)
+ shelf_delegate_->set_watcher(watcher);
}
bool ShellDelegateImpl::IsFirstRunAfterBoot() const {
@@ -121,9 +121,9 @@ app_list::AppListViewDelegate* ShellDelegateImpl::CreateAppListViewDelegate() {
return ash::shell::CreateAppListViewDelegate();
}
-LauncherDelegate* ShellDelegateImpl::CreateLauncherDelegate(ShelfModel* model) {
- launcher_delegate_ = new LauncherDelegateImpl(watcher_);
- return launcher_delegate_;
+ShelfDelegate* ShellDelegateImpl::CreateShelfDelegate(ShelfModel* model) {
+ shelf_delegate_ = new ShelfDelegateImpl(watcher_);
+ return shelf_delegate_;
}
ash::SystemTrayDelegate* ShellDelegateImpl::CreateSystemTrayDelegate() {
diff --git a/ash/shell/shell_delegate_impl.h b/ash/shell/shell_delegate_impl.h
index 500bef2dc4..ecc7ee6eb5 100644
--- a/ash/shell/shell_delegate_impl.h
+++ b/ash/shell/shell_delegate_impl.h
@@ -21,7 +21,7 @@ class KeyboardControllerProxy;
namespace ash {
namespace shell {
-class LauncherDelegateImpl;
+class ShelfDelegateImpl;
class WindowWatcher;
class ShellDelegateImpl : public ash::ShellDelegate {
@@ -45,7 +45,7 @@ class ShellDelegateImpl : public ash::ShellDelegate {
CreateKeyboardControllerProxy() OVERRIDE;
virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE;
virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
- virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE;
+ virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE;
virtual ash::SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE;
virtual ash::UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE;
virtual ash::CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE;
@@ -64,7 +64,7 @@ class ShellDelegateImpl : public ash::ShellDelegate {
// Used to update Launcher. Owned by main.
WindowWatcher* watcher_;
- LauncherDelegateImpl* launcher_delegate_;
+ ShelfDelegateImpl* shelf_delegate_;
content::BrowserContext* browser_context_;
DISALLOW_COPY_AND_ASSIGN(ShellDelegateImpl);
diff --git a/ash/shell/window_type_launcher.cc b/ash/shell/window_type_launcher.cc
index e701e25299..f90c4f3d1c 100644
--- a/ash/shell/window_type_launcher.cc
+++ b/ash/shell/window_type_launcher.cc
@@ -344,7 +344,8 @@ void WindowTypeLauncher::ButtonPressed(views::Button* sender,
ASCIIToUTF16("Notification message body."),
gfx::Image(),
ASCIIToUTF16("www.testshell.org"),
- message_center::NotifierId(),
+ message_center::NotifierId(
+ message_center::NotifierId::APPLICATION, "test-id"),
message_center::RichNotificationData(),
NULL /* delegate */));
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index 761a9cc0c9..988985a5e4 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -6,12 +6,12 @@
#include "ash/display/display_controller.h"
#include "ash/launcher/launcher.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "ash/shelf/shelf_model.h"
#include "ash/shelf/shelf_util.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
-#include "ash/shell/window_watcher_launcher_item_delegate.h"
+#include "ash/shell/window_watcher_shelf_item_delegate.h"
#include "ash/shell_window_ids.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
@@ -117,11 +117,11 @@ void WindowWatcher::OnWindowAdded(aura::Window* new_window) {
model->Add(item);
- ash::LauncherItemDelegateManager* manager =
- ash::Shell::GetInstance()->launcher_item_delegate_manager();
- scoped_ptr<LauncherItemDelegate> delegate(
- new WindowWatcherLauncherItemDelegate(id, this));
- manager->SetLauncherItemDelegate(id, delegate.Pass());
+ ShelfItemDelegateManager* manager =
+ Shell::GetInstance()->shelf_item_delegate_manager();
+ scoped_ptr<ShelfItemDelegate> delegate(
+ new WindowWatcherShelfItemDelegate(id, this));
+ manager->SetShelfItemDelegate(id, delegate.Pass());
SetLauncherIDForWindow(id, new_window);
}
diff --git a/ash/shell/window_watcher_launcher_item_delegate.cc b/ash/shell/window_watcher_launcher_item_delegate.cc
deleted file mode 100644
index 76e39fd3f9..0000000000
--- a/ash/shell/window_watcher_launcher_item_delegate.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2013 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/shell/window_watcher_launcher_item_delegate.h"
-
-#include "ash/shell/window_watcher.h"
-#include "ash/wm/window_util.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace shell {
-
-WindowWatcherLauncherItemDelegate::WindowWatcherLauncherItemDelegate(
- ash::LauncherID id,
- ash::shell::WindowWatcher* watcher)
- : id_(id),
- watcher_(watcher) {
- DCHECK_GT(id_, 0);
- DCHECK(watcher_);
-}
-
-WindowWatcherLauncherItemDelegate::~WindowWatcherLauncherItemDelegate() {
-}
-
-bool WindowWatcherLauncherItemDelegate::ItemSelected(const ui::Event& event) {
- aura::Window* window = watcher_->GetWindowByID(id_);
- if (window->type() == aura::client::WINDOW_TYPE_PANEL)
- ash::wm::MoveWindowToEventRoot(window, event);
- window->Show();
- ash::wm::ActivateWindow(window);
- return false;
-}
-
-base::string16 WindowWatcherLauncherItemDelegate::GetTitle() {
- return watcher_->GetWindowByID(id_)->title();
-}
-
-ui::MenuModel* WindowWatcherLauncherItemDelegate::CreateContextMenu(
- aura::Window* root_window) {
- return NULL;
-}
-
-ash::LauncherMenuModel*
-WindowWatcherLauncherItemDelegate::CreateApplicationMenu(
- int event_flags) {
- return NULL;
-}
-
-bool WindowWatcherLauncherItemDelegate::IsDraggable() {
- return true;
-}
-
-bool WindowWatcherLauncherItemDelegate::ShouldShowTooltip() {
- return true;
-}
-
-} // namespace shell
-} // namespace ash
diff --git a/ash/shell/window_watcher_launcher_item_delegate.h b/ash/shell/window_watcher_launcher_item_delegate.h
deleted file mode 100644
index 280f3b435e..0000000000
--- a/ash/shell/window_watcher_launcher_item_delegate.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
-#define ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-
-namespace ash {
-namespace shell {
-
-class WindowWatcher;
-
-// LauncherItemDelegate implementation used by WindowWatcher.
-class WindowWatcherLauncherItemDelegate : public ash::LauncherItemDelegate {
- public:
- WindowWatcherLauncherItemDelegate(ash::LauncherID id,
- ash::shell::WindowWatcher* watcher);
- virtual ~WindowWatcherLauncherItemDelegate();
-
- // ash::LauncherItemDelegate overrides:
- virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
- virtual base::string16 GetTitle() OVERRIDE;
- virtual ui::MenuModel* CreateContextMenu(
- aura::Window* root_window) OVERRIDE;
- virtual ash::LauncherMenuModel* CreateApplicationMenu(
- int event_flags) OVERRIDE;
- virtual bool IsDraggable() OVERRIDE;
- virtual bool ShouldShowTooltip() OVERRIDE;
-
- private:
- ash::LauncherID id_;
- ash::shell::WindowWatcher* watcher_;
-
- DISALLOW_COPY_AND_ASSIGN(WindowWatcherLauncherItemDelegate);
-};
-
-} // namespace shell
-} // namespace ash
-
-#endif // ASH_SHELL_WINDOW_WATCHER_LAUNCHER_ITEM_DELEGATE_
diff --git a/ash/shell/window_watcher_shelf_item_delegate.cc b/ash/shell/window_watcher_shelf_item_delegate.cc
new file mode 100644
index 0000000000..ecb4308c94
--- /dev/null
+++ b/ash/shell/window_watcher_shelf_item_delegate.cc
@@ -0,0 +1,57 @@
+// Copyright 2013 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/shell/window_watcher_shelf_item_delegate.h"
+
+#include "ash/shell/window_watcher.h"
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace shell {
+
+WindowWatcherShelfItemDelegate::WindowWatcherShelfItemDelegate(
+ LauncherID id,
+ WindowWatcher* watcher)
+ : id_(id), watcher_(watcher) {
+ DCHECK_GT(id_, 0);
+ DCHECK(watcher_);
+}
+
+WindowWatcherShelfItemDelegate::~WindowWatcherShelfItemDelegate() {
+}
+
+bool WindowWatcherShelfItemDelegate::ItemSelected(const ui::Event& event) {
+ aura::Window* window = watcher_->GetWindowByID(id_);
+ if (window->type() == aura::client::WINDOW_TYPE_PANEL)
+ wm::MoveWindowToEventRoot(window, event);
+ window->Show();
+ wm::ActivateWindow(window);
+ return false;
+}
+
+base::string16 WindowWatcherShelfItemDelegate::GetTitle() {
+ return watcher_->GetWindowByID(id_)->title();
+}
+
+ui::MenuModel* WindowWatcherShelfItemDelegate::CreateContextMenu(
+ aura::Window* root_window) {
+ return NULL;
+}
+
+ShelfMenuModel* WindowWatcherShelfItemDelegate::CreateApplicationMenu(
+ int event_flags) {
+ return NULL;
+}
+
+bool WindowWatcherShelfItemDelegate::IsDraggable() {
+ return true;
+}
+
+bool WindowWatcherShelfItemDelegate::ShouldShowTooltip() {
+ return true;
+}
+
+} // namespace shell
+} // namespace ash
diff --git a/ash/shell/window_watcher_shelf_item_delegate.h b/ash/shell/window_watcher_shelf_item_delegate.h
new file mode 100644
index 0000000000..cf8f6a7931
--- /dev/null
+++ b/ash/shell/window_watcher_shelf_item_delegate.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+#ifndef ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
+#define ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
+
+#include "ash/launcher/launcher_types.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+namespace shell {
+
+class WindowWatcher;
+
+// ShelfItemDelegate implementation used by WindowWatcher.
+class WindowWatcherShelfItemDelegate : public ShelfItemDelegate {
+ public:
+ WindowWatcherShelfItemDelegate(LauncherID id, WindowWatcher* watcher);
+ virtual ~WindowWatcherShelfItemDelegate();
+
+ // ShelfItemDelegate:
+ virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
+ virtual base::string16 GetTitle() OVERRIDE;
+ virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+ virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+ virtual bool IsDraggable() OVERRIDE;
+ virtual bool ShouldShowTooltip() OVERRIDE;
+
+ private:
+ LauncherID id_;
+ WindowWatcher* watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowWatcherShelfItemDelegate);
+};
+
+} // namespace shell
+} // namespace ash
+
+#endif // ASH_SHELL_WINDOW_WATCHER_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index e84af7ef67..41c44060cd 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -44,15 +44,15 @@ namespace ash {
class AccessibilityDelegate;
class CapsLockDelegate;
-class LauncherDelegate;
-struct LauncherItem;
class MediaDelegate;
class NewWindowDelegate;
class RootWindowHostFactory;
class SessionStateDelegate;
+class ShelfDelegate;
class ShelfModel;
class SystemTrayDelegate;
class UserWallpaperDelegate;
+struct LauncherItem;
enum UserMetricsAction {
UMA_ACCEL_KEYBOARD_BRIGHTNESS_DOWN_F6,
@@ -68,7 +68,6 @@ enum UserMetricsAction {
UMA_GESTURE_OVERVIEW,
UMA_LAUNCHER_CLICK_ON_APP,
UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON,
- UMA_MINIMIZE_PER_KEY,
UMA_MOUSE_DOWN,
UMA_SHELF_ALIGNMENT_SET_BOTTOM,
UMA_SHELF_ALIGNMENT_SET_LEFT,
@@ -145,9 +144,9 @@ class ASH_EXPORT ShellDelegate {
// the created delegate.
virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() = 0;
- // Creates a new LauncherDelegate. Shell takes ownership of the returned
+ // Creates a new ShelfDelegate. Shell takes ownership of the returned
// value.
- virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) = 0;
+ virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) = 0;
// Creates a system-tray delegate. Shell takes ownership of the delegate.
virtual SystemTrayDelegate* CreateSystemTrayDelegate() = 0;
diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc
index 5d75e945ab..e9b042d90d 100644
--- a/ash/system/bluetooth/tray_bluetooth.cc
+++ b/ash/system/bluetooth/tray_bluetooth.cc
@@ -353,7 +353,7 @@ class BluetoothDetailedView : public TrayDetailsView,
ash::SystemTrayDelegate* delegate =
ash::Shell::GetInstance()->system_tray_delegate();
if (sender == footer()->content()) {
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
} else if (sender == manage_devices_) {
delegate->ManageBluetoothDevices();
} else if (sender == enable_bluetooth_) {
diff --git a/ash/system/chromeos/audio/tray_audio.cc b/ash/system/chromeos/audio/tray_audio.cc
index da83aee885..5922f004ba 100644
--- a/ash/system/chromeos/audio/tray_audio.cc
+++ b/ash/system/chromeos/audio/tray_audio.cc
@@ -518,7 +518,7 @@ class AudioDetailedView : public TrayDetailsView,
// Overridden from ViewClickListener.
virtual void OnViewClicked(views::View* sender) OVERRIDE {
if (sender == footer()->content()) {
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
} else {
AudioDeviceMap::iterator iter = device_map_.find(sender);
if (iter == device_map_.end())
diff --git a/ash/system/chromeos/label_tray_view.cc b/ash/system/chromeos/label_tray_view.cc
index 5af294efe0..0ae4604680 100644
--- a/ash/system/chromeos/label_tray_view.cc
+++ b/ash/system/chromeos/label_tray_view.cc
@@ -9,6 +9,7 @@
#include "ash/system/tray/view_click_listener.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/font.h"
+#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/fill_layout.h"
diff --git a/ash/system/chromeos/managed/tray_locally_managed_user.cc b/ash/system/chromeos/managed/tray_locally_managed_user.cc
index 9a8fd481d6..bd3e380b25 100644
--- a/ash/system/chromeos/managed/tray_locally_managed_user.cc
+++ b/ash/system/chromeos/managed/tray_locally_managed_user.cc
@@ -10,6 +10,7 @@
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_notification_view.h"
#include "ash/system/user/login_status.h"
+#include "base/callback.h"
#include "base/logging.h"
#include "grit/ash_resources.h"
#include "ui/base/resource/resource_bundle.h"
@@ -77,18 +78,14 @@ void TrayLocallyManagedUser::UpdateAfterLoginStatusChange(
void TrayLocallyManagedUser::CreateOrUpdateNotification(
const base::string16& new_message) {
ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
- scoped_ptr<Notification> notification(new Notification(
- message_center::NOTIFICATION_TYPE_SIMPLE,
- kNotificationId,
- string16() /* no title */,
- new_message,
- bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER),
- base::string16() /* display_source */,
- message_center::NotifierId(
- system_notifier::NOTIFIER_LOCALLY_MANAGED_USER),
- message_center::RichNotificationData(),
- NULL /* no delegate */));
- notification->SetSystemPriority();
+ scoped_ptr<Notification> notification(
+ message_center::Notification::CreateSystemNotification(
+ kNotificationId,
+ string16() /* no title */,
+ new_message,
+ bundle.GetImageNamed(IDR_AURA_UBER_TRAY_MANAGED_USER),
+ system_notifier::kNotifierLocallyManagedUser,
+ base::Closure() /* null callback */));
message_center::MessageCenter::Get()->AddNotification(notification.Pass());
}
diff --git a/ash/system/chromeos/network/network_connect.cc b/ash/system/chromeos/network/network_connect.cc
index 4013c77349..d8f524aea4 100644
--- a/ash/system/chromeos/network/network_connect.cc
+++ b/ash/system/chromeos/network/network_connect.cc
@@ -439,7 +439,7 @@ void ShowMobileSetup(const std::string& service_path) {
UTF8ToUTF16(cellular->name())),
ui::ResourceBundle::GetSharedInstance().GetImageNamed(
IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED),
- ash::system_notifier::NOTIFIER_NETWORK,
+ ash::system_notifier::kNotifierNetwork,
base::Bind(&ash::network_connect::ShowNetworkSettings,
service_path)));
return;
diff --git a/ash/system/chromeos/network/network_state_list_detailed_view.cc b/ash/system/chromeos/network/network_state_list_detailed_view.cc
index ffd8ad5833..ff45d77b12 100644
--- a/ash/system/chromeos/network/network_state_list_detailed_view.cc
+++ b/ash/system/chromeos/network/network_state_list_detailed_view.cc
@@ -299,8 +299,7 @@ void NetworkStateListDetailedView::OnViewClicked(views::View* sender) {
ResetInfoBubble();
if (sender == footer()->content()) {
- RootWindowController::ForWindow(GetWidget()->GetNativeView())->
- GetSystemTray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
return;
}
diff --git a/ash/system/chromeos/network/network_state_notifier.cc b/ash/system/chromeos/network/network_state_notifier.cc
index c8beb32a6a..ac69a8ab55 100644
--- a/ash/system/chromeos/network/network_state_notifier.cc
+++ b/ash/system/chromeos/network/network_state_notifier.cc
@@ -67,7 +67,7 @@ void ShowErrorNotification(const std::string& notification_id,
title,
message,
icon,
- ash::system_notifier::NOTIFIER_NETWORK_ERROR,
+ ash::system_notifier::kNotifierNetworkError,
callback));
}
@@ -184,7 +184,7 @@ void NetworkStateNotifier::UpdateCellularActivating(
l10n_util::GetStringFUTF16(IDS_NETWORK_CELLULAR_ACTIVATED,
UTF8ToUTF16((cellular->name()))),
icon,
- system_notifier::NOTIFIER_NETWORK,
+ system_notifier::kNotifierNetwork,
base::Bind(&ash::network_connect::ShowNetworkSettings,
cellular->path())));
}
diff --git a/ash/system/chromeos/network/tray_sms.cc b/ash/system/chromeos/network/tray_sms.cc
index 9adb5f9c91..7b00c276e7 100644
--- a/ash/system/chromeos/network/tray_sms.cc
+++ b/ash/system/chromeos/network/tray_sms.cc
@@ -234,7 +234,7 @@ class TraySms::SmsDetailedView : public TrayDetailsView,
// Overridden from ViewClickListener.
virtual void OnViewClicked(views::View* sender) OVERRIDE {
if (sender == footer()->content())
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
}
DISALLOW_COPY_AND_ASSIGN(SmsDetailedView);
diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc
index 1eb004840c..1fdd6a95ed 100644
--- a/ash/system/chromeos/power/tray_power.cc
+++ b/ash/system/chromeos/power/tray_power.cc
@@ -199,7 +199,9 @@ bool TrayPower::MaybeShowUsbChargerNotification() {
IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
base::string16(),
- message_center::NotifierId(system_notifier::NOTIFIER_POWER),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierPower),
message_center::RichNotificationData(),
NULL));
message_center_->AddNotification(notification.Pass());
diff --git a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
index 2998b3b08a..0bba2b2585 100644
--- a/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_capture_tray_item.cc
@@ -63,7 +63,9 @@ void ScreenCaptureTrayItem::CreateOrUpdateNotification() {
base::string16() /* body is blank */,
resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
base::string16() /* display_source */,
- message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_CAPTURE),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierScreenCapture),
data,
new tray::ScreenNotificationDelegate(this)));
notification->SetSystemPriority();
diff --git a/ash/system/chromeos/screen_security/screen_share_tray_item.cc b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
index bf70c44019..bceae5958c 100644
--- a/ash/system/chromeos/screen_security/screen_share_tray_item.cc
+++ b/ash/system/chromeos/screen_security/screen_share_tray_item.cc
@@ -73,7 +73,9 @@ void ScreenShareTrayItem::CreateOrUpdateNotification() {
base::string16() /* body is blank */,
resource_bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
base::string16() /* display_source */,
- message_center::NotifierId(system_notifier::NOTIFIER_SCREEN_SHARE),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierScreenShare),
data,
new tray::ScreenNotificationDelegate(this)));
notification->SetSystemPriority();
diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc
index bf0b3dc76a..35627e1ae2 100644
--- a/ash/system/chromeos/tray_display.cc
+++ b/ash/system/chromeos/tray_display.cc
@@ -107,12 +107,21 @@ base::string16 GetAllDisplayInfo() {
}
void OpenSettings() {
- user::LoginStatus login_status =
- Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
- if (login_status == user::LOGGED_IN_USER ||
- login_status == user::LOGGED_IN_OWNER ||
- login_status == user::LOGGED_IN_GUEST) {
- Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
+ // switch is intentionally introduced without default, to cause an error when
+ // a new type of login status is introduced.
+ switch (Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus()) {
+ case user::LOGGED_IN_NONE:
+ case user::LOGGED_IN_LOCKED:
+ return;
+
+ case user::LOGGED_IN_USER:
+ case user::LOGGED_IN_OWNER:
+ case user::LOGGED_IN_GUEST:
+ case user::LOGGED_IN_RETAIL_MODE:
+ case user::LOGGED_IN_PUBLIC:
+ case user::LOGGED_IN_LOCALLY_MANAGED:
+ case user::LOGGED_IN_KIOSK_APP:
+ Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings();
}
}
@@ -264,7 +273,6 @@ class DisplayView : public internal::ActionableView {
int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 -
kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width();
label_->SizeToFit(label_max_width);
- PreferredSizeChanged();
}
views::ImageView* image_;
@@ -373,7 +381,9 @@ void TrayDisplay::CreateOrUpdateNotification(
additional_message,
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DISPLAY),
base::string16(), // display_source
- message_center::NotifierId(system_notifier::NOTIFIER_DISPLAY),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierDisplay),
message_center::RichNotificationData(),
new message_center::HandleNotificationClickedDelegate(
base::Bind(&OpenSettings))));
diff --git a/ash/system/date/date_view.cc b/ash/system/date/date_view.cc
index 24428d1d87..ae4a546530 100644
--- a/ash/system/date/date_view.cc
+++ b/ash/system/date/date_view.cc
@@ -17,6 +17,7 @@
#include "third_party/icu/source/i18n/unicode/dtptngen.h"
#include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/border.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/grid_layout.h"
diff --git a/ash/system/drive/tray_drive.cc b/ash/system/drive/tray_drive.cc
index 028aa29bb5..4305d5af0e 100644
--- a/ash/system/drive/tray_drive.cc
+++ b/ash/system/drive/tray_drive.cc
@@ -364,7 +364,7 @@ class DriveDetailedView : public TrayDetailsView,
virtual void OnViewClicked(views::View* sender) OVERRIDE {
SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
if (sender == footer()->content()) {
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
} else if (sender == settings_) {
delegate->ShowDriveSettings();
}
diff --git a/ash/system/ime/tray_ime.cc b/ash/system/ime/tray_ime.cc
index 87e254a1cf..d58b1ef398 100644
--- a/ash/system/ime/tray_ime.cc
+++ b/ash/system/ime/tray_ime.cc
@@ -147,7 +147,7 @@ class IMEDetailedView : public TrayDetailsView,
virtual void OnViewClicked(views::View* sender) OVERRIDE {
SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
if (sender == footer()->content()) {
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
} else if (sender == settings_) {
delegate->ShowIMESettings();
} else {
@@ -231,7 +231,9 @@ void TrayIME::UpdateOrCreateNotification() {
base::string16(), // message
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_IME),
base::string16(), // display_source
- message_center::NotifierId(system_notifier::NOTIFIER_INPUT_METHOD),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierInputMethod),
message_center::RichNotificationData(),
new message_center::HandleNotificationClickedDelegate(
base::Bind(&TrayIME::PopupDetailedView,
diff --git a/ash/system/locale/locale_notification_controller.cc b/ash/system/locale/locale_notification_controller.cc
index 7d66d5b77f..419baafb40 100644
--- a/ash/system/locale/locale_notification_controller.cc
+++ b/ash/system/locale/locale_notification_controller.cc
@@ -117,7 +117,9 @@ void LocaleNotificationController::OnLocaleChanged(
IDS_ASH_STATUS_TRAY_LOCALE_CHANGE_MESSAGE, from, to),
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_LOCALE),
base::string16() /* display_source */,
- message_center::NotifierId(system_notifier::NOTIFIER_LOCALE),
+ message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierLocale),
optional,
new LocaleNotificationDelegate(delegate)));
message_center::MessageCenter::Get()->AddNotification(notification.Pass());
diff --git a/ash/system/session_length_limit/tray_session_length_limit.cc b/ash/system/session_length_limit/tray_session_length_limit.cc
index fe8ff7ebc1..cdf69d5d60 100644
--- a/ash/system/session_length_limit/tray_session_length_limit.cc
+++ b/ash/system/session_length_limit/tray_session_length_limit.cc
@@ -103,7 +103,8 @@ void CreateOrUpdateNotification(const base::TimeDelta& remaining_time,
bundle.GetImageNamed(IDR_AURA_UBER_TRAY_SESSION_LENGTH_LIMIT_TIMER),
base::string16() /* display_source */,
message_center::NotifierId(
- system_notifier::NOTIFIER_SESSION_LENGTH_TIMEOUT),
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ system_notifier::kNotifierSessionLengthTimeout),
data,
NULL /* delegate */));
notification->SetSystemPriority();
diff --git a/ash/system/system_notifier.cc b/ash/system/system_notifier.cc
index 554c0f945c..e4d65d2733 100644
--- a/ash/system/system_notifier.cc
+++ b/ash/system/system_notifier.cc
@@ -13,33 +13,67 @@ namespace {
// See http://dev.chromium.org/chromium-os/chromiumos-design-docs/
// system-notifications for the reasoning.
-const AshSystemComponentNotifierType kAlwaysShownNotifierIds[] = {
- NOTIFIER_DISPLAY,
- NOTIFIER_DISPLAY_ERROR,
- NOTIFIER_POWER,
+const char* kAlwaysShownNotifierIds[] = {
+ kNotifierDisplay,
+ kNotifierDisplayError,
+ kNotifierPower,
+ NULL
};
-} // namespace
-
-std::string SystemComponentTypeToString(AshSystemComponentNotifierType type) {
- if (type == NOTIFIER_SCREENSHOT)
- return "screenshot";
-
- // TODO(mukai): fill the names of other components.
- NOTIMPLEMENTED();
- return std::string();
-}
+const char* kAshSystemNotifiers[] = {
+ kNotifierDisplay,
+ kNotifierDisplayResolutionChange,
+ kNotifierDisplayError,
+ kNotifierInputMethod,
+ kNotifierLocale,
+ kNotifierLocallyManagedUser,
+ kNotifierMultiProfileFirstRun,
+ kNotifierNetwork,
+ kNotifierNetworkError,
+ kNotifierScreenshot,
+ kNotifierScreenCapture,
+ kNotifierScreenShare,
+ kNotifierSessionLengthTimeout,
+ kNotifierPower,
+ NULL
+};
-bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) {
+bool MatchSystemNotifierId(const message_center::NotifierId& notifier_id,
+ const char* id_list[]) {
if (notifier_id.type != message_center::NotifierId::SYSTEM_COMPONENT)
return false;
- for (size_t i = 0; i < arraysize(kAlwaysShownNotifierIds); ++i) {
- if (notifier_id.system_component_type == kAlwaysShownNotifierIds[i])
+ for (size_t i = 0; id_list[i] != NULL; ++i) {
+ if (notifier_id.id == id_list[i])
return true;
}
return false;
}
+} // namespace
+
+const char kNotifierDisplay[] = "ash.display";
+const char kNotifierDisplayResolutionChange[] = "ash.display.resolution-change";
+const char kNotifierDisplayError[] = "ash.display.error";
+const char kNotifierInputMethod[] = "ash.input-method";
+const char kNotifierLocale[] = "ash.locale";
+const char kNotifierLocallyManagedUser[] = "ash.locally-managed-user";
+const char kNotifierMultiProfileFirstRun[] = "ash.multi-profile.first-run";
+const char kNotifierNetwork[] = "ash.network";
+const char kNotifierNetworkError[] = "ash.network.error";
+const char kNotifierScreenshot[] = "ash.screenshot";
+const char kNotifierScreenCapture[] = "ash.screen-capture";
+const char kNotifierScreenShare[] = "ash.screen-share";
+const char kNotifierSessionLengthTimeout[] = "ash.session-length-timeout";
+const char kNotifierPower[] = "ash.power";
+
+bool ShouldAlwaysShowPopups(const message_center::NotifierId& notifier_id) {
+ return MatchSystemNotifierId(notifier_id, kAlwaysShownNotifierIds);
+}
+
+bool IsAshSystemNotifier(const message_center::NotifierId& notifier_id) {
+ return MatchSystemNotifierId(notifier_id, kAshSystemNotifiers);
+}
+
} // namespace system_notifier
} // namespace ash
diff --git a/ash/system/system_notifier.h b/ash/system/system_notifier.h
index 3d823c8851..53ac77db7d 100644
--- a/ash/system/system_notifier.h
+++ b/ash/system/system_notifier.h
@@ -13,27 +13,21 @@
namespace ash {
namespace system_notifier {
-enum AshSystemComponentNotifierType {
- NOTIFIER_NO_SYSTEM_COMPONENT = -1,
-
- // Alphabetical order.
- NOTIFIER_DISPLAY,
- NOTIFIER_DISPLAY_RESOLUTION_CHANGE,
- NOTIFIER_DISPLAY_ERROR,
- NOTIFIER_INPUT_METHOD,
- NOTIFIER_LOCALE,
- NOTIFIER_LOCALLY_MANAGED_USER,
- NOTIFIER_NETWORK,
- NOTIFIER_NETWORK_ERROR,
- NOTIFIER_SCREENSHOT,
- NOTIFIER_SCREEN_CAPTURE,
- NOTIFIER_SCREEN_SHARE,
- NOTIFIER_SESSION_LENGTH_TIMEOUT,
- NOTIFIER_POWER,
-};
-
-ASH_EXPORT std::string SystemComponentTypeToString(
- AshSystemComponentNotifierType type);
+// The list of ash system notifier IDs. Alphabetical order.
+ASH_EXPORT extern const char kNotifierDisplay[];
+ASH_EXPORT extern const char kNotifierDisplayResolutionChange[];
+ASH_EXPORT extern const char kNotifierDisplayError[];
+ASH_EXPORT extern const char kNotifierInputMethod[];
+ASH_EXPORT extern const char kNotifierLocale[];
+ASH_EXPORT extern const char kNotifierLocallyManagedUser[];
+ASH_EXPORT extern const char kNotifierMultiProfileFirstRun[];
+ASH_EXPORT extern const char kNotifierNetwork[];
+ASH_EXPORT extern const char kNotifierNetworkError[];
+ASH_EXPORT extern const char kNotifierScreenshot[];
+ASH_EXPORT extern const char kNotifierScreenCapture[];
+ASH_EXPORT extern const char kNotifierScreenShare[];
+ASH_EXPORT extern const char kNotifierSessionLengthTimeout[];
+ASH_EXPORT extern const char kNotifierPower[];
// Returns true if notifications from |notifier_id| should always appear as
// popups. "Always appear" means the popups should appear even in login screen,
@@ -41,6 +35,10 @@ ASH_EXPORT std::string SystemComponentTypeToString(
ASH_EXPORT bool ShouldAlwaysShowPopups(
const message_center::NotifierId& notifier_id);
+// Returns true if |notifier_id| is the system notifier from Ash.
+ASH_EXPORT bool IsAshSystemNotifier(
+ const message_center::NotifierId& notifier_id);
+
} // namespace system_notifier
} // namespace ash
diff --git a/ash/system/tray/actionable_view.cc b/ash/system/tray/actionable_view.cc
index 1fae3ba1e1..169b46da80 100644
--- a/ash/system/tray/actionable_view.cc
+++ b/ash/system/tray/actionable_view.cc
@@ -22,10 +22,14 @@ ActionableView::ActionableView()
ActionableView::~ActionableView() {
}
-void ActionableView::DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds) {
- gfx::Rect rect = bounds;
- rect.Inset(1, 1, 3, 3);
- canvas->DrawRect(rect, kFocusBorderColor);
+void ActionableView::OnPaintFocus(gfx::Canvas* canvas) {
+ gfx::Rect rect(GetFocusBounds());
+ rect.Inset(1, 1, 3, 2);
+ canvas->DrawSolidFocusRect(rect, kFocusBorderColor);
+}
+
+gfx::Rect ActionableView::GetFocusBounds() {
+ return GetLocalBounds();
}
const char* ActionableView::GetClassName() const {
@@ -59,9 +63,27 @@ void ActionableView::SetAccessibleName(const base::string16& name) {
accessible_name_ = name;
}
-void ActionableView::OnPaintFocusBorder(gfx::Canvas* canvas) {
+void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) {
+ state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
+ state->name = accessible_name_;
+}
+
+void ActionableView::OnPaint(gfx::Canvas* canvas) {
+ View::OnPaint(canvas);
if (HasFocus())
- DrawBorder(canvas, GetLocalBounds());
+ OnPaintFocus(canvas);
+}
+
+void ActionableView::OnFocus() {
+ View::OnFocus();
+ // We render differently when focused.
+ SchedulePaint();
+}
+
+void ActionableView::OnBlur() {
+ View::OnBlur();
+ // We render differently when focused.
+ SchedulePaint();
}
void ActionableView::OnGestureEvent(ui::GestureEvent* event) {
@@ -69,10 +91,5 @@ void ActionableView::OnGestureEvent(ui::GestureEvent* event) {
event->SetHandled();
}
-void ActionableView::GetAccessibleState(ui::AccessibleViewState* state) {
- state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
- state->name = accessible_name_;
-}
-
} // namespace internal
} // namespace ash
diff --git a/ash/system/tray/actionable_view.h b/ash/system/tray/actionable_view.h
index 0d0f1a1c30..f2b3ec1deb 100644
--- a/ash/system/tray/actionable_view.h
+++ b/ash/system/tray/actionable_view.h
@@ -21,6 +21,8 @@ namespace internal {
// Exported for SystemTray.
class ASH_EXPORT ActionableView : public views::View {
public:
+ static const char kViewClassName[];
+
ActionableView();
virtual ~ActionableView();
@@ -28,10 +30,11 @@ class ASH_EXPORT ActionableView : public views::View {
void SetAccessibleName(const base::string16& name);
const base::string16& accessible_name() const { return accessible_name_; }
- static const char kViewClassName[];
-
protected:
- void DrawBorder(gfx::Canvas* canvas, const gfx::Rect& bounds);
+ void OnPaintFocus(gfx::Canvas* canvas);
+
+ // Returns the bounds to paint the focus rectangle in.
+ virtual gfx::Rect GetFocusBounds();
// Performs an action when user clicks on the view (on mouse-press event), or
// presses a key when this view is in focus. Returns true if the event has
@@ -45,7 +48,9 @@ class ASH_EXPORT ActionableView : public views::View {
virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseCaptureLost() OVERRIDE;
virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
// Overridden from ui::EventHandler.
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
diff --git a/ash/system/tray/fixed_sized_scroll_view.cc b/ash/system/tray/fixed_sized_scroll_view.cc
index 59b524b752..d3fff9ad1a 100644
--- a/ash/system/tray/fixed_sized_scroll_view.cc
+++ b/ash/system/tray/fixed_sized_scroll_view.cc
@@ -9,7 +9,6 @@ namespace internal {
FixedSizedScrollView::FixedSizedScrollView() {
set_notify_enter_exit_on_child(true);
- set_focus_border(NULL);
}
FixedSizedScrollView::~FixedSizedScrollView() {
@@ -54,9 +53,5 @@ void FixedSizedScrollView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
contents()->SetBoundsRect(bounds);
}
-void FixedSizedScrollView::OnPaintFocusBorder(gfx::Canvas* canvas) {
- // Do not paint the focus border.
-}
-
} // namespace internal
} // namespace ash
diff --git a/ash/system/tray/fixed_sized_scroll_view.h b/ash/system/tray/fixed_sized_scroll_view.h
index c5d9bc0a09..aff53c217a 100644
--- a/ash/system/tray/fixed_sized_scroll_view.h
+++ b/ash/system/tray/fixed_sized_scroll_view.h
@@ -33,7 +33,6 @@ class FixedSizedScrollView : public views::ScrollView {
protected:
// Overridden from views::View:
virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
private:
gfx::Size fixed_size_;
diff --git a/ash/system/tray/hover_highlight_view.cc b/ash/system/tray/hover_highlight_view.cc
index f68ea12b67..8afa2de400 100644
--- a/ash/system/tray/hover_highlight_view.cc
+++ b/ash/system/tray/hover_highlight_view.cc
@@ -11,6 +11,7 @@
#include "ui/base/accessibility/accessible_view_state.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
+#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
diff --git a/ash/system/tray/special_popup_row.cc b/ash/system/tray/special_popup_row.cc
index 896d1516df..fd10f4721b 100644
--- a/ash/system/tray/special_popup_row.cc
+++ b/ash/system/tray/special_popup_row.cc
@@ -13,6 +13,7 @@
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/rect.h"
+#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/painter.h"
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 045526c5dd..b5d8cc3032 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -101,11 +101,11 @@ class SystemBubbleWrapper {
}
is_persistent_ = is_persistent;
- // If ChromeVox is enabled, focus the default item.
- AccessibilityDelegate* delegate =
- Shell::GetInstance()->accessibility_delegate();
- if (delegate->IsSpokenFeedbackEnabled())
- bubble_->FocusDefault();
+ // If ChromeVox is enabled, focus the default item if no item is focused.
+ if (Shell::GetInstance()->accessibility_delegate()->
+ IsSpokenFeedbackEnabled()) {
+ bubble_->FocusDefaultIfNeeded();
+ }
}
// Convenience accessors:
@@ -461,11 +461,11 @@ void SystemTray::ShowItems(const std::vector<SystemTrayItem*>& items,
notification_bubble_.reset();
if (system_bubble_.get() && creation_type == BUBBLE_USE_EXISTING) {
system_bubble_->bubble()->UpdateView(items, bubble_type);
- // If ChromeVox is enabled, focus the default item.
- AccessibilityDelegate* delegate =
- Shell::GetInstance()->accessibility_delegate();
- if (delegate->IsSpokenFeedbackEnabled())
- system_bubble_->bubble()->FocusDefault();
+ // If ChromeVox is enabled, focus the default item if no item is focused.
+ if (Shell::GetInstance()->accessibility_delegate()->
+ IsSpokenFeedbackEnabled()) {
+ system_bubble_->bubble()->FocusDefaultIfNeeded();
+ }
} else {
// Remember if the menu is a single property (like e.g. volume) or the
// full tray menu. Note that in case of the |BUBBLE_USE_EXISTING| case
diff --git a/ash/system/tray/system_tray_bubble.cc b/ash/system/tray/system_tray_bubble.cc
index 362e996553..1aa60bc554 100644
--- a/ash/system/tray/system_tray_bubble.cc
+++ b/ash/system/tray/system_tray_bubble.cc
@@ -271,9 +271,9 @@ void SystemTrayBubble::InitView(views::View* anchor,
}
}
-void SystemTrayBubble::FocusDefault() {
+void SystemTrayBubble::FocusDefaultIfNeeded() {
views::FocusManager* manager = bubble_view_->GetFocusManager();
- if (!manager)
+ if (!manager || manager->GetFocusedView())
return;
views::View* view = manager->GetNextFocusableView(NULL, NULL, false, false);
@@ -352,11 +352,14 @@ bool SystemTrayBubble::ShouldShowLauncher() const {
void SystemTrayBubble::CreateItemViews(user::LoginStatus login_status) {
std::vector<views::View*> item_views;
+ views::View* focus_view = NULL;
for (size_t i = 0; i < items_.size(); ++i) {
views::View* view = NULL;
switch (bubble_type_) {
case BUBBLE_TYPE_DEFAULT:
view = items_[i]->CreateDefaultView(login_status);
+ if (items_[i]->restore_focus())
+ focus_view = view;
break;
case BUBBLE_TYPE_DETAILED:
view = items_[i]->CreateDetailedView(login_status);
@@ -377,6 +380,8 @@ void SystemTrayBubble::CreateItemViews(user::LoginStatus login_status) {
item_views[i], is_default_bubble,
is_default_bubble && (i < item_views.size() - 2)));
}
+ if (focus_view)
+ focus_view->RequestFocus();
}
} // namespace internal
diff --git a/ash/system/tray/system_tray_bubble.h b/ash/system/tray/system_tray_bubble.h
index 7232805342..b84c3ea022 100644
--- a/ash/system/tray/system_tray_bubble.h
+++ b/ash/system/tray/system_tray_bubble.h
@@ -43,8 +43,8 @@ class SystemTrayBubble {
user::LoginStatus login_status,
views::TrayBubbleView::InitParams* init_params);
- // Focus the default item.
- void FocusDefault();
+ // Focus the default item if no item is focused. Othewise, do nothing.
+ void FocusDefaultIfNeeded();
BubbleType bubble_type() const { return bubble_type_; }
views::TrayBubbleView* bubble_view() const { return bubble_view_; }
diff --git a/ash/system/tray/system_tray_item.cc b/ash/system/tray/system_tray_item.cc
index b2acf08c07..f5d5b717c7 100644
--- a/ash/system/tray/system_tray_item.cc
+++ b/ash/system/tray/system_tray_item.cc
@@ -12,7 +12,8 @@
namespace ash {
SystemTrayItem::SystemTrayItem(SystemTray* system_tray)
- : system_tray_(system_tray) {
+ : system_tray_(system_tray),
+ restore_focus_(false) {
}
SystemTrayItem::~SystemTrayItem() {
diff --git a/ash/system/tray/system_tray_item.h b/ash/system/tray/system_tray_item.h
index a0d7d1b0e8..b42f3f5b35 100644
--- a/ash/system/tray/system_tray_item.h
+++ b/ash/system/tray/system_tray_item.h
@@ -108,8 +108,14 @@ class ASH_EXPORT SystemTrayItem {
// Returns the system tray that this item belongs to.
SystemTray* system_tray() const { return system_tray_; }
+ bool restore_focus() const { return restore_focus_; }
+ void set_restore_focus(bool restore_focus) {
+ restore_focus_ = restore_focus;
+ }
+
private:
SystemTray* system_tray_;
+ bool restore_focus_;
DISALLOW_COPY_AND_ASSIGN(SystemTrayItem);
};
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 3939b70105..80cac07e36 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -333,10 +333,10 @@ TrayBackgroundView::TrayBackgroundView(
set_notify_enter_exit_on_child(true);
// Initially we want to paint the background, but without the hover effect.
- hide_background_animator_.SetPaintsBackground(true,
- internal::BackgroundAnimator::CHANGE_IMMEDIATE);
- hover_background_animator_.SetPaintsBackground(false,
- internal::BackgroundAnimator::CHANGE_IMMEDIATE);
+ hide_background_animator_.SetPaintsBackground(
+ true, BACKGROUND_CHANGE_IMMEDIATE);
+ hover_background_animator_.SetPaintsBackground(
+ false, BACKGROUND_CHANGE_IMMEDIATE);
tray_container_ = new TrayContainer(shelf_alignment_);
SetContents(tray_container_);
@@ -362,8 +362,8 @@ void TrayBackgroundView::OnMouseEntered(const ui::MouseEvent& event) {
if (!background_ || draw_background_as_active_ ||
ash::switches::UseAlternateShelfLayout())
return;
- hover_background_animator_.SetPaintsBackground(true,
- internal::BackgroundAnimator::CHANGE_ANIMATE);
+ hover_background_animator_.SetPaintsBackground(
+ true, BACKGROUND_CHANGE_ANIMATE);
}
void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) {
@@ -371,22 +371,14 @@ void TrayBackgroundView::OnMouseExited(const ui::MouseEvent& event) {
if (!background_ || draw_background_as_active_ ||
ash::switches::UseAlternateShelfLayout())
return;
- hover_background_animator_.SetPaintsBackground(false,
- internal::BackgroundAnimator::CHANGE_ANIMATE);
+ hover_background_animator_.SetPaintsBackground(
+ false, BACKGROUND_CHANGE_ANIMATE);
}
void TrayBackgroundView::ChildPreferredSizeChanged(views::View* child) {
PreferredSizeChanged();
}
-void TrayBackgroundView::OnPaintFocusBorder(gfx::Canvas* canvas) {
- // The tray itself expands to the right and bottom edge of the screen to make
- // sure clicking on the edges brings up the popup. However, the focus border
- // should be only around the container.
- if (HasFocus())
- DrawBorder(canvas, GetContentsBounds());
-}
-
void TrayBackgroundView::GetAccessibleState(ui::AccessibleViewState* state) {
state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
state->name = GetAccessibleNameForTray();
@@ -403,6 +395,13 @@ bool TrayBackgroundView::PerformAction(const ui::Event& event) {
return false;
}
+gfx::Rect TrayBackgroundView::GetFocusBounds() {
+ // The tray itself expands to the right and bottom edge of the screen to make
+ // sure clicking on the edges brings up the popup. However, the focus border
+ // should be only around the container.
+ return GetContentsBounds();
+}
+
void TrayBackgroundView::UpdateBackground(int alpha) {
// The animator should never fire when the alternate shelf layout is used.
if (!background_ || draw_background_as_active_)
@@ -419,8 +418,7 @@ void TrayBackgroundView::SetContents(views::View* contents) {
}
void TrayBackgroundView::SetPaintsBackground(
- bool value,
- internal::BackgroundAnimator::ChangeType change_type) {
+ bool value, BackgroundAnimatorChangeType change_type) {
DCHECK(!ash::switches::UseAlternateShelfLayout());
hide_background_animator_.SetPaintsBackground(value, change_type);
}
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index b459684429..5adaf4ec0c 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -70,12 +70,12 @@ class ASH_EXPORT TrayBackgroundView : public ActionableView,
virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE;
virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE;
virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
virtual void AboutToRequestFocusFromTabTraversal(bool reverse) OVERRIDE;
// Overridden from internal::ActionableView.
virtual bool PerformAction(const ui::Event& event) OVERRIDE;
+ virtual gfx::Rect GetFocusBounds() OVERRIDE;
// Overridden from internal::BackgroundAnimatorDelegate.
virtual void UpdateBackground(int alpha) OVERRIDE;
@@ -108,9 +108,8 @@ class ASH_EXPORT TrayBackgroundView : public ActionableView,
// Sets whether the tray paints a background. Default is true, but is set to
// false if a window overlaps the shelf.
- void SetPaintsBackground(
- bool value,
- internal::BackgroundAnimator::ChangeType change_type);
+ void SetPaintsBackground(bool value,
+ BackgroundAnimatorChangeType change_type);
// Initializes animations for the bubble.
void InitializeBubbleAnimations(views::Widget* bubble_widget);
diff --git a/ash/system/tray/tray_details_view.cc b/ash/system/tray/tray_details_view.cc
index dd33cdf05a..72efe4ffe0 100644
--- a/ash/system/tray/tray_details_view.cc
+++ b/ash/system/tray/tray_details_view.cc
@@ -5,10 +5,12 @@
#include "ash/system/tray/tray_details_view.h"
#include "ash/system/tray/fixed_sized_scroll_view.h"
+#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_item.h"
#include "ash/system/tray/tray_constants.h"
#include "ui/gfx/canvas.h"
#include "ui/views/background.h"
+#include "ui/views/border.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/layout/box_layout.h"
@@ -114,6 +116,16 @@ void TrayDetailsView::Reset() {
scroll_content_ = NULL;
}
+void TrayDetailsView::TransitionToDefaultView() {
+ // Cache pointer to owner in this function scope. TrayDetailsView will be
+ // deleted after called ShowDefaultView.
+ SystemTrayItem* owner = owner_;
+ if (footer_ && footer_->content() && footer_->content()->HasFocus())
+ owner->set_restore_focus(true);
+ owner->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ owner->set_restore_focus(false);
+}
+
void TrayDetailsView::Layout() {
if (!scroller_ || !footer_ || bounds().IsEmpty()) {
views::View::Layout();
diff --git a/ash/system/tray/tray_details_view.h b/ash/system/tray/tray_details_view.h
index 073873ad0d..28e9082c58 100644
--- a/ash/system/tray/tray_details_view.h
+++ b/ash/system/tray/tray_details_view.h
@@ -5,6 +5,7 @@
#ifndef ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_
#define ASH_SYSTEM_TRAY_TRAY_DETAILS_VIEW_H_
+#include "ash/ash_export.h"
#include "ash/system/tray/special_popup_row.h"
#include "ui/views/view.h"
@@ -22,7 +23,7 @@ class FixedSizedScrollView;
class ScrollBorder;
class ViewClickListener;
-class TrayDetailsView : public views::View {
+class ASH_EXPORT TrayDetailsView : public views::View {
public:
explicit TrayDetailsView(SystemTrayItem* owner);
virtual ~TrayDetailsView();
@@ -41,6 +42,11 @@ class TrayDetailsView : public views::View {
// Removes (and destroys) all child views.
void Reset();
+ // Transition to default view from details view. If |footer_| has focus before
+ // transition, the default view should focus on the owner of this details
+ // view.
+ void TransitionToDefaultView();
+
SystemTrayItem* owner() const { return owner_; }
SpecialPopupRow* footer() const { return footer_; }
FixedSizedScrollView* scroller() const { return scroller_; }
diff --git a/ash/system/tray/tray_details_view_unittest.cc b/ash/system/tray/tray_details_view_unittest.cc
new file mode 100644
index 0000000000..49a637e808
--- /dev/null
+++ b/ash/system/tray/tray_details_view_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright 2013 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/system/tray/tray_details_view.h"
+
+#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/system/status_area_widget.h"
+#include "ash/system/tray/system_tray.h"
+#include "ash/system/tray/system_tray_item.h"
+#include "ash/system/tray/tray_details_view.h"
+#include "ash/system/tray/view_click_listener.h"
+#include "ash/test/ash_test_base.h"
+#include "base/run_loop.h"
+#include "grit/ash_strings.h"
+#include "ui/aura/window.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+SystemTray* GetSystemTray() {
+ return Shell::GetPrimaryRootWindowController()->shelf()->
+ status_area_widget()->system_tray();
+}
+
+class TestDetailsView : public internal::TrayDetailsView,
+ public internal::ViewClickListener {
+ public:
+ explicit TestDetailsView(SystemTrayItem* owner)
+ : internal::TrayDetailsView(owner) {}
+
+ virtual ~TestDetailsView() {}
+
+ void CreateFooterAndFocus() {
+ // Uses bluetooth label for testing purpose. It can be changed to any
+ // string_id.
+ CreateSpecialRow(IDS_ASH_STATUS_TRAY_BLUETOOTH, this);
+ footer()->content()->RequestFocus();
+ }
+
+ // Overridden from internal::ViewClickListener:
+ virtual void OnViewClicked(views::View* sender) OVERRIDE {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestDetailsView);
+};
+
+// Trivial item implementation that tracks its views for testing.
+class TestItem : public SystemTrayItem {
+ public:
+ TestItem() : SystemTrayItem(GetSystemTray()), tray_view_(NULL) {}
+
+ // Overridden from SystemTrayItem:
+ virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE {
+ tray_view_ = new views::View;
+ return tray_view_;
+ }
+ virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
+ default_view_ = new views::View;
+ default_view_->set_focusable(true);
+ return default_view_;
+ }
+ virtual views::View* CreateDetailedView(user::LoginStatus status) OVERRIDE {
+ detailed_view_ = new TestDetailsView(this);
+ return detailed_view_;
+ }
+ virtual void DestroyTrayView() OVERRIDE {
+ tray_view_ = NULL;
+ }
+ virtual void DestroyDefaultView() OVERRIDE {
+ default_view_ = NULL;
+ }
+ virtual void DestroyDetailedView() OVERRIDE {
+ detailed_view_ = NULL;
+ }
+
+ views::View* tray_view() const { return tray_view_; }
+ views::View* default_view() const { return default_view_; }
+ TestDetailsView* detailed_view() const { return detailed_view_; }
+
+ private:
+ views::View* tray_view_;
+ views::View* default_view_;
+ TestDetailsView* detailed_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestItem);
+};
+
+} // namespace
+
+typedef AshTestBase TrayDetailsViewTest;
+
+TEST_F(TrayDetailsViewTest, TransitionToDefaultViewTest) {
+ SystemTray* tray = GetSystemTray();
+ ASSERT_TRUE(tray->GetWidget());
+
+ TestItem* test_item_1 = new TestItem;
+ TestItem* test_item_2 = new TestItem;
+ tray->AddTrayItem(test_item_1);
+ tray->AddTrayItem(test_item_2);
+
+ // Ensure the tray views are created.
+ ASSERT_TRUE(test_item_1->tray_view() != NULL);
+ ASSERT_TRUE(test_item_2->tray_view() != NULL);
+
+ // Show the default view.
+ tray->ShowDefaultView(BUBBLE_CREATE_NEW);
+ RunAllPendingInMessageLoop();
+
+ // Show the detailed view of item 2.
+ tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING);
+ EXPECT_TRUE(test_item_2->detailed_view());
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(test_item_2->default_view());
+
+ // Transition back to default view, the default view of item 2 should have
+ // focus.
+ test_item_2->detailed_view()->CreateFooterAndFocus();
+ test_item_2->detailed_view()->TransitionToDefaultView();
+ RunAllPendingInMessageLoop();
+
+ EXPECT_TRUE(test_item_2->default_view());
+ EXPECT_FALSE(test_item_2->detailed_view());
+ EXPECT_TRUE(test_item_2->default_view()->HasFocus());
+
+ // Show the detailed view of item 2 again.
+ tray->ShowDetailedView(test_item_2, 0, true, BUBBLE_USE_EXISTING);
+ EXPECT_TRUE(test_item_2->detailed_view());
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(test_item_2->default_view());
+
+ // Transition back to default view, the default view of item 2 should NOT have
+ // focus.
+ test_item_2->detailed_view()->TransitionToDefaultView();
+ RunAllPendingInMessageLoop();
+
+ EXPECT_TRUE(test_item_2->default_view());
+ EXPECT_FALSE(test_item_2->detailed_view());
+ EXPECT_FALSE(test_item_2->default_view()->HasFocus());
+}
+
+} // namespace test
+} // namespace ash
diff --git a/ash/system/tray/tray_empty.cc b/ash/system/tray/tray_empty.cc
index 27fb7302ed..f15f7b31e7 100644
--- a/ash/system/tray/tray_empty.cc
+++ b/ash/system/tray/tray_empty.cc
@@ -6,6 +6,7 @@
#include "ui/views/layout/box_layout.h"
#include "ui/views/background.h"
+#include "ui/views/border.h"
#include "ui/views/view.h"
namespace {
diff --git a/ash/system/tray/tray_notification_view.cc b/ash/system/tray/tray_notification_view.cc
index aa4b1d1a8a..c09589285e 100644
--- a/ash/system/tray/tray_notification_view.cc
+++ b/ash/system/tray/tray_notification_view.cc
@@ -10,6 +10,7 @@
#include "grit/ui_resources.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/image/image_skia.h"
+#include "ui/views/background.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/layout/grid_layout.h"
diff --git a/ash/system/tray/tray_popup_header_button.cc b/ash/system/tray/tray_popup_header_button.cc
index 7e20c00e4e..e9430e57df 100644
--- a/ash/system/tray/tray_popup_header_button.cc
+++ b/ash/system/tray/tray_popup_header_button.cc
@@ -8,6 +8,7 @@
#include "ash/system/tray/tray_constants.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
+#include "ui/views/painter.h"
namespace ash {
namespace internal {
@@ -37,6 +38,10 @@ TrayPopupHeaderButton::TrayPopupHeaderButton(views::ButtonListener* listener,
SetAccessibleName(bundle.GetLocalizedString(accessible_name_id));
set_focusable(true);
set_request_focus_on_press(false);
+
+ SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+ kFocusBorderColor,
+ gfx::Insets(1, 2, 2, 3)));
}
TrayPopupHeaderButton::~TrayPopupHeaderButton() {}
@@ -57,13 +62,6 @@ void TrayPopupHeaderButton::OnPaintBorder(gfx::Canvas* canvas) {
ash::kBorderDarkColor);
}
-void TrayPopupHeaderButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
- if (HasFocus()) {
- canvas->DrawRect(gfx::Rect(2, 1, width() - 4, height() - 3),
- kFocusBorderColor);
- }
-}
-
void TrayPopupHeaderButton::StateChanged() {
SchedulePaint();
}
diff --git a/ash/system/tray/tray_popup_header_button.h b/ash/system/tray/tray_popup_header_button.h
index f9209c86c7..37849bc2e9 100644
--- a/ash/system/tray/tray_popup_header_button.h
+++ b/ash/system/tray/tray_popup_header_button.h
@@ -32,7 +32,6 @@ class ASH_EXPORT TrayPopupHeaderButton : public views::ToggleImageButton {
virtual const char* GetClassName() const OVERRIDE;
virtual gfx::Size GetPreferredSize() OVERRIDE;
virtual void OnPaintBorder(gfx::Canvas* canvas) OVERRIDE;
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
// Overridden from views::CustomButton:
virtual void StateChanged() OVERRIDE;
diff --git a/ash/system/tray/tray_popup_label_button.cc b/ash/system/tray/tray_popup_label_button.cc
index 43cf3bdfbb..2f97c372c4 100644
--- a/ash/system/tray/tray_popup_label_button.cc
+++ b/ash/system/tray/tray_popup_label_button.cc
@@ -8,6 +8,7 @@
#include "ash/system/tray/tray_popup_label_button_border.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/rect.h"
+#include "ui/views/painter.h"
namespace ash {
namespace internal {
@@ -20,16 +21,12 @@ TrayPopupLabelButton::TrayPopupLabelButton(views::ButtonListener* listener,
set_request_focus_on_press(false);
set_animate_on_state_change(false);
SetHorizontalAlignment(gfx::ALIGN_CENTER);
+ SetFocusPainter(views::Painter::CreateSolidFocusPainter(
+ kFocusBorderColor,
+ gfx::Insets(1, 1, 2, 2)));
}
TrayPopupLabelButton::~TrayPopupLabelButton() {}
-void TrayPopupLabelButton::OnPaintFocusBorder(gfx::Canvas* canvas) {
- if (HasFocus()) {
- canvas->DrawRect(gfx::Rect(1, 1, width() - 3, height() - 3),
- ash::kFocusBorderColor);
- }
-}
-
} // namespace internal
} // namespace ash
diff --git a/ash/system/tray/tray_popup_label_button.h b/ash/system/tray/tray_popup_label_button.h
index 8d21abd98a..a1f9db6b7b 100644
--- a/ash/system/tray/tray_popup_label_button.h
+++ b/ash/system/tray/tray_popup_label_button.h
@@ -21,9 +21,6 @@ class TrayPopupLabelButton : public views::LabelButton {
virtual ~TrayPopupLabelButton();
private:
- // Overridden from views::LabelButton:
- virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
-
DISALLOW_COPY_AND_ASSIGN(TrayPopupLabelButton);
};
diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc
index 63fd21505b..f064a5f925 100644
--- a/ash/system/tray_accessibility.cc
+++ b/ash/system/tray_accessibility.cc
@@ -229,7 +229,7 @@ void AccessibilityDetailedView::OnViewClicked(views::View* sender) {
AccessibilityDelegate* delegate =
Shell::GetInstance()->accessibility_delegate();
if (sender == footer()->content()) {
- owner()->system_tray()->ShowDefaultView(BUBBLE_USE_EXISTING);
+ TransitionToDefaultView();
} else if (sender == spoken_feedback_view_) {
delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
} else if (sender == high_contrast_view_) {
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc
index 8d1499dd88..e0d9587351 100644
--- a/ash/system/user/tray_user.cc
+++ b/ash/system/user/tray_user.cc
@@ -481,12 +481,12 @@ PublicAccountUserDetails::PublicAccountUserDetails(SystemTrayItem* owner,
// user.
base::string16 display_name =
Shell::GetInstance()->session_state_delegate()->GetUserDisplayName(0);
- RemoveChars(display_name, kDisplayNameMark, &display_name);
+ base::RemoveChars(display_name, kDisplayNameMark, &display_name);
display_name = kDisplayNameMark[0] + display_name + kDisplayNameMark[0];
// Retrieve the domain managing the device and wrap it with markers.
base::string16 domain = UTF8ToUTF16(
Shell::GetInstance()->system_tray_delegate()->GetEnterpriseDomain());
- RemoveChars(domain, kDisplayNameMark, &domain);
+ base::RemoveChars(domain, kDisplayNameMark, &domain);
base::i18n::WrapStringWithLTRFormatting(&domain);
// Retrieve the label text, inserting the display name and domain.
text_ = l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_PUBLIC_LABEL,
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index 09e615e0a3..b0b75d87df 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -416,6 +416,9 @@ bool WebNotificationTray::ShowPopups() {
}
void WebNotificationTray::HidePopups() {
+ DCHECK(popup_collection_.get());
+
+ popup_collection_->MarkAllPopupsShown();
popup_collection_.reset();
work_area_observer_->StopObserving();
}
diff --git a/ash/system/web_notification/web_notification_tray_unittest.cc b/ash/system/web_notification/web_notification_tray_unittest.cc
index 5ae2e49dea..b64e2b8f7f 100644
--- a/ash/system/web_notification/web_notification_tray_unittest.cc
+++ b/ash/system/web_notification/web_notification_tray_unittest.cc
@@ -208,6 +208,16 @@ TEST_F(WebNotificationTrayTest, WebNotificationPopupBubble) {
// Removing the visible notification should hide the popup bubble.
RemoveNotification("test_id3");
EXPECT_FALSE(GetTray()->IsPopupVisible());
+
+ // Now test that we can show multiple popups and then show the message center.
+ AddNotification("test_id4");
+ AddNotification("test_id5");
+ EXPECT_TRUE(GetTray()->IsPopupVisible());
+
+ GetTray()->message_center_tray_->ShowMessageCenterBubble();
+ GetTray()->message_center_tray_->HideMessageCenterBubble();
+
+ EXPECT_FALSE(GetTray()->IsPopupVisible());
}
using message_center::NotificationList;
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index cd4174a4ee..335972d40b 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -138,8 +138,7 @@ void AshTestBase::SetUp() {
metro_viewer_host_.reset(
new TestMetroViewerProcessHost(ipc_thread_->message_loop_proxy()));
CHECK(metro_viewer_host_->LaunchViewerAndWaitForConnection(
- win8::test::kDefaultTestAppUserModelId,
- L"test_open"));
+ win8::test::kDefaultTestAppUserModelId));
aura::RemoteRootWindowHostWin* root_window_host =
aura::RemoteRootWindowHostWin::Instance();
CHECK(root_window_host != NULL);
diff --git a/ash/test/ash_unittests.cc b/ash/test/ash_unittests.cc
index a49945201c..040e40f33f 100644
--- a/ash/test/ash_unittests.cc
+++ b/ash/test/ash_unittests.cc
@@ -8,8 +8,10 @@
int main(int argc, char** argv) {
ash::test::AuraShellTestSuite test_suite(argc, argv);
- return base::LaunchUnitTests(argc,
- argv,
- base::Bind(&ash::test::AuraShellTestSuite::Run,
- base::Unretained(&test_suite)));
+
+ return base::LaunchUnitTestsSerially(
+ argc,
+ argv,
+ base::Bind(&ash::test::AuraShellTestSuite::Run,
+ base::Unretained(&test_suite)));
}
diff --git a/ash/test/launcher_item_delegate_manager_test_api.h b/ash/test/launcher_item_delegate_manager_test_api.h
deleted file mode 100644
index d916a0558c..0000000000
--- a/ash/test/launcher_item_delegate_manager_test_api.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
-#define ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
-
-#include "base/basictypes.h"
-
-namespace ash {
-
-class LauncherItemDelegateManager;
-
-namespace test {
-
-// Accesses private methods from a LauncherItemDelegateManager for testing.
-class LauncherItemDelegateManagerTestAPI {
- public:
- explicit LauncherItemDelegateManagerTestAPI(
- LauncherItemDelegateManager* manager);
-
- // Clear all exsiting LauncherItemDelegate for test.
- void RemoveAllLauncherItemDelegateForTest();
-
- private:
- LauncherItemDelegateManager* manager_; // Not owned.
-
- DISALLOW_COPY_AND_ASSIGN(LauncherItemDelegateManagerTestAPI);
-};
-
-} // namespace test
-} // namespace ash
-
-#endif // ASH_TEST_LAUNCHER_ITEM_DELEGATE_MANAGER_TEST_API_H_
diff --git a/ash/test/launcher_test_api.cc b/ash/test/launcher_test_api.cc
index 86137f4847..2e56a86231 100644
--- a/ash/test/launcher_test_api.cc
+++ b/ash/test/launcher_test_api.cc
@@ -20,7 +20,7 @@ internal::ShelfView* LauncherTestAPI::shelf_view() {
return launcher_->shelf_view_;
}
-void LauncherTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) {
+void LauncherTestAPI::SetShelfDelegate(ShelfDelegate* delegate) {
launcher_->delegate_ = delegate;
}
diff --git a/ash/test/launcher_test_api.h b/ash/test/launcher_test_api.h
index c680e93639..d83833922e 100644
--- a/ash/test/launcher_test_api.h
+++ b/ash/test/launcher_test_api.h
@@ -10,7 +10,7 @@
namespace ash {
class Launcher;
-class LauncherDelegate;
+class ShelfDelegate;
namespace internal {
class ShelfView;
@@ -28,8 +28,8 @@ class LauncherTestAPI {
// An accessor for |shelf_view|.
internal::ShelfView* shelf_view();
- // Set LauncherDelegate.
- void SetLauncherDelegate(LauncherDelegate* delegate);
+ // Set ShelfDelegate.
+ void SetShelfDelegate(ShelfDelegate* delegate);
private:
Launcher* launcher_;
diff --git a/ash/test/launcher_item_delegate_manager_test_api.cc b/ash/test/shelf_item_delegate_manager_test_api.cc
index 159f7dafa9..740164d692 100644
--- a/ash/test/launcher_item_delegate_manager_test_api.cc
+++ b/ash/test/shelf_item_delegate_manager_test_api.cc
@@ -2,23 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ash/test/launcher_item_delegate_manager_test_api.h"
+#include "ash/test/shelf_item_delegate_manager_test_api.h"
-#include "ash/launcher/launcher_item_delegate.h"
-#include "ash/launcher/launcher_item_delegate_manager.h"
+#include "ash/shelf/shelf_item_delegate.h"
+#include "ash/shelf/shelf_item_delegate_manager.h"
#include "base/stl_util.h"
namespace ash {
namespace test {
-LauncherItemDelegateManagerTestAPI::LauncherItemDelegateManagerTestAPI(
- LauncherItemDelegateManager* manager)
+ShelfItemDelegateManagerTestAPI::ShelfItemDelegateManagerTestAPI(
+ ShelfItemDelegateManager* manager)
: manager_(manager) {
DCHECK(manager_);
}
-void
-LauncherItemDelegateManagerTestAPI::RemoveAllLauncherItemDelegateForTest() {
+void ShelfItemDelegateManagerTestAPI::RemoveAllShelfItemDelegateForTest() {
STLDeleteContainerPairSecondPointers(
manager_->id_to_item_delegate_map_.begin(),
manager_->id_to_item_delegate_map_.end());
diff --git a/ash/test/shelf_item_delegate_manager_test_api.h b/ash/test/shelf_item_delegate_manager_test_api.h
new file mode 100644
index 0000000000..183bab583c
--- /dev/null
+++ b/ash/test/shelf_item_delegate_manager_test_api.h
@@ -0,0 +1,33 @@
+// Copyright 2013 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.
+
+#ifndef ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
+#define ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
+
+#include "base/basictypes.h"
+
+namespace ash {
+
+class ShelfItemDelegateManager;
+
+namespace test {
+
+// Accesses private methods from a ShelfItemDelegateManager for testing.
+class ShelfItemDelegateManagerTestAPI {
+ public:
+ explicit ShelfItemDelegateManagerTestAPI(ShelfItemDelegateManager* manager);
+
+ // Clear all exsiting ShelfItemDelegate for test.
+ void RemoveAllShelfItemDelegateForTest();
+
+ private:
+ ShelfItemDelegateManager* manager_; // Not owned.
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfItemDelegateManagerTestAPI);
+};
+
+} // namespace test
+} // namespace ash
+
+#endif // ASH_TEST_SHELF_ITEM_DELEGATE_MANAGER_TEST_API_H_
diff --git a/ash/test/shelf_view_test_api.cc b/ash/test/shelf_view_test_api.cc
index 568eda9281..b0e0dff17a 100644
--- a/ash/test/shelf_view_test_api.cc
+++ b/ash/test/shelf_view_test_api.cc
@@ -120,7 +120,7 @@ bool ShelfViewTestAPI::SameDragType(LauncherItemType typea,
return shelf_view_->SameDragType(typea, typeb);
}
-void ShelfViewTestAPI::SetLauncherDelegate(LauncherDelegate* delegate) {
+void ShelfViewTestAPI::SetShelfDelegate(ShelfDelegate* delegate) {
shelf_view_->delegate_ = delegate;
}
@@ -128,5 +128,13 @@ gfx::Rect ShelfViewTestAPI::GetBoundsForDragInsertInScreen() {
return shelf_view_->GetBoundsForDragInsertInScreen();
}
+bool ShelfViewTestAPI::IsDraggingShelfItem() {
+ return shelf_view_->dragging();
+}
+
+bool ShelfViewTestAPI::DraggedItemFromOverflowToShelf() {
+ return shelf_view_->dragged_off_from_overflow_to_shelf_;
+}
+
} // namespace test
} // namespace ash
diff --git a/ash/test/shelf_view_test_api.h b/ash/test/shelf_view_test_api.h
index d26c02d984..56ca723913 100644
--- a/ash/test/shelf_view_test_api.h
+++ b/ash/test/shelf_view_test_api.h
@@ -15,7 +15,7 @@ class Size;
namespace ash {
-class LauncherDelegate;
+class ShelfDelegate;
namespace internal {
class OverflowBubble;
@@ -77,12 +77,18 @@ class ShelfViewTestAPI {
// Wrapper for ShelfView::SameDragType.
bool SameDragType(LauncherItemType typea, LauncherItemType typeb) const;
- // Sets LauncherDelegate.
- void SetLauncherDelegate(LauncherDelegate* delegate);
+ // Sets ShelfDelegate.
+ void SetShelfDelegate(ShelfDelegate* delegate);
// Returns re-insertable bounds in screen.
gfx::Rect GetBoundsForDragInsertInScreen();
+ // Returns true if item is ripped off.
+ bool IsDraggingShelfItem();
+
+ // Returns true if an item is ripped off and entered into shelf.
+ bool DraggedItemFromOverflowToShelf();
+
private:
internal::ShelfView* shelf_view_;
diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc
index 353a76ced2..5fe130e8f1 100644
--- a/ash/test/shell_test_api.cc
+++ b/ash/test/shell_test_api.cc
@@ -4,8 +4,8 @@
#include "ash/test/shell_test_api.h"
-#include "ash/launcher/launcher_delegate.h"
#include "ash/root_window_controller.h"
+#include "ash/shelf/shelf_delegate.h"
#include "ash/shell.h"
#if defined(OS_CHROMEOS)
@@ -67,8 +67,8 @@ void ShellTestApi::DisableOutputConfiguratorAnimation() {
#endif // defined(OS_CHROMEOS)
}
-void ShellTestApi::SetLauncherDelegate(LauncherDelegate* delegate) {
- shell_->launcher_delegate_.reset(delegate);
+void ShellTestApi::SetShelfDelegate(ShelfDelegate* delegate) {
+ shell_->shelf_delegate_.reset(delegate);
}
} // namespace test
diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h
index 9457e7511f..269eb78459 100644
--- a/ash/test/shell_test_api.h
+++ b/ash/test/shell_test_api.h
@@ -15,7 +15,7 @@ class InputMethodEventFilter;
namespace ash {
class AshNativeCursorManager;
-class LauncherDelegate;
+class ShelfDelegate;
class ShelfModel;
class Shell;
@@ -47,8 +47,8 @@ public:
void DisableOutputConfiguratorAnimation();
- // Set LauncherDelegate.
- void SetLauncherDelegate(LauncherDelegate* delegate);
+ // Set ShelfDelegate.
+ void SetShelfDelegate(ShelfDelegate* delegate);
private:
Shell* shell_; // not owned
diff --git a/ash/test/test_launcher_delegate.cc b/ash/test/test_launcher_delegate.cc
deleted file mode 100644
index 67e8be3b58..0000000000
--- a/ash/test/test_launcher_delegate.cc
+++ /dev/null
@@ -1,111 +0,0 @@
-// 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/test/test_launcher_delegate.h"
-
-#include "ash/launcher/launcher_item_delegate_manager.h"
-#include "ash/shelf/shelf_model.h"
-#include "ash/shelf/shelf_util.h"
-#include "ash/shell.h"
-#include "ash/test/test_launcher_item_delegate.h"
-#include "base/strings/string_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "grit/ash_resources.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace test {
-
-TestLauncherDelegate* TestLauncherDelegate::instance_ = NULL;
-
-TestLauncherDelegate::TestLauncherDelegate(ShelfModel* model)
- : model_(model) {
- CHECK(!instance_);
- instance_ = this;
-}
-
-TestLauncherDelegate::~TestLauncherDelegate() {
- instance_ = NULL;
-}
-
-void TestLauncherDelegate::AddLauncherItem(aura::Window* window) {
- AddLauncherItem(window, STATUS_CLOSED);
-}
-
-void TestLauncherDelegate::AddLauncherItem(
- aura::Window* window,
- LauncherItemStatus status) {
- ash::LauncherItem item;
- if (window->type() == aura::client::WINDOW_TYPE_PANEL)
- item.type = ash::TYPE_APP_PANEL;
- else
- item.type = ash::TYPE_PLATFORM_APP;
- LauncherID id = model_->next_id();
- item.status = status;
- model_->Add(item);
- window->AddObserver(this);
-
- ash::LauncherItemDelegateManager* manager =
- ash::Shell::GetInstance()->launcher_item_delegate_manager();
- // |manager| owns TestLauncherItemDelegate.
- scoped_ptr<LauncherItemDelegate> delegate(
- new TestLauncherItemDelegate(window));
- manager->SetLauncherItemDelegate(id, delegate.Pass());
- SetLauncherIDForWindow(id, window);
-}
-
-void TestLauncherDelegate::RemoveLauncherItemForWindow(aura::Window* window) {
- ash::LauncherID id = GetLauncherIDForWindow(window);
- if (id == 0)
- return;
- int index = model_->ItemIndexByID(id);
- DCHECK_NE(-1, index);
- model_->RemoveItemAt(index);
- window->RemoveObserver(this);
-}
-
-void TestLauncherDelegate::OnWindowDestroying(aura::Window* window) {
- RemoveLauncherItemForWindow(window);
-}
-
-void TestLauncherDelegate::OnWindowHierarchyChanging(
- const HierarchyChangeParams& params) {
- // The window may be legitimately reparented while staying open if it moves
- // to another display or container. If the window does not have a new parent
- // then remove the launcher item.
- if (!params.new_parent)
- RemoveLauncherItemForWindow(params.target);
-}
-
-void TestLauncherDelegate::OnLauncherCreated(Launcher* launcher) {
-}
-
-void TestLauncherDelegate::OnLauncherDestroyed(Launcher* launcher) {
-}
-
-LauncherID TestLauncherDelegate::GetLauncherIDForAppID(
- const std::string& app_id) {
- return 0;
-}
-
-const std::string& TestLauncherDelegate::GetAppIDForLauncherID(LauncherID id) {
- return EmptyString();
-}
-
-void TestLauncherDelegate::PinAppWithID(const std::string& app_id) {
-}
-
-bool TestLauncherDelegate::CanPin() const {
- return true;
-}
-
-bool TestLauncherDelegate::IsAppPinned(const std::string& app_id) {
- return false;
-}
-
-void TestLauncherDelegate::UnpinAppWithID(const std::string& app_id) {
-}
-
-} // namespace test
-} // namespace ash
diff --git a/ash/test/test_launcher_item_delegate.cc b/ash/test/test_launcher_item_delegate.cc
deleted file mode 100644
index 3913c6841c..0000000000
--- a/ash/test/test_launcher_item_delegate.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2013 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/test/test_launcher_item_delegate.h"
-
-#include "ash/wm/window_util.h"
-#include "ui/aura/window.h"
-
-namespace ash {
-namespace test {
-
-TestLauncherItemDelegate::TestLauncherItemDelegate(aura::Window* window)
- : window_(window) {
-}
-
-TestLauncherItemDelegate::~TestLauncherItemDelegate() {
-}
-
-bool TestLauncherItemDelegate::ItemSelected(const ui::Event& event) {
- if (window_) {
- if (window_->type() == aura::client::WINDOW_TYPE_PANEL)
- ash::wm::MoveWindowToEventRoot(window_, event);
- window_->Show();
- ash::wm::ActivateWindow(window_);
- }
- return false;
-}
-
-base::string16 TestLauncherItemDelegate::GetTitle() {
- return window_ ? window_->title() : base::string16();
-}
-
-ui::MenuModel* TestLauncherItemDelegate::CreateContextMenu(
- aura::Window* root_window) {
- return NULL;
-}
-
-ash::LauncherMenuModel* TestLauncherItemDelegate::CreateApplicationMenu(
- int event_flags) {
- return NULL;
-}
-
-bool TestLauncherItemDelegate::IsDraggable() {
- return true;
-}
-
-bool TestLauncherItemDelegate::ShouldShowTooltip() {
- return true;
-}
-
-} // namespace test
-} // namespace ash
diff --git a/ash/test/test_launcher_item_delegate.h b/ash/test/test_launcher_item_delegate.h
deleted file mode 100644
index a2f631044f..0000000000
--- a/ash/test/test_launcher_item_delegate.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
-#define ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
-
-#include "ash/launcher/launcher_item_delegate.h"
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-
-namespace aura {
-class Window;
-}
-
-namespace ash {
-namespace test {
-
-// Test implementation of ash::LauncherItemDelegate.
-class TestLauncherItemDelegate : public ash::LauncherItemDelegate {
- public:
- explicit TestLauncherItemDelegate(aura::Window* window);
- virtual ~TestLauncherItemDelegate();
-
- // ash::LauncherItemDelegate overrides:
- virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
- virtual base::string16 GetTitle() OVERRIDE;
- virtual ui::MenuModel* CreateContextMenu(
- aura::Window* root_window) OVERRIDE;
- virtual ash::LauncherMenuModel* CreateApplicationMenu(
- int event_flags) OVERRIDE;
- virtual bool IsDraggable() OVERRIDE;
- virtual bool ShouldShowTooltip() OVERRIDE;
-
- private:
- aura::Window* window_;
-
- DISALLOW_COPY_AND_ASSIGN(TestLauncherItemDelegate);
-};
-
-} // namespace test
-} // namespace ash
-
-#endif // ASH_TEST_TEST_LAUNCHER_ITEM_DELEGATE_
diff --git a/ash/test/test_metro_viewer_process_host.cc b/ash/test/test_metro_viewer_process_host.cc
index 3a58433a4b..a5e3fbfa40 100644
--- a/ash/test/test_metro_viewer_process_host.cc
+++ b/ash/test/test_metro_viewer_process_host.cc
@@ -42,5 +42,9 @@ void TestMetroViewerProcessHost::OnHandleSearchRequest(
const string16& search_string) {
}
+void TestMetroViewerProcessHost::OnWindowSizeChanged(uint32 width,
+ uint32 height) {
+}
+
} // namespace test
} // namespace ash
diff --git a/ash/test/test_metro_viewer_process_host.h b/ash/test/test_metro_viewer_process_host.h
index e6d0a648cd..7c3770e38a 100644
--- a/ash/test/test_metro_viewer_process_host.h
+++ b/ash/test/test_metro_viewer_process_host.h
@@ -26,6 +26,7 @@ class TestMetroViewerProcessHost : public win8::MetroViewerProcessHost {
virtual void OnSetTargetSurface(gfx::NativeViewId target_surface) OVERRIDE;
virtual void OnOpenURL(const string16& url) OVERRIDE;
virtual void OnHandleSearchRequest(const string16& search_string) OVERRIDE;
+ virtual void OnWindowSizeChanged(uint32 width, uint32 height) OVERRIDE;
scoped_ptr<AcceleratedSurface> backing_surface_;
diff --git a/ash/test/test_session_state_delegate.cc b/ash/test/test_session_state_delegate.cc
index 8032f4ff44..5c168078b4 100644
--- a/ash/test/test_session_state_delegate.cc
+++ b/ash/test/test_session_state_delegate.cc
@@ -147,7 +147,7 @@ void TestSessionStateDelegate::SwitchActiveUser(const std::string& user_id) {
activated_user_ = user_id;
}
-void TestSessionStateDelegate::SwitchActiveUserToNext() {
+void TestSessionStateDelegate::CycleActiveUser(CycleUser cycle_user) {
activated_user_ = "someone@tray";
}
diff --git a/ash/test/test_session_state_delegate.h b/ash/test/test_session_state_delegate.h
index fcd375d2fe..8c26c8884d 100644
--- a/ash/test/test_session_state_delegate.h
+++ b/ash/test/test_session_state_delegate.h
@@ -41,7 +41,7 @@ class TestSessionStateDelegate : public SessionStateDelegate {
ash::MultiProfileIndex index) const OVERRIDE;
virtual void GetLoggedInUsers(UserIdList* users) OVERRIDE;
virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE;
- virtual void SwitchActiveUserToNext() OVERRIDE;
+ virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE;
virtual void AddSessionStateObserver(
ash::SessionStateObserver* observer) OVERRIDE;
virtual void RemoveSessionStateObserver(
diff --git a/ash/test/test_shelf_delegate.cc b/ash/test/test_shelf_delegate.cc
new file mode 100644
index 0000000000..c880d2433f
--- /dev/null
+++ b/ash/test/test_shelf_delegate.cc
@@ -0,0 +1,108 @@
+// Copyright 2013 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/test/test_shelf_delegate.h"
+
+#include "ash/shelf/shelf_item_delegate_manager.h"
+#include "ash/shelf/shelf_model.h"
+#include "ash/shelf/shelf_util.h"
+#include "ash/shell.h"
+#include "ash/test/test_shelf_item_delegate.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "grit/ash_resources.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace test {
+
+TestShelfDelegate* TestShelfDelegate::instance_ = NULL;
+
+TestShelfDelegate::TestShelfDelegate(ShelfModel* model)
+ : model_(model) {
+ CHECK(!instance_);
+ instance_ = this;
+}
+
+TestShelfDelegate::~TestShelfDelegate() {
+ instance_ = NULL;
+}
+
+void TestShelfDelegate::AddLauncherItem(aura::Window* window) {
+ AddLauncherItem(window, STATUS_CLOSED);
+}
+
+void TestShelfDelegate::AddLauncherItem(aura::Window* window,
+ LauncherItemStatus status) {
+ LauncherItem item;
+ if (window->type() == aura::client::WINDOW_TYPE_PANEL)
+ item.type = TYPE_APP_PANEL;
+ else
+ item.type = TYPE_PLATFORM_APP;
+ LauncherID id = model_->next_id();
+ item.status = status;
+ model_->Add(item);
+ window->AddObserver(this);
+
+ ShelfItemDelegateManager* manager =
+ Shell::GetInstance()->shelf_item_delegate_manager();
+ // |manager| owns TestShelfItemDelegate.
+ scoped_ptr<ShelfItemDelegate> delegate(new TestShelfItemDelegate(window));
+ manager->SetShelfItemDelegate(id, delegate.Pass());
+ SetLauncherIDForWindow(id, window);
+}
+
+void TestShelfDelegate::RemoveLauncherItemForWindow(aura::Window* window) {
+ LauncherID id = GetLauncherIDForWindow(window);
+ if (id == 0)
+ return;
+ int index = model_->ItemIndexByID(id);
+ DCHECK_NE(-1, index);
+ model_->RemoveItemAt(index);
+ window->RemoveObserver(this);
+}
+
+void TestShelfDelegate::OnWindowDestroying(aura::Window* window) {
+ RemoveLauncherItemForWindow(window);
+}
+
+void TestShelfDelegate::OnWindowHierarchyChanging(
+ const HierarchyChangeParams& params) {
+ // The window may be legitimately reparented while staying open if it moves
+ // to another display or container. If the window does not have a new parent
+ // then remove the launcher item.
+ if (!params.new_parent)
+ RemoveLauncherItemForWindow(params.target);
+}
+
+void TestShelfDelegate::OnLauncherCreated(Launcher* launcher) {
+}
+
+void TestShelfDelegate::OnLauncherDestroyed(Launcher* launcher) {
+}
+
+LauncherID TestShelfDelegate::GetLauncherIDForAppID(const std::string& app_id) {
+ return 0;
+}
+
+const std::string& TestShelfDelegate::GetAppIDForLauncherID(LauncherID id) {
+ return base::EmptyString();
+}
+
+void TestShelfDelegate::PinAppWithID(const std::string& app_id) {
+}
+
+bool TestShelfDelegate::CanPin() const {
+ return true;
+}
+
+bool TestShelfDelegate::IsAppPinned(const std::string& app_id) {
+ return false;
+}
+
+void TestShelfDelegate::UnpinAppWithID(const std::string& app_id) {
+}
+
+} // namespace test
+} // namespace ash
diff --git a/ash/test/test_launcher_delegate.h b/ash/test/test_shelf_delegate.h
index 11328bc5b2..e47545349b 100644
--- a/ash/test/test_launcher_delegate.h
+++ b/ash/test/test_shelf_delegate.h
@@ -1,14 +1,14 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2013 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.
-#ifndef ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
-#define ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
+#ifndef ASH_TEST_TEST_SHELF_DELEGATE_H_
+#define ASH_TEST_TEST_SHELF_DELEGATE_H_
#include <map>
#include <set>
-#include "ash/launcher/launcher_delegate.h"
+#include "ash/shelf/shelf_delegate.h"
#include "base/compiler_specific.h"
#include "ui/aura/window_observer.h"
@@ -18,26 +18,25 @@ class ShelfModel;
namespace test {
-// Test implementation of LauncherDelegate.
+// Test implementation of ShelfDelegate.
// Tests may create icons for windows by calling AddLauncherItem
-class TestLauncherDelegate : public LauncherDelegate,
- public aura::WindowObserver {
+class TestShelfDelegate : public ShelfDelegate, public aura::WindowObserver {
public:
- explicit TestLauncherDelegate(ShelfModel* model);
- virtual ~TestLauncherDelegate();
+ explicit TestShelfDelegate(ShelfModel* model);
+ virtual ~TestShelfDelegate();
void AddLauncherItem(aura::Window* window);
void AddLauncherItem(aura::Window* window, LauncherItemStatus status);
void RemoveLauncherItemForWindow(aura::Window* window);
- static TestLauncherDelegate* instance() { return instance_; }
+ static TestShelfDelegate* instance() { return instance_; }
// WindowObserver implementation
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
virtual void OnWindowHierarchyChanging(
const HierarchyChangeParams& params) OVERRIDE;
- // LauncherDelegate implementation.
+ // ShelfDelegate implementation.
virtual void OnLauncherCreated(Launcher* launcher) OVERRIDE;
virtual void OnLauncherDestroyed(Launcher* launcher) OVERRIDE;
virtual LauncherID GetLauncherIDForAppID(const std::string& app_id) OVERRIDE;
@@ -48,14 +47,14 @@ class TestLauncherDelegate : public LauncherDelegate,
virtual void UnpinAppWithID(const std::string& app_id) OVERRIDE;
private:
- static TestLauncherDelegate* instance_;
+ static TestShelfDelegate* instance_;
ShelfModel* model_;
- DISALLOW_COPY_AND_ASSIGN(TestLauncherDelegate);
+ DISALLOW_COPY_AND_ASSIGN(TestShelfDelegate);
};
} // namespace test
} // namespace ash
-#endif // ASH_TEST_TEST_LAUNCHER_DELEGATE_H_
+#endif // ASH_TEST_TEST_SHELF_DELEGATE_H_
diff --git a/ash/test/test_shelf_item_delegate.cc b/ash/test/test_shelf_item_delegate.cc
new file mode 100644
index 0000000000..b09d9d79d4
--- /dev/null
+++ b/ash/test/test_shelf_item_delegate.cc
@@ -0,0 +1,52 @@
+// Copyright 2013 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/test/test_shelf_item_delegate.h"
+
+#include "ash/wm/window_util.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace test {
+
+TestShelfItemDelegate::TestShelfItemDelegate(aura::Window* window)
+ : window_(window) {
+}
+
+TestShelfItemDelegate::~TestShelfItemDelegate() {
+}
+
+bool TestShelfItemDelegate::ItemSelected(const ui::Event& event) {
+ if (window_) {
+ if (window_->type() == aura::client::WINDOW_TYPE_PANEL)
+ wm::MoveWindowToEventRoot(window_, event);
+ window_->Show();
+ wm::ActivateWindow(window_);
+ }
+ return false;
+}
+
+base::string16 TestShelfItemDelegate::GetTitle() {
+ return window_ ? window_->title() : base::string16();
+}
+
+ui::MenuModel* TestShelfItemDelegate::CreateContextMenu(
+ aura::Window* root_window) {
+ return NULL;
+}
+
+ShelfMenuModel* TestShelfItemDelegate::CreateApplicationMenu(int event_flags) {
+ return NULL;
+}
+
+bool TestShelfItemDelegate::IsDraggable() {
+ return true;
+}
+
+bool TestShelfItemDelegate::ShouldShowTooltip() {
+ return true;
+}
+
+} // namespace test
+} // namespace ash
diff --git a/ash/test/test_shelf_item_delegate.h b/ash/test/test_shelf_item_delegate.h
new file mode 100644
index 0000000000..af8a26ade0
--- /dev/null
+++ b/ash/test/test_shelf_item_delegate.h
@@ -0,0 +1,42 @@
+// Copyright 2013 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.
+
+#ifndef ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
+#define ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
+
+#include "ash/shelf/shelf_item_delegate.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+namespace test {
+
+// Test implementation of ShelfItemDelegate.
+class TestShelfItemDelegate : public ShelfItemDelegate {
+ public:
+ explicit TestShelfItemDelegate(aura::Window* window);
+ virtual ~TestShelfItemDelegate();
+
+ // ShelfItemDelegate:
+ virtual bool ItemSelected(const ui::Event& event) OVERRIDE;
+ virtual base::string16 GetTitle() OVERRIDE;
+ virtual ui::MenuModel* CreateContextMenu(aura::Window* root_window) OVERRIDE;
+ virtual ShelfMenuModel* CreateApplicationMenu(int event_flags) OVERRIDE;
+ virtual bool IsDraggable() OVERRIDE;
+ virtual bool ShouldShowTooltip() OVERRIDE;
+
+ private:
+ aura::Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestShelfItemDelegate);
+};
+
+} // namespace test
+} // namespace ash
+
+#endif // ASH_TEST_TEST_SHELF_ITEM_DELEGATE_H_
diff --git a/ash/test/test_shell_delegate.cc b/ash/test/test_shell_delegate.cc
index 40284d9f5b..fcf9562031 100644
--- a/ash/test/test_shell_delegate.cc
+++ b/ash/test/test_shell_delegate.cc
@@ -15,8 +15,8 @@
#include "ash/shell.h"
#include "ash/shell/keyboard_controller_proxy_stub.h"
#include "ash/shell_window_ids.h"
-#include "ash/test/test_launcher_delegate.h"
#include "ash/test/test_session_state_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/test/test_system_tray_delegate.h"
#include "ash/test/test_user_wallpaper_delegate.h"
#include "ash/wm/window_state.h"
@@ -101,8 +101,8 @@ app_list::AppListViewDelegate* TestShellDelegate::CreateAppListViewDelegate() {
return new app_list::test::AppListTestViewDelegate;
}
-LauncherDelegate* TestShellDelegate::CreateLauncherDelegate(ShelfModel* model) {
- return new TestLauncherDelegate(model);
+ShelfDelegate* TestShellDelegate::CreateShelfDelegate(ShelfModel* model) {
+ return new TestShelfDelegate(model);
}
SystemTrayDelegate* TestShellDelegate::CreateSystemTrayDelegate() {
diff --git a/ash/test/test_shell_delegate.h b/ash/test/test_shell_delegate.h
index 3edd95cc2c..1a34462b09 100644
--- a/ash/test/test_shell_delegate.h
+++ b/ash/test/test_shell_delegate.h
@@ -41,7 +41,7 @@ class TestShellDelegate : public ShellDelegate {
CreateKeyboardControllerProxy() OVERRIDE;
virtual content::BrowserContext* GetCurrentBrowserContext() OVERRIDE;
virtual app_list::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
- virtual LauncherDelegate* CreateLauncherDelegate(ShelfModel* model) OVERRIDE;
+ virtual ShelfDelegate* CreateShelfDelegate(ShelfModel* model) OVERRIDE;
virtual SystemTrayDelegate* CreateSystemTrayDelegate() OVERRIDE;
virtual UserWallpaperDelegate* CreateUserWallpaperDelegate() OVERRIDE;
virtual CapsLockDelegate* CreateCapsLockDelegate() OVERRIDE;
diff --git a/ash/touch/touch_uma.cc b/ash/touch/touch_uma.cc
index 29f61850ed..730911f7bd 100644
--- a/ash/touch/touch_uma.cc
+++ b/ash/touch/touch_uma.cc
@@ -55,8 +55,10 @@ enum UMAEventType {
UMA_ET_GESTURE_PINCH_UPDATE_3,
UMA_ET_GESTURE_PINCH_UPDATE_4P,
UMA_ET_GESTURE_LONG_TAP,
+ UMA_ET_GESTURE_SHOW_PRESS,
+ UMA_ET_GESTURE_TAP_CANCEL,
// NOTE: Add new event types only immediately above this line. Make sure to
- // update the enum list in tools/histogram/histograms.xml accordingly.
+ // update the enum list in tools/metrics/histogram/histograms.xml accordingly.
UMA_ET_COUNT
};
@@ -157,6 +159,10 @@ UMAEventType UMAEventTypeFromEvent(const ui::Event& event) {
return UMA_ET_GESTURE_MULTIFINGER_SWIPE_3;
return UMA_ET_GESTURE_MULTIFINGER_SWIPE;
}
+ case ui::ET_GESTURE_TAP_CANCEL:
+ return UMA_ET_GESTURE_TAP_CANCEL;
+ case ui::ET_GESTURE_SHOW_PRESS:
+ return UMA_ET_GESTURE_SHOW_PRESS;
case ui::ET_SCROLL:
return UMA_ET_SCROLL;
case ui::ET_SCROLL_FLING_START:
@@ -164,6 +170,7 @@ UMAEventType UMAEventTypeFromEvent(const ui::Event& event) {
case ui::ET_SCROLL_FLING_CANCEL:
return UMA_ET_SCROLL_FLING_CANCEL;
default:
+ NOTREACHED();
return UMA_ET_UNKNOWN;
}
}
@@ -292,7 +299,7 @@ void TouchUMA::RecordTouchEvent(aura::Window* target,
if (details->last_start_time_.count(event.touch_id())) {
base::TimeDelta duration = event.time_stamp() -
details->last_start_time_[event.touch_id()];
- UMA_HISTOGRAM_COUNTS_100("Ash.TouchDuration", duration.InMilliseconds());
+ UMA_HISTOGRAM_TIMES("Ash.TouchDuration", duration);
// Look for touches that were [almost] stationary for a long time.
const double kLongStationaryTouchDuration = 10;
diff --git a/ash/wm/app_list_controller.cc b/ash/wm/app_list_controller.cc
index df3f724e60..2f0ce0420f 100644
--- a/ash/wm/app_list_controller.cc
+++ b/ash/wm/app_list_controller.cc
@@ -290,6 +290,10 @@ void AppListController::ProcessLocatedEvent(ui::LocatedEvent* event) {
ash::internal::kShellWindowId_MenuContainer);
if (menu_container->Contains(target))
return;
+ aura::Window* keyboard_container = root_controller->GetContainer(
+ ash::internal::kShellWindowId_VirtualKeyboardContainer);
+ if (keyboard_container->Contains(target))
+ return;
}
}
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.cc b/ash/wm/caption_buttons/alternate_frame_size_button.cc
new file mode 100644
index 0000000000..3c5669f532
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button.cc
@@ -0,0 +1,211 @@
+// Copyright 2013 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/caption_buttons/alternate_frame_size_button.h"
+
+#include "ash/shell.h"
+#include "ash/shell_delegate.h"
+#include "ash/touch/touch_uma.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/workspace/snap_sizer.h"
+#include "ui/gfx/vector2d.h"
+#include "ui/views/widget/widget.h"
+
+namespace {
+
+// The default delay between the user pressing the size button and the buttons
+// adjacent to the size button morphing into buttons for snapping left and
+// right.
+const int kSetButtonsToSnapModeDelayMs = 150;
+
+// The amount that a user can overshoot the snap left / snap right button and
+// keep the snap left / snap right button pressed.
+const int kPressedHitBoundsExpandX = 200;
+const int kPressedHitBoundsExpandY = 50;
+
+} // namespace
+
+namespace ash {
+
+AlternateFrameSizeButton::AlternateFrameSizeButton(
+ views::ButtonListener* listener,
+ views::Widget* frame,
+ AlternateFrameSizeButtonDelegate* delegate)
+ : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
+ frame_(frame),
+ delegate_(delegate),
+ set_buttons_to_snap_mode_delay_ms_(kSetButtonsToSnapModeDelayMs),
+ in_snap_mode_(false),
+ snap_type_(SNAP_NONE) {
+}
+
+AlternateFrameSizeButton::~AlternateFrameSizeButton() {
+}
+
+bool AlternateFrameSizeButton::OnMousePressed(const ui::MouseEvent& event) {
+ // The minimize and close buttons are set to snap left and right when snapping
+ // is enabled. Do not enable snapping if the minimize button is not visible.
+ // The close button is always visible.
+ if (IsTriggerableEvent(event) &&
+ !in_snap_mode_ &&
+ delegate_->IsMinimizeButtonVisible()) {
+ StartSetButtonsToSnapModeTimer(event);
+ }
+ FrameCaptionButton::OnMousePressed(event);
+ return true;
+}
+
+bool AlternateFrameSizeButton::OnMouseDragged(const ui::MouseEvent& event) {
+ UpdatePressedButton(event);
+ FrameCaptionButton::OnMouseDragged(event);
+ return true;
+}
+
+void AlternateFrameSizeButton::OnMouseReleased(const ui::MouseEvent& event) {
+ if (!IsTriggerableEvent(event) || !CommitSnap(event))
+ FrameCaptionButton::OnMouseReleased(event);
+}
+
+void AlternateFrameSizeButton::OnMouseCaptureLost() {
+ SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+ FrameCaptionButton::OnMouseCaptureLost();
+}
+
+void AlternateFrameSizeButton::OnGestureEvent(ui::GestureEvent* event) {
+ if (event->details().touch_points() > 1) {
+ SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+ return;
+ }
+
+ if (event->type() == ui::ET_GESTURE_TAP_DOWN) {
+ StartSetButtonsToSnapModeTimer(*event);
+ // Go through FrameCaptionButton's handling so that the button gets pressed.
+ FrameCaptionButton::OnGestureEvent(event);
+ return;
+ }
+
+ if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+ event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+ UpdatePressedButton(*event);
+ event->SetHandled();
+ return;
+ }
+
+ if (event->type() == ui::ET_GESTURE_TAP ||
+ event->type() == ui::ET_GESTURE_SCROLL_END ||
+ event->type() == ui::ET_SCROLL_FLING_START ||
+ event->type() == ui::ET_GESTURE_END) {
+ if (CommitSnap(*event)) {
+ if (event->type() == ui::ET_GESTURE_TAP) {
+ TouchUMA::GetInstance()->RecordGestureAction(
+ TouchUMA::GESTURE_FRAMEMAXIMIZE_TAP);
+ }
+ event->SetHandled();
+ return;
+ }
+ }
+
+ FrameCaptionButton::OnGestureEvent(event);
+}
+
+void AlternateFrameSizeButton::StartSetButtonsToSnapModeTimer(
+ const ui::LocatedEvent& event) {
+ set_buttons_to_snap_mode_timer_event_location_ = event.location();
+ if (set_buttons_to_snap_mode_delay_ms_ == 0) {
+ SetButtonsToSnapMode();
+ } else {
+ set_buttons_to_snap_mode_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(set_buttons_to_snap_mode_delay_ms_),
+ this,
+ &AlternateFrameSizeButton::SetButtonsToSnapMode);
+ }
+}
+
+void AlternateFrameSizeButton::SetButtonsToSnapMode() {
+ if (in_snap_mode_)
+ return;
+ in_snap_mode_ = true;
+ delegate_->SetButtonIcons(CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+ CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+ AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+}
+
+void AlternateFrameSizeButton::UpdatePressedButton(
+ const ui::LocatedEvent& event) {
+ if (!in_snap_mode_) {
+ // Set the buttons adjacent to the size button to snap left and right early
+ // if the user drags past the drag threshold.
+ // |set_buttons_to_snap_mode_timer_| is checked to avoid entering the snap
+ // mode as a result of an unsupported drag type (e.g. only the right mouse
+ // button is pressed).
+ gfx::Vector2d delta(
+ event.location() - set_buttons_to_snap_mode_timer_event_location_);
+ if (!set_buttons_to_snap_mode_timer_.IsRunning() ||
+ !views::View::ExceededDragThreshold(delta)) {
+ return;
+ }
+ SetButtonsToSnapMode();
+ }
+
+ gfx::Point event_location_in_screen(event.location());
+ views::View::ConvertPointToScreen(this, &event_location_in_screen);
+
+ gfx::Insets pressed_button_hittest_insets(-kPressedHitBoundsExpandY,
+ -kPressedHitBoundsExpandX,
+ -kPressedHitBoundsExpandY,
+ -kPressedHitBoundsExpandX);
+ const FrameCaptionButton* pressed_button = delegate_->PressButtonAt(
+ event_location_in_screen, pressed_button_hittest_insets);
+ snap_type_ = SNAP_NONE;
+ if (pressed_button) {
+ switch (pressed_button->icon()) {
+ case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
+ snap_type_ = SNAP_LEFT;
+ break;
+ case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
+ snap_type_ = SNAP_RIGHT;
+ break;
+ case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
+ // snap_type_ = SNAP_NONE
+ break;
+ case CAPTION_BUTTON_ICON_MINIMIZE:
+ case CAPTION_BUTTON_ICON_CLOSE:
+ NOTREACHED();
+ break;
+ }
+ }
+}
+
+bool AlternateFrameSizeButton::CommitSnap(const ui::LocatedEvent& event) {
+ // The position of |event| may be different than the position of the previous
+ // event.
+ UpdatePressedButton(event);
+
+ if (in_snap_mode_ &&
+ (snap_type_ == SNAP_LEFT || snap_type_ == SNAP_RIGHT)) {
+ using internal::SnapSizer;
+ SnapSizer::SnapWindow(ash::wm::GetWindowState(frame_->GetNativeWindow()),
+ snap_type_ == SNAP_LEFT ?
+ SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE);
+ ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(
+ snap_type_ == SNAP_LEFT ?
+ ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
+ ash::UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_RIGHT);
+ SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_NO);
+ return true;
+ }
+ SetButtonsToNormalMode(AlternateFrameSizeButtonDelegate::ANIMATE_YES);
+ return false;
+}
+
+void AlternateFrameSizeButton::SetButtonsToNormalMode(
+ AlternateFrameSizeButtonDelegate::Animate animate) {
+ in_snap_mode_ = false;
+ snap_type_ = SNAP_NONE;
+ set_buttons_to_snap_mode_timer_.Stop();
+ delegate_->SetButtonsToNormal(animate);
+}
+
+} // namespace ash
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button.h b/ash/wm/caption_buttons/alternate_frame_size_button.h
new file mode 100644
index 0000000000..586eabbeb9
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button.h
@@ -0,0 +1,100 @@
+// Copyright 2013 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.
+
+#ifndef ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
+#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
+#include "ash/wm/workspace/snap_types.h"
+#include "base/timer/timer.h"
+
+namespace views {
+class Widget;
+}
+
+namespace ash {
+class AlternateFrameSizeButtonDelegate;
+
+// The maximize/restore button when using the alternate button style.
+// When the mouse is pressed over the size button or the size button is touched:
+// - The minimize and close buttons are set to snap left and snap right
+// respectively.
+// - The pressed button is updated during the drag to reflect the button
+// underneath the mouse cursor. (The size button is potentially unpressed).
+// When the drag terminates, the action for the pressed button is executed.
+// For the sake of simplicity, the size button is the event handler for a click
+// starting on the size button and the entire drag (including when the size
+// button is unpressed).
+class ASH_EXPORT AlternateFrameSizeButton : public FrameCaptionButton {
+ public:
+ AlternateFrameSizeButton(views::ButtonListener* listener,
+ views::Widget* frame,
+ AlternateFrameSizeButtonDelegate* delegate);
+
+ virtual ~AlternateFrameSizeButton();
+
+ // views::CustomButton overrides:
+ virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
+ virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseCaptureLost() OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ void set_delay_to_set_buttons_to_snap_mode(int delay_ms) {
+ set_buttons_to_snap_mode_delay_ms_ = delay_ms;
+ }
+
+ private:
+ // Starts |set_buttons_to_snap_mode_timer_|.
+ void StartSetButtonsToSnapModeTimer(const ui::LocatedEvent& event);
+
+ // Sets the buttons adjacent to the size button to snap left and right.
+ void SetButtonsToSnapMode();
+
+ // Updates the pressed button based on |event_location|.
+ void UpdatePressedButton(const ui::LocatedEvent& event);
+
+ // Snaps |frame_| according to |snap_type_|. Returns true if |frame_| was
+ // snapped.
+ bool CommitSnap(const ui::LocatedEvent& event);
+
+ // Sets the buttons adjacent to the size button to minimize and close again.
+ // Clears any state set while snapping was enabled. |animate| indicates
+ // whether the buttons should animate back to their original icons.
+ void SetButtonsToNormalMode(
+ AlternateFrameSizeButtonDelegate::Animate animate);
+
+ // Widget that the size button acts on.
+ views::Widget* frame_;
+
+ // Not owned.
+ AlternateFrameSizeButtonDelegate* delegate_;
+
+ // Location of the event which started |set_buttons_to_snap_mode_timer_| in
+ // view coordinates.
+ gfx::Point set_buttons_to_snap_mode_timer_event_location_;
+
+ // The delay between the user pressing the size button and the buttons
+ // adjacent to the size button morphing into buttons for snapping left and
+ // right.
+ int set_buttons_to_snap_mode_delay_ms_;
+
+ base::OneShotTimer<AlternateFrameSizeButton> set_buttons_to_snap_mode_timer_;
+
+ // Whether the buttons adjacent to the size button snap the window left and
+ // right.
+ bool in_snap_mode_;
+
+ // The action of the currently pressed button. If |snap_type_| == SNAP_NONE,
+ // the size button's default action is run when clicked.
+ SnapType snap_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButton);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_H_
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h
new file mode 100644
index 0000000000..c1472b21bf
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button_delegate.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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.
+
+#ifndef ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
+#define ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+
+namespace gfx {
+class Insets;
+class Point;
+}
+
+namespace ash {
+class FrameCaptionButton;
+
+// Delegate interface for AlternateFrameSizeButton.
+class ASH_EXPORT AlternateFrameSizeButtonDelegate {
+ public:
+ enum Animate {
+ ANIMATE_YES,
+ ANIMATE_NO
+ };
+
+ // Returns whether the minimize button is visible.
+ virtual bool IsMinimizeButtonVisible() const = 0;
+
+ // Reset the caption button views::Button::ButtonState back to normal. If
+ // |animate| is ANIMATE_YES, the buttons will crossfade back to their
+ // original icons.
+ virtual void SetButtonsToNormal(Animate animate) = 0;
+
+ // Sets the minimize and close button icons. The buttons will crossfade to
+ // their new icons if |animate| is ANIMATE_YES.
+ virtual void SetButtonIcons(CaptionButtonIcon left_button_action,
+ CaptionButtonIcon right_button_action,
+ Animate animate) = 0;
+
+ // Presses the button at |position_in_screen| and unpresses any other pressed
+ // caption buttons.
+ // |pressed_button_hittest_insets| indicates how much the hittest insets for
+ // the currently pressed button should be expanded if no button was found at
+ // |position_in_screen| using the normal button hittest insets.
+ // Returns the button which was pressed.
+ virtual const FrameCaptionButton* PressButtonAt(
+ const gfx::Point& position_in_screen,
+ const gfx::Insets& pressed_button_hittest_insets) const = 0;
+
+ protected:
+ virtual ~AlternateFrameSizeButtonDelegate() {}
+};
+
+} // namespace ash
+
+#endif // ASH_WM_CAPTION_BUTTONS_ALTERNATE_FRAME_SIZE_BUTTON_DELEGATE_H_
diff --git a/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc
new file mode 100644
index 0000000000..75c0f50604
--- /dev/null
+++ b/ash/wm/caption_buttons/alternate_frame_size_button_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright 2013 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/caption_buttons/alternate_frame_size_button.h"
+
+#include "ash/ash_switches.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
+#include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/workspace/snap_sizer.h"
+#include "base/command_line.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/window.h"
+#include "ui/events/gestures/gesture_configuration.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+class TestWidgetDelegate : public views::WidgetDelegateView {
+ public:
+ TestWidgetDelegate() {}
+ virtual ~TestWidgetDelegate() {}
+
+ // Overridden from views::WidgetDelegate:
+ virtual views::View* GetContentsView() OVERRIDE {
+ return this;
+ }
+ virtual bool CanResize() const OVERRIDE {
+ return true;
+ }
+ virtual bool CanMaximize() const OVERRIDE {
+ return true;
+ }
+
+ ash::FrameCaptionButtonContainerView* caption_button_container() {
+ return caption_button_container_;
+ }
+
+ private:
+ // Overridden from views::View:
+ virtual void Layout() OVERRIDE {
+ caption_button_container_->Layout();
+
+ // Right align the caption button container.
+ gfx::Size preferred_size = caption_button_container_->GetPreferredSize();
+ caption_button_container_->SetBounds(width() - preferred_size.width(), 0,
+ preferred_size.width(), preferred_size.height());
+ }
+
+ virtual void ViewHierarchyChanged(
+ const ViewHierarchyChangedDetails& details) OVERRIDE {
+ if (details.is_add && details.child == this) {
+ caption_button_container_ = new FrameCaptionButtonContainerView(
+ GetWidget(), FrameCaptionButtonContainerView::MINIMIZE_ALLOWED);
+ AddChildView(caption_button_container_);
+ }
+ }
+
+ // Not owned.
+ ash::FrameCaptionButtonContainerView* caption_button_container_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWidgetDelegate);
+};
+
+} // namespace
+
+class AlternateFrameSizeButtonTest : public AshTestBase {
+ public:
+ AlternateFrameSizeButtonTest() {}
+ virtual ~AlternateFrameSizeButtonTest() {}
+
+ // Returns the center point of |view| in screen coordinates.
+ gfx::Point CenterPointInScreen(views::View* view) {
+ return view->GetBoundsInScreen().CenterPoint();
+ }
+
+ // Returns true if the window is snapped to |edge|.
+ bool IsSnapped(internal::SnapSizer::Edge edge) const {
+ ash::wm::WindowShowType show_type = window_state()->window_show_type();
+ if (edge == internal::SnapSizer::LEFT_EDGE)
+ return show_type == ash::wm::SHOW_TYPE_LEFT_SNAPPED;
+ else
+ return show_type == ash::wm::SHOW_TYPE_RIGHT_SNAPPED;
+ }
+
+ // Returns true if all three buttons are in the normal state.
+ bool AllButtonsInNormalState() const {
+ return minimize_button_->state() == views::Button::STATE_NORMAL &&
+ size_button_->state() == views::Button::STATE_NORMAL &&
+ close_button_->state() == views::Button::STATE_NORMAL;
+ }
+
+ // Creates a widget with |delegate|. The returned widget takes ownership of
+ // |delegate|.
+ views::Widget* CreateWidget(views::WidgetDelegate* delegate) {
+ views::Widget* widget = new views::Widget;
+ views::Widget::InitParams params(
+ views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ params.context = CurrentContext();
+ params.delegate = delegate;
+ params.bounds = gfx::Rect(10, 10, 100, 100);
+ widget->Init(params);
+ widget->Show();
+ return widget;
+ }
+
+ // AshTestBase overrides:
+ virtual void SetUp() OVERRIDE {
+ AshTestBase::SetUp();
+
+ CommandLine* command_line = CommandLine::ForCurrentProcess();
+ command_line->AppendSwitch(
+ switches::kAshEnableAlternateFrameCaptionButtonStyle);
+ CHECK(!command_line->HasSwitch(switches::kAshMultipleSnapWindowWidths));
+
+ TestWidgetDelegate* delegate = new TestWidgetDelegate();
+ window_state_ = ash::wm::GetWindowState(
+ CreateWidget(delegate)->GetNativeWindow());
+
+ FrameCaptionButtonContainerView::TestApi test(
+ delegate->caption_button_container());
+
+ minimize_button_ = test.minimize_button();
+ size_button_ = test.size_button();
+ static_cast<AlternateFrameSizeButton*>(
+ size_button_)->set_delay_to_set_buttons_to_snap_mode(0);
+ close_button_ = test.close_button();
+ }
+
+ ash::wm::WindowState* window_state() { return window_state_; }
+ const ash::wm::WindowState* window_state() const { return window_state_; }
+
+ FrameCaptionButton* minimize_button() { return minimize_button_; }
+ FrameCaptionButton* size_button() { return size_button_; }
+ FrameCaptionButton* close_button() { return close_button_; }
+
+ private:
+ // Not owned.
+ ash::wm::WindowState* window_state_;
+ FrameCaptionButton* minimize_button_;
+ FrameCaptionButton* size_button_;
+ FrameCaptionButton* close_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlternateFrameSizeButtonTest);
+};
+
+// Tests that pressing the left mouse button or tapping down on the size button
+// puts the button into the pressed state.
+TEST_F(AlternateFrameSizeButtonTest, PressedState) {
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressLeftButton();
+ EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressTouchId(3);
+ EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+ generator.ReleaseTouchId(3);
+ RunAllPendingInMessageLoop();
+ EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+}
+
+// Tests that clicking on the size button toggles between the maximized and
+// normal state.
+TEST_F(AlternateFrameSizeButtonTest, ClickSizeButtonTogglesMaximize) {
+ EXPECT_FALSE(window_state()->IsMaximized());
+
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.ClickLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(window_state()->IsMaximized());
+
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.ClickLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(window_state()->IsMaximized());
+
+ generator.GestureTapAt(CenterPointInScreen(size_button()));
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(window_state()->IsMaximized());
+
+ generator.GestureTapAt(CenterPointInScreen(size_button()));
+ RunAllPendingInMessageLoop();
+ EXPECT_FALSE(window_state()->IsMaximized());
+}
+
+// Test that clicking + dragging to a button adjacent to the size button snaps
+// the window left or right.
+TEST_F(AlternateFrameSizeButtonTest, ButtonDrag) {
+ EXPECT_TRUE(window_state()->IsNormalShowState());
+ EXPECT_FALSE(window_state()->IsSnapped());
+
+ // 1) Test by dragging the mouse.
+ // Snap right.
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressLeftButton();
+ generator.MoveMouseTo(CenterPointInScreen(close_button()));
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+
+ // Snap left.
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressLeftButton();
+ generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+ // 2) Test with scroll gestures.
+ // Snap right.
+ generator.GestureScrollSequence(
+ CenterPointInScreen(size_button()),
+ CenterPointInScreen(close_button()),
+ base::TimeDelta::FromMilliseconds(100),
+ 3);
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+
+ // Snap left.
+ generator.GestureScrollSequence(
+ CenterPointInScreen(size_button()),
+ CenterPointInScreen(minimize_button()),
+ base::TimeDelta::FromMilliseconds(100),
+ 3);
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+ // 3) Test with tap gestures.
+ const int touch_default_radius =
+ ui::GestureConfiguration::default_radius();
+ ui::GestureConfiguration::set_default_radius(0);
+ // Snap right.
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(close_button()));
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::RIGHT_EDGE));
+ // Snap left.
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressMoveAndReleaseTouchTo(CenterPointInScreen(minimize_button()));
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+ ui::GestureConfiguration::set_default_radius(touch_default_radius);
+}
+
+// Test that clicking, dragging, and overshooting the minimize button a bit
+// horizontally still snaps the window left.
+TEST_F(AlternateFrameSizeButtonTest, SnapLeftOvershootMinimize) {
+ EXPECT_TRUE(window_state()->IsNormalShowState());
+ EXPECT_FALSE(window_state()->IsSnapped());
+
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+
+ generator.PressLeftButton();
+ // Move to the minimize button.
+ generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+ // Overshoot the minimize button.
+ generator.MoveMouseBy(-minimize_button()->width(), 0);
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+}
+
+// Test that right clicking the size button has no effect.
+TEST_F(AlternateFrameSizeButtonTest, RightMouseButton) {
+ EXPECT_TRUE(window_state()->IsNormalShowState());
+ EXPECT_FALSE(window_state()->IsSnapped());
+
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressRightButton();
+ generator.ReleaseRightButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(window_state()->IsNormalShowState());
+ EXPECT_FALSE(window_state()->IsSnapped());
+}
+
+// Test that upon releasing the mouse button after having pressed the size
+// button
+// - The state of all the caption buttons is reset.
+// - The icon displayed by all of the caption buttons is reset.
+TEST_F(AlternateFrameSizeButtonTest, ResetButtonsAfterClick) {
+ EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+ EXPECT_TRUE(AllButtonsInNormalState());
+
+ // Pressing the size button should result in the size button being pressed and
+ // the minimize and close button icons changing.
+ aura::test::EventGenerator& generator = GetEventGenerator();
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressLeftButton();
+ EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
+ EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+ EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+ // Dragging the mouse over the minimize button should press the minimize
+ // button and the minimize and close button icons should stay changed.
+ generator.MoveMouseTo(CenterPointInScreen(minimize_button()));
+ EXPECT_EQ(views::Button::STATE_PRESSED, minimize_button()->state());
+ EXPECT_EQ(views::Button::STATE_NORMAL, size_button()->state());
+ EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+ // Release the mouse, snapping the window left.
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+ // None of the buttons should stay pressed and the buttons should have their
+ // regular icons.
+ EXPECT_TRUE(AllButtonsInNormalState());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+
+ // Repeat test but release button where it does not affect the window's state
+ // because the code path is different.
+ generator.MoveMouseTo(CenterPointInScreen(size_button()));
+ generator.PressLeftButton();
+ EXPECT_EQ(views::Button::STATE_NORMAL, minimize_button()->state());
+ EXPECT_EQ(views::Button::STATE_PRESSED, size_button()->state());
+ EXPECT_EQ(views::Button::STATE_NORMAL, close_button()->state());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+ const gfx::Rect& kWorkAreaBoundsInScreen =
+ ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
+ generator.MoveMouseTo(kWorkAreaBoundsInScreen.bottom_left());
+
+ // None of the buttons should be pressed because we are really far away from
+ // any of the caption buttons. The minimize and close button icons should
+ // be changed because the mouse is pressed.
+ EXPECT_TRUE(AllButtonsInNormalState());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_LEFT_SNAPPED, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_RIGHT_SNAPPED, close_button()->icon());
+
+ // Release the mouse. The window should stay snapped left.
+ generator.ReleaseLeftButton();
+ RunAllPendingInMessageLoop();
+ EXPECT_TRUE(IsSnapped(internal::SnapSizer::LEFT_EDGE));
+
+ // The buttons should stay unpressed and the buttons should now have their
+ // regular icons.
+ EXPECT_TRUE(AllButtonsInNormalState());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_MINIMIZE, minimize_button()->icon());
+ EXPECT_EQ(CAPTION_BUTTON_ICON_CLOSE, close_button()->icon());
+}
+
+} // namespace test
+} // namespace ash
diff --git a/ash/wm/caption_buttons/caption_button_types.h b/ash/wm/caption_buttons/caption_button_types.h
new file mode 100644
index 0000000000..b34a861508
--- /dev/null
+++ b/ash/wm/caption_buttons/caption_button_types.h
@@ -0,0 +1,30 @@
+// Copyright 2013 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.
+
+#ifndef ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
+#define ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
+
+namespace ash {
+
+// These are the types of maximization we know.
+enum MaximizeBubbleFrameState {
+ FRAME_STATE_NONE = 0,
+ FRAME_STATE_FULL = 1, // This is the full maximized state.
+ FRAME_STATE_SNAP_LEFT = 2,
+ FRAME_STATE_SNAP_RIGHT = 3
+};
+
+// These are the icon types that a caption button can have. The size button's
+// action (SnapType) can be different from its icon.
+enum CaptionButtonIcon {
+ CAPTION_BUTTON_ICON_MINIMIZE,
+ CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE,
+ CAPTION_BUTTON_ICON_CLOSE,
+ CAPTION_BUTTON_ICON_LEFT_SNAPPED,
+ CAPTION_BUTTON_ICON_RIGHT_SNAPPED,
+};
+
+} // namespace ash
+
+#endif // ASH_WM_CAPTION_BUTTONS_CAPTION_BUTTON_TYPES_H_
diff --git a/ash/wm/caption_buttons/frame_caption_button.cc b/ash/wm/caption_buttons/frame_caption_button.cc
new file mode 100644
index 0000000000..5a6a835683
--- /dev/null
+++ b/ash/wm/caption_buttons/frame_caption_button.cc
@@ -0,0 +1,177 @@
+// Copyright 2013 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/caption_buttons/frame_caption_button.h"
+
+#include "grit/ash_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/animation/slide_animation.h"
+#include "ui/gfx/canvas.h"
+
+namespace ash {
+
+namespace {
+
+// The duration of the crossfade animation when swapping the button's icon.
+const int kCrossfadeDurationMs = 200;
+
+} // namespace
+
+// static
+const char FrameCaptionButton::kViewClassName[] = "FrameCaptionButton";
+
+FrameCaptionButton::FrameCaptionButton(views::ButtonListener* listener,
+ CaptionButtonIcon icon)
+ : ImageButton(listener),
+ icon_(icon),
+ style_(STYLE_SHORT_RESTORED),
+ last_paint_scale_(1.0f),
+ animation_(new gfx::SlideAnimation(this)) {
+ animation_->Reset(1);
+ UpdateImages();
+}
+
+FrameCaptionButton::~FrameCaptionButton() {
+}
+
+void FrameCaptionButton::SetIcon(CaptionButtonIcon icon, Animate animate) {
+ if (icon_ == icon)
+ return;
+
+ if (animate == ANIMATE_YES) {
+ gfx::Canvas canvas(size(), last_paint_scale_, false);
+ OnPaint(&canvas);
+ crossfade_image_ = gfx::ImageSkia(canvas.ExtractImageRep());
+
+ icon_ = icon;
+ UpdateImages();
+
+ animation_->Reset(0);
+ animation_->SetSlideDuration(kCrossfadeDurationMs);
+ animation_->Show();
+ } else {
+ animation_->Reset(1);
+ icon_ = icon;
+ UpdateImages();
+ }
+}
+
+void FrameCaptionButton::SetStyle(Style style) {
+ if (style_ == style)
+ return;
+ animation_->Reset(1);
+ style_ = style;
+ UpdateImages();
+}
+
+const char* FrameCaptionButton::GetClassName() const {
+ return kViewClassName;
+}
+
+void FrameCaptionButton::OnPaint(gfx::Canvas* canvas) {
+ last_paint_scale_ = canvas->image_scale();
+ int alpha = static_cast<int>(animation_->GetCurrentValue() * 255);
+ int crossfade_alpha = 255 - alpha;
+ if (crossfade_alpha > 0 && !crossfade_image_.isNull()) {
+ gfx::Canvas composed_canvas(size(), last_paint_scale_, false);
+ SkPaint paint;
+ paint.setAlpha(crossfade_alpha);
+ paint.setXfermodeMode(SkXfermode::kPlus_Mode);
+ composed_canvas.DrawImageInt(crossfade_image_, 0, 0, paint);
+ paint.setAlpha(alpha);
+ ImageButton::OnPaint(&composed_canvas);
+
+ canvas->DrawImageInt(
+ gfx::ImageSkia(composed_canvas.ExtractImageRep()), 0, 0);
+ } else {
+ ImageButton::OnPaint(canvas);
+ }
+}
+
+void FrameCaptionButton::OnGestureEvent(ui::GestureEvent* event) {
+ // ImageButton does not become pressed when the user drags off and then back
+ // onto the button. Make FrameCaptionButton pressed in this case because this
+ // behavior is more consistent with AlternateFrameSizeButton.
+ if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
+ event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
+ if (HitTestPoint(event->location())) {
+ SetState(STATE_PRESSED);
+ RequestFocus();
+ event->StopPropagation();
+ } else {
+ SetState(STATE_NORMAL);
+ }
+ } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
+ if (HitTestPoint(event->location())) {
+ SetState(STATE_HOVERED);
+ NotifyClick(*event);
+ event->StopPropagation();
+ }
+ }
+ ImageButton::OnGestureEvent(event);
+}
+
+void FrameCaptionButton::StateChanged() {
+ if (state_ == STATE_HOVERED || state_ == STATE_PRESSED)
+ animation_->Reset(1);
+}
+
+void FrameCaptionButton::UpdateImages() {
+ switch (icon_) {
+ case CAPTION_BUTTON_ICON_MINIMIZE:
+ SetImages(IDR_AURA_WINDOW_MINIMIZE_SHORT,
+ IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
+ IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
+ break;
+ case CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE:
+ case CAPTION_BUTTON_ICON_LEFT_SNAPPED:
+ case CAPTION_BUTTON_ICON_RIGHT_SNAPPED:
+ if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
+ SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
+ } else if (style_ == STYLE_SHORT_RESTORED) {
+ SetImages(IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
+ IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
+ } else {
+ SetImages(IDR_AURA_WINDOW_MAXIMIZE,
+ IDR_AURA_WINDOW_MAXIMIZE_H,
+ IDR_AURA_WINDOW_MAXIMIZE_P);
+ }
+ break;
+ case CAPTION_BUTTON_ICON_CLOSE:
+ if (style_ == STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN) {
+ SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
+ } else if (style_ == STYLE_SHORT_RESTORED) {
+ SetImages(IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
+ IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
+ } else {
+ SetImages(IDR_AURA_WINDOW_CLOSE,
+ IDR_AURA_WINDOW_CLOSE_H,
+ IDR_AURA_WINDOW_CLOSE_P);
+ }
+ break;
+ }
+
+ SchedulePaint();
+}
+
+void FrameCaptionButton::SetImages(int normal_image_id,
+ int hot_image_id,
+ int pushed_image_id) {
+ ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
+ SetImage(STATE_NORMAL, resource_bundle.GetImageSkiaNamed(normal_image_id));
+ SetImage(STATE_HOVERED, resource_bundle.GetImageSkiaNamed(hot_image_id));
+ SetImage(STATE_PRESSED, resource_bundle.GetImageSkiaNamed(pushed_image_id));
+}
+
+void FrameCaptionButton::AnimationProgressed(const gfx::Animation* animation) {
+ SchedulePaint();
+}
+
+} // namespace ash
diff --git a/ash/wm/caption_buttons/frame_caption_button.h b/ash/wm/caption_buttons/frame_caption_button.h
new file mode 100644
index 0000000000..61ed922241
--- /dev/null
+++ b/ash/wm/caption_buttons/frame_caption_button.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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.
+
+#ifndef ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
+#define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
+
+#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/image/image_skia.h"
+#include "ui/views/controls/button/image_button.h"
+
+namespace gfx {
+class SlideAnimation;
+}
+
+namespace ash {
+
+// Base class for the window caption buttons (minimize, maximize, restore,
+// close).
+class ASH_EXPORT FrameCaptionButton : public views::ImageButton {
+ public:
+ enum Animate {
+ ANIMATE_YES,
+ ANIMATE_NO
+ };
+
+ enum Style {
+ // Restored tabbed browser windows.
+ STYLE_TALL_RESTORED,
+
+ // All other restored windows.
+ STYLE_SHORT_RESTORED,
+
+ // Maximized or fullscreen windows.
+ STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN
+ };
+
+ static const char kViewClassName[];
+
+ FrameCaptionButton(views::ButtonListener* listener, CaptionButtonIcon icon);
+ virtual ~FrameCaptionButton();
+
+ // Sets the button's icon. If |animate| is ANIMATE_YES, the button crossfades
+ // to the new icon.
+ void SetIcon(CaptionButtonIcon icon, Animate animate);
+
+ // Sets the button's style. The transition to the new style is not animated.
+ void SetStyle(Style style);
+
+ // views::View overrides:
+ virtual const char* GetClassName() const OVERRIDE;
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
+
+ CaptionButtonIcon icon() const {
+ return icon_;
+ }
+
+ protected:
+ // views::CustomButton overrides:
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+ virtual void StateChanged() OVERRIDE;
+
+ private:
+ // Updates the button's images based on the current icon and style.
+ void UpdateImages();
+
+ // Sets the button's images based on the given ids.
+ void SetImages(int normal_image_id, int hot_image_id, int pushed_image_id);
+
+ // gfx::AnimationDelegate override:
+ virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
+
+ // The button's current icon.
+ CaptionButtonIcon icon_;
+
+ // The button's current style.
+ Style style_;
+
+ // The scale at which the button was previously painted.
+ float last_paint_scale_;
+
+ // The image to crossfade from.
+ gfx::ImageSkia crossfade_image_;
+
+ scoped_ptr<gfx::SlideAnimation> animation_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrameCaptionButton);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_H_
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.cc b/ash/wm/caption_buttons/frame_caption_button_container_view.cc
index 40708b86c7..2e4d323dac 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view.cc
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view.cc
@@ -7,8 +7,9 @@
#include "ash/ash_switches.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
#include "ash/wm/caption_buttons/frame_maximize_button.h"
-#include "ash/wm/window_state.h"
#include "grit/ash_resources.h"
#include "grit/ui_strings.h" // Accessibility names
#include "ui/base/hit_test.h"
@@ -16,7 +17,8 @@
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/gfx/canvas.h"
-#include "ui/views/border.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/point.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -54,7 +56,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
bool alternate_style = switches::UseAlternateFrameCaptionButtonStyle();
// Insert the buttons left to right.
- minimize_button_ = new views::ImageButton(this);
+ minimize_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_MINIMIZE);
minimize_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_MINIMIZE));
// Hide |minimize_button_| when using the non-alternate button style because
@@ -65,7 +67,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
AddChildView(minimize_button_);
if (alternate_style)
- size_button_ = new views::ImageButton(this);
+ size_button_ = new AlternateFrameSizeButton(this, frame, this);
else
size_button_ = new FrameMaximizeButton(this, frame);
size_button_->SetAccessibleName(
@@ -73,7 +75,7 @@ FrameCaptionButtonContainerView::FrameCaptionButtonContainerView(
size_button_->SetVisible(frame_->widget_delegate()->CanMaximize());
AddChildView(size_button_);
- close_button_ = new views::ImageButton(this);
+ close_button_ = new FrameCaptionButton(this, CAPTION_BUTTON_ICON_CLOSE);
close_button_->SetAccessibleName(
l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
AddChildView(close_button_);
@@ -92,8 +94,7 @@ FrameCaptionButtonContainerView::GetOldStyleSizeButton() {
}
void FrameCaptionButtonContainerView::ResetWindowControls() {
- minimize_button_->SetState(views::CustomButton::STATE_NORMAL);
- size_button_->SetState(views::CustomButton::STATE_NORMAL);
+ SetButtonsToNormal(ANIMATE_NO);
}
int FrameCaptionButtonContainerView::NonClientHitTest(
@@ -128,45 +129,19 @@ gfx::Size FrameCaptionButtonContainerView::GetPreferredSize() {
}
void FrameCaptionButtonContainerView::Layout() {
- SetButtonImages(minimize_button_,
- IDR_AURA_WINDOW_MINIMIZE_SHORT,
- IDR_AURA_WINDOW_MINIMIZE_SHORT_H,
- IDR_AURA_WINDOW_MINIMIZE_SHORT_P);
+ FrameCaptionButton::Style style = FrameCaptionButton::STYLE_SHORT_RESTORED;
if (header_style_ == HEADER_STYLE_SHORT) {
- // The new assets only make sense if the window is maximized or fullscreen
- // because we usually use a black header in this case.
- if ((frame_->IsMaximized() || frame_->IsFullscreen()) &&
- wm::GetWindowState(
- frame_->GetNativeWindow())->tracked_by_workspace()) {
- SetButtonImages(size_button_,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE2,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_H,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE2_P);
- SetButtonImages(close_button_,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_H,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE2_P);
- } else {
- SetButtonImages(size_button_,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE_H,
- IDR_AURA_WINDOW_MAXIMIZED_RESTORE_P);
- SetButtonImages(close_button_,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE_H,
- IDR_AURA_WINDOW_MAXIMIZED_CLOSE_P);
- }
+ if (frame_->IsMaximized() || frame_->IsFullscreen())
+ style = FrameCaptionButton::STYLE_SHORT_MAXIMIZED_OR_FULLSCREEN;
+ // Else: FrameCaptionButton::STYLE_SHORT_RESTORED;
} else {
- SetButtonImages(size_button_,
- IDR_AURA_WINDOW_MAXIMIZE,
- IDR_AURA_WINDOW_MAXIMIZE_H,
- IDR_AURA_WINDOW_MAXIMIZE_P);
- SetButtonImages(close_button_,
- IDR_AURA_WINDOW_CLOSE,
- IDR_AURA_WINDOW_CLOSE_H,
- IDR_AURA_WINDOW_CLOSE_P);
+ style = FrameCaptionButton::STYLE_TALL_RESTORED;
}
+ minimize_button_->SetStyle(style);
+ size_button_->SetStyle(style);
+ close_button_->SetStyle(style);
+
int x = 0;
for (int i = 0; i < child_count(); ++i) {
views::View* child = child_at(i);
@@ -210,15 +185,14 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
}
+ // Abort any animations of the button icons.
+ SetButtonsToNormal(ANIMATE_NO);
+
ash::UserMetricsAction action =
ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_MINIMIZE;
if (sender == minimize_button_) {
- // The minimize button may move out from under the cursor.
- ResetWindowControls();
frame_->Minimize();
} else if (sender == size_button_) {
- // The size button may move out from under the cursor.
- ResetWindowControls();
if (frame_->IsFullscreen()) { // Can be clicked in immersive fullscreen.
frame_->SetFullscreen(false);
action = ash::UMA_WINDOW_MAXIMIZE_BUTTON_CLICK_EXIT_FULLSCREEN;
@@ -238,18 +212,65 @@ void FrameCaptionButtonContainerView::ButtonPressed(views::Button* sender,
ash::Shell::GetInstance()->delegate()->RecordUserMetricsAction(action);
}
-void FrameCaptionButtonContainerView::SetButtonImages(
- views::ImageButton* button,
- int normal_image_id,
- int hot_image_id,
- int pushed_image_id) {
- ui::ResourceBundle& resource_bundle = ui::ResourceBundle::GetSharedInstance();
- button->SetImage(views::CustomButton::STATE_NORMAL,
- resource_bundle.GetImageSkiaNamed(normal_image_id));
- button->SetImage(views::CustomButton::STATE_HOVERED,
- resource_bundle.GetImageSkiaNamed(hot_image_id));
- button->SetImage(views::CustomButton::STATE_PRESSED,
- resource_bundle.GetImageSkiaNamed(pushed_image_id));
+bool FrameCaptionButtonContainerView::IsMinimizeButtonVisible() const {
+ return minimize_button_->visible();
+}
+
+void FrameCaptionButtonContainerView::SetButtonsToNormal(Animate animate) {
+ SetButtonIcons(CAPTION_BUTTON_ICON_MINIMIZE, CAPTION_BUTTON_ICON_CLOSE,
+ animate);
+ minimize_button_->SetState(views::Button::STATE_NORMAL);
+ size_button_->SetState(views::Button::STATE_NORMAL);
+ close_button_->SetState(views::Button::STATE_NORMAL);
+}
+
+void FrameCaptionButtonContainerView::SetButtonIcons(
+ CaptionButtonIcon minimize_button_icon,
+ CaptionButtonIcon close_button_icon,
+ Animate animate) {
+ FrameCaptionButton::Animate fcb_animate = (animate == ANIMATE_YES) ?
+ FrameCaptionButton::ANIMATE_YES : FrameCaptionButton::ANIMATE_NO;
+ minimize_button_->SetIcon(minimize_button_icon, fcb_animate);
+ close_button_->SetIcon(close_button_icon, fcb_animate);
+}
+
+const FrameCaptionButton*
+FrameCaptionButtonContainerView::PressButtonAt(
+ const gfx::Point& position_in_screen,
+ const gfx::Insets& pressed_hittest_outer_insets) const {
+ DCHECK(switches::UseAlternateFrameCaptionButtonStyle());
+ gfx::Point position(position_in_screen);
+ views::View::ConvertPointFromScreen(this, &position);
+
+ FrameCaptionButton* buttons[] = {
+ close_button_, size_button_, minimize_button_
+ };
+ FrameCaptionButton* pressed_button = NULL;
+ for (size_t i = 0; i < arraysize(buttons); ++i) {
+ FrameCaptionButton* button = buttons[i];
+ if (!button->visible())
+ continue;
+
+ if (button->state() == views::Button::STATE_PRESSED) {
+ gfx::Rect expanded_bounds = button->bounds();
+ expanded_bounds.Inset(pressed_hittest_outer_insets);
+ if (expanded_bounds.Contains(position)) {
+ pressed_button = button;
+ // Do not break in order to give preference to buttons which are
+ // closer to |position_in_screen| than the currently pressed button.
+ // TODO(pkotwicz): Make the caption buttons not overlap.
+ }
+ } else if (ConvertPointToViewAndHitTest(this, button, position)) {
+ pressed_button = button;
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < arraysize(buttons); ++i) {
+ buttons[i]->SetState(buttons[i] == pressed_button ?
+ views::Button::STATE_PRESSED : views::Button::STATE_NORMAL);
+ }
+ return pressed_button;
}
} // namespace ash
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view.h b/ash/wm/caption_buttons/frame_caption_button_container_view.h
index f8e1a92fcb..4fd663b5d7 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view.h
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view.h
@@ -6,23 +6,25 @@
#define ASH_WM_CAPTION_BUTTONS_FRAME_CAPTION_BUTTON_CONTAINER_VIEW_H_
#include "ash/ash_export.h"
+#include "ash/wm/caption_buttons/alternate_frame_size_button_delegate.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
namespace views {
-class ImageButton;
class Widget;
}
namespace ash {
+class FrameCaptionButton;
class FrameMaximizeButton;
// Container view for the frame caption buttons. It performs the appropriate
// action when a caption button is clicked.
class ASH_EXPORT FrameCaptionButtonContainerView
: public views::View,
- public views::ButtonListener {
+ public views::ButtonListener,
+ public AlternateFrameSizeButtonDelegate {
public:
static const char kViewClassName[];
@@ -54,15 +56,15 @@ class ASH_EXPORT FrameCaptionButtonContainerView
: container_view_(container_view) {
}
- views::ImageButton* minimize_button() const {
+ FrameCaptionButton* minimize_button() const {
return container_view_->minimize_button_;
}
- views::ImageButton* size_button() const {
+ FrameCaptionButton* size_button() const {
return container_view_->size_button_;
}
- views::ImageButton* close_button() const {
+ FrameCaptionButton* close_button() const {
return container_view_->close_button_;
}
@@ -102,11 +104,15 @@ class ASH_EXPORT FrameCaptionButtonContainerView
virtual void ButtonPressed(views::Button* sender,
const ui::Event& event) OVERRIDE;
- // Sets the images for a button based on the given ids.
- void SetButtonImages(views::ImageButton* button,
- int normal_image_id,
- int hot_image_id,
- int pushed_image_id);
+ // AlternateFrameSizeButton::Delegate overrides:
+ virtual bool IsMinimizeButtonVisible() const OVERRIDE;
+ virtual void SetButtonsToNormal(Animate animate) OVERRIDE;
+ virtual void SetButtonIcons(CaptionButtonIcon minimize_button_icon,
+ CaptionButtonIcon close_button_icon,
+ Animate animate) OVERRIDE;
+ virtual const FrameCaptionButton* PressButtonAt(
+ const gfx::Point& position_in_screen,
+ const gfx::Insets& pressed_hittest_outer_insets) const OVERRIDE;
// The widget that the buttons act on.
views::Widget* frame_;
@@ -118,9 +124,9 @@ class ASH_EXPORT FrameCaptionButtonContainerView
// The buttons. In the normal button style, at most one of |minimize_button_|
// and |size_button_| is visible.
- views::ImageButton* minimize_button_;
- views::ImageButton* size_button_;
- views::ImageButton* close_button_;
+ FrameCaptionButton* minimize_button_;
+ FrameCaptionButton* size_button_;
+ FrameCaptionButton* close_button_;
DISALLOW_COPY_AND_ASSIGN(FrameCaptionButtonContainerView);
};
diff --git a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
index 6737b92d25..589123125e 100644
--- a/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
+++ b/ash/wm/caption_buttons/frame_caption_button_container_view_unittest.cc
@@ -6,13 +6,12 @@
#include "ash/ash_switches.h"
#include "ash/test/ash_test_base.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
#include "base/command_line.h"
#include "grit/ash_resources.h"
#include "ui/aura/root_window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/border.h"
-#include "ui/views/controls/button/custom_button.h"
-#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
@@ -68,8 +67,8 @@ class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase {
// Tests that |leftmost| and |rightmost| are at |container|'s edges.
bool CheckButtonsAtEdges(FrameCaptionButtonContainerView* container,
- const views::CustomButton& leftmost,
- const views::CustomButton& rightmost) {
+ const ash::FrameCaptionButton& leftmost,
+ const ash::FrameCaptionButton& rightmost) {
gfx::Rect expected(container->GetPreferredSize());
gfx::Rect container_size(container->GetPreferredSize());
@@ -89,7 +88,7 @@ class FrameCaptionButtonContainerViewTest : public ash::test::AshTestBase {
}
// Returns true if the images for |button|'s states match the passed in ids.
- bool ImagesMatch(views::ImageButton* button,
+ bool ImagesMatch(ash::FrameCaptionButton* button,
int normal_image_id,
int hovered_image_id,
int pressed_image_id) {
@@ -244,7 +243,6 @@ class FrameCaptionButtonContainerViewTestAlternateStyle
FrameCaptionButtonContainerViewTest::SetUp();
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kAshEnableAlternateFrameCaptionButtonStyle);
- ASSERT_TRUE(switches::UseAlternateFrameCaptionButtonStyle());
}
private:
@@ -254,6 +252,11 @@ class FrameCaptionButtonContainerViewTestAlternateStyle
// Test how the alternate button style affects which buttons are visible in the
// default case.
TEST_F(FrameCaptionButtonContainerViewTestAlternateStyle, ButtonVisibility) {
+ // Using the alternate caption button style is dependant on all snapped
+ // windows being 50% of the screen's width.
+ if (!switches::UseAlternateFrameCaptionButtonStyle())
+ return;
+
// Both the minimize button and the maximize button should be visible when
// both minimizing and maximizing are allowed when using the alternate
// button style.
diff --git a/ash/wm/caption_buttons/frame_maximize_button.cc b/ash/wm/caption_buttons/frame_maximize_button.cc
index 14526a0136..5a610e58d7 100644
--- a/ash/wm/caption_buttons/frame_maximize_button.cc
+++ b/ash/wm/caption_buttons/frame_maximize_button.cc
@@ -4,7 +4,6 @@
#include "ash/wm/caption_buttons/frame_maximize_button.h"
-#include "ash/launcher/launcher.h"
#include "ash/screen_ash.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
@@ -82,7 +81,7 @@ void FrameMaximizeButton::EscapeEventFilter::OnKeyEvent(
FrameMaximizeButton::FrameMaximizeButton(views::ButtonListener* listener,
views::Widget* frame)
- : ImageButton(listener),
+ : FrameCaptionButton(listener, CAPTION_BUTTON_ICON_MAXIMIZE_RESTORE),
frame_(frame),
observing_frame_(false),
is_snap_enabled_(false),
@@ -527,16 +526,10 @@ gfx::Point FrameMaximizeButton::LocationForSnapSizer(
void FrameMaximizeButton::Snap(SnapSizer* snap_sizer) {
Shell* shell = Shell::GetInstance();
- wm::WindowState* window_state = wm::GetWindowState(frame_->GetNativeWindow());
switch (snap_type_) {
case SNAP_LEFT:
case SNAP_RIGHT: {
- // Others might also have set up a restore rectangle already. If so, we
- // should not overwrite the restore rectangle.
- gfx::Rect current_bounds_in_screen = frame_->GetWindowBoundsInScreen();
snap_sizer->SnapWindowToTargetBounds();
- if (!window_state->HasRestoreBounds())
- window_state->SetRestoreBoundsInScreen(current_bounds_in_screen);
shell->delegate()->RecordUserMetricsAction(
snap_type_ == SNAP_LEFT ?
UMA_WINDOW_MAXIMIZE_BUTTON_MAXIMIZE_LEFT :
diff --git a/ash/wm/caption_buttons/frame_maximize_button.h b/ash/wm/caption_buttons/frame_maximize_button.h
index c60f7a570d..a1bb991a98 100644
--- a/ash/wm/caption_buttons/frame_maximize_button.h
+++ b/ash/wm/caption_buttons/frame_maximize_button.h
@@ -6,13 +6,13 @@
#define ASH_WM_CAPTION_BUTTONS_FRAME_MAXIMIZE_BUTTON_H_
#include "ash/ash_export.h"
-#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
+#include "ash/wm/caption_buttons/frame_caption_button.h"
#include "ash/wm/workspace/snap_types.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "ui/aura/window_observer.h"
-#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget_observer.h"
namespace views {
@@ -30,7 +30,7 @@ class SnapSizer;
class MaximizeBubbleController;
// Button used for the maximize control on the frame. Handles snapping logic.
-class ASH_EXPORT FrameMaximizeButton : public views::ImageButton,
+class ASH_EXPORT FrameMaximizeButton : public FrameCaptionButton,
public views::WidgetObserver,
public aura::WindowObserver {
public:
diff --git a/ash/wm/caption_buttons/maximize_bubble_controller.h b/ash/wm/caption_buttons/maximize_bubble_controller.h
index 9a842e5c43..53a07dd295 100644
--- a/ash/wm/caption_buttons/maximize_bubble_controller.h
+++ b/ash/wm/caption_buttons/maximize_bubble_controller.h
@@ -6,7 +6,7 @@
#define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_CONTROLLER_H_
#include "ash/ash_export.h"
-#include "ash/wm/caption_buttons/maximize_bubble_frame_state.h"
+#include "ash/wm/caption_buttons/caption_button_types.h"
#include "ash/wm/workspace/snap_types.h"
#include "base/memory/scoped_ptr.h"
diff --git a/ash/wm/caption_buttons/maximize_bubble_frame_state.h b/ash/wm/caption_buttons/maximize_bubble_frame_state.h
deleted file mode 100644
index 1737818255..0000000000
--- a/ash/wm/caption_buttons/maximize_bubble_frame_state.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2013 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.
-
-#ifndef ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
-#define ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
-
-namespace ash {
-
-// These are the types of maximization we know.
-enum MaximizeBubbleFrameState {
- FRAME_STATE_NONE = 0,
- FRAME_STATE_FULL = 1, // This is the full maximized state.
- FRAME_STATE_SNAP_LEFT = 2,
- FRAME_STATE_SNAP_RIGHT = 3
-};
-
-} // namespace views
-
-#endif // ASH_WM_CAPTION_BUTTONS_MAXIMIZE_BUBBLE_FRAME_STATE_H_
diff --git a/ash/wm/custom_frame_view_ash.cc b/ash/wm/custom_frame_view_ash.cc
index ef2bc108c7..268a4d2b82 100644
--- a/ash/wm/custom_frame_view_ash.cc
+++ b/ash/wm/custom_frame_view_ash.cc
@@ -4,13 +4,20 @@
#include "ash/wm/custom_frame_view_ash.h"
+#include "ash/ash_switches.h"
#include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
#include "ash/wm/caption_buttons/frame_maximize_button.h"
#include "ash/wm/caption_buttons/frame_maximize_button_observer.h"
#include "ash/wm/frame_border_hit_test_controller.h"
#include "ash/wm/header_painter.h"
#include "ash/wm/immersive_fullscreen_controller.h"
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_state_delegate.h"
+#include "base/command_line.h"
#include "grit/ash_resources.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/font.h"
#include "ui/gfx/rect.h"
@@ -30,6 +37,87 @@ const gfx::Font& GetTitleFont() {
return *title_font;
}
+///////////////////////////////////////////////////////////////////////////////
+// CustomFrameViewAshWindowStateDelegate
+
+// Handles a user's fullscreen request (Shift+F4/F4). Puts the window into
+// immersive fullscreen if the kAshEnableImmersiveFullscreenForAllWindows
+// flag is set.
+class CustomFrameViewAshWindowStateDelegate
+ : public ash::wm::WindowStateDelegate,
+ public ash::wm::WindowStateObserver,
+ public aura::WindowObserver {
+ public:
+ CustomFrameViewAshWindowStateDelegate(
+ ash::wm::WindowState* window_state,
+ ash::CustomFrameViewAsh* custom_frame_view)
+ : window_state_(NULL) {
+#if defined(OS_CHROMEOS)
+ // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for
+ // Windows Ash.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ ash::switches::kAshEnableImmersiveFullscreenForAllWindows)) {
+ immersive_fullscreen_controller_.reset(
+ new ash::ImmersiveFullscreenController);
+ custom_frame_view->InitImmersiveFullscreenControllerForView(
+ immersive_fullscreen_controller_.get());
+
+ // Add a window state observer to exit fullscreen properly in case
+ // fullscreen is exited without going through
+ // WindowState::ToggleFullscreen(). This is the case when exiting
+ // immersive fullscreen via the "Restore" window control.
+ // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048
+ window_state_ = window_state;
+ window_state_->AddObserver(this);
+ window_state_->window()->AddObserver(this);
+ }
+#endif
+ }
+ virtual ~CustomFrameViewAshWindowStateDelegate() {
+ if (window_state_) {
+ window_state_->RemoveObserver(this);
+ window_state_->window()->RemoveObserver(this);
+ }
+ }
+ private:
+ // Overridden from ash::wm::WindowStateDelegate:
+ virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE {
+ bool enter_fullscreen = !window_state->IsFullscreen();
+ if (enter_fullscreen) {
+ window_state->window()->SetProperty(aura::client::kShowStateKey,
+ ui::SHOW_STATE_FULLSCREEN);
+ } else {
+ window_state->Restore();
+ }
+ if (immersive_fullscreen_controller_)
+ immersive_fullscreen_controller_->SetEnabled(enter_fullscreen);
+ return true;
+ }
+ // Overridden from aura::WindowObserver:
+ virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
+ window_state_->RemoveObserver(this);
+ window_state_->window()->RemoveObserver(this);
+ window_state_ = NULL;
+ }
+ // Overridden from ash::wm::WindowStateObserver:
+ virtual void OnWindowShowTypeChanged(
+ ash::wm::WindowState* window_state,
+ ash::wm::WindowShowType old_type) OVERRIDE {
+ if (!window_state->IsFullscreen() &&
+ !window_state->IsMinimized() &&
+ immersive_fullscreen_controller_.get() &&
+ immersive_fullscreen_controller_->IsEnabled()) {
+ immersive_fullscreen_controller_->SetEnabled(false);
+ }
+ }
+
+ ash::wm::WindowState* window_state_;
+ scoped_ptr<ash::ImmersiveFullscreenController>
+ immersive_fullscreen_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(CustomFrameViewAshWindowStateDelegate);
+};
+
} // namespace
namespace ash {
@@ -177,7 +265,7 @@ void CustomFrameViewAsh::HeaderView::Layout() {
void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) {
int theme_image_id = 0;
- if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO))
+ if (frame_->IsMaximized() || frame_->IsFullscreen())
theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
else if (paint_as_active_)
theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
@@ -302,6 +390,15 @@ CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame)
// |header_view_| is set as the non client view's overlay view so that it can
// overlay the web contents in immersive fullscreen.
frame->non_client_view()->SetOverlayView(new OverlayView(header_view_));
+
+ // A delegate for a more complex way of fullscreening the window may already
+ // be set. This is the case for packaged apps.
+ wm::WindowState* window_state = wm::GetWindowState(frame->GetNativeWindow());
+ if (!window_state->HasDelegate()) {
+ window_state->SetDelegate(scoped_ptr<wm::WindowStateDelegate>(
+ new CustomFrameViewAshWindowStateDelegate(
+ window_state, this)).Pass());
+ }
}
CustomFrameViewAsh::~CustomFrameViewAsh() {
diff --git a/ash/wm/dock/docked_window_layout_manager.cc b/ash/wm/dock/docked_window_layout_manager.cc
index f4a2e2629b..3298f05602 100644
--- a/ash/wm/dock/docked_window_layout_manager.cc
+++ b/ash/wm/dock/docked_window_layout_manager.cc
@@ -21,15 +21,21 @@
#include "base/auto_reset.h"
#include "base/command_line.h"
#include "base/metrics/histogram.h"
+#include "grit/ash_resources.h"
#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPaint.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/window_tree_client.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
+#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/rect.h"
+#include "ui/views/background.h"
namespace ash {
namespace internal {
@@ -45,24 +51,90 @@ const int DockedWindowLayoutManager::kMinDockGap = 2;
const int DockedWindowLayoutManager::kIdealWidth = 250;
const int kMinimumHeight = 250;
const int kSlideDurationMs = 120;
-const int kFadeDurationMs = 720;
+const int kFadeDurationMs = 60;
+const int kMinimizeDurationMs = 720;
-namespace {
-
-const SkColor kDockBackgroundColor = SkColorSetARGB(0xff, 0x10, 0x10, 0x10);
-const float kDockBackgroundOpacity = 0.5f;
-
-class DockedBackgroundWidget : public views::Widget {
+class DockedBackgroundWidget : public views::Widget,
+ public internal::BackgroundAnimatorDelegate {
public:
- explicit DockedBackgroundWidget(aura::Window* container) {
+ explicit DockedBackgroundWidget(aura::Window* container)
+ : alignment_(DOCKED_ALIGNMENT_NONE),
+ background_animator_(this, 0, kLauncherBackgroundAlpha),
+ alpha_(0),
+ opaque_background_(ui::LAYER_SOLID_COLOR) {
InitWidget(container);
}
+ // Sets widget bounds and sizes opaque background layer to fill the widget.
+ void SetBackgroundBounds(const gfx::Rect bounds, DockedAlignment alignment) {
+ SetBounds(bounds);
+ opaque_background_.SetBounds(gfx::Rect(bounds.size()));
+ alignment_ = alignment;
+ }
+
+ // Sets the docked area background type and starts transition animation.
+ void SetPaintsBackground(
+ ShelfBackgroundType background_type,
+ BackgroundAnimatorChangeType change_type) {
+ float target_opacity =
+ (background_type == SHELF_BACKGROUND_MAXIMIZED) ? 1.0f : 0.0f;
+ scoped_ptr<ui::ScopedLayerAnimationSettings> opaque_background_animation;
+ if (change_type != BACKGROUND_CHANGE_IMMEDIATE) {
+ opaque_background_animation.reset(new ui::ScopedLayerAnimationSettings(
+ opaque_background_.GetAnimator()));
+ opaque_background_animation->SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kTimeToSwitchBackgroundMs));
+ }
+ opaque_background_.SetOpacity(target_opacity);
+
+ // TODO(varkha): use ui::Layer on both opaque_background and normal
+ // background retire background_animator_ at all. It would be simpler.
+ // See also ShelfWidget::SetPaintsBackground.
+ background_animator_.SetPaintsBackground(
+ background_type != SHELF_BACKGROUND_DEFAULT,
+ change_type);
+ SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
+ }
+
+ // views::Widget:
+ virtual void OnNativeWidgetPaint(gfx::Canvas* canvas) OVERRIDE {
+ const gfx::ImageSkia& launcher_background(
+ alignment_ == DOCKED_ALIGNMENT_LEFT ?
+ launcher_background_left_ : launcher_background_right_);
+ gfx::Rect rect = gfx::Rect(GetWindowBoundsInScreen().size());
+ SkPaint paint;
+ paint.setAlpha(alpha_);
+ canvas->DrawImageInt(
+ launcher_background,
+ 0, 0, launcher_background.width(), launcher_background.height(),
+ alignment_ == DOCKED_ALIGNMENT_LEFT ?
+ rect.width() - launcher_background.width() : 0, 0,
+ launcher_background.width(), rect.height(),
+ false,
+ paint);
+ canvas->DrawImageInt(
+ launcher_background,
+ alignment_ == DOCKED_ALIGNMENT_LEFT ?
+ 0 : launcher_background.width() - 1, 0,
+ 1, launcher_background.height(),
+ alignment_ == DOCKED_ALIGNMENT_LEFT ?
+ 0 : launcher_background.width(), 0,
+ rect.width() - launcher_background.width(), rect.height(),
+ false,
+ paint);
+ }
+
+ // BackgroundAnimatorDelegate:
+ virtual void UpdateBackground(int alpha) OVERRIDE {
+ alpha_ = alpha;
+ SchedulePaintInRect(gfx::Rect(GetWindowBoundsInScreen().size()));
+ }
+
private:
void InitWidget(aura::Window* parent) {
views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_POPUP;
- params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
+ params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.can_activate = false;
params.keep_on_top = false;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
@@ -71,17 +143,41 @@ class DockedBackgroundWidget : public views::Widget {
set_focus_on_creation(false);
Init(params);
GetNativeWindow()->SetProperty(internal::kStayInSameRootWindowKey, true);
- DCHECK_EQ(GetNativeView()->GetRootWindow(), parent->GetRootWindow());
- views::View* content_view = new views::View;
- content_view->set_background(
- views::Background::CreateSolidBackground(kDockBackgroundColor));
- SetContentsView(content_view);
+ opaque_background_.SetColor(SK_ColorBLACK);
+ opaque_background_.SetBounds(gfx::Rect(GetWindowBoundsInScreen().size()));
+ opaque_background_.SetOpacity(0.0f);
+ GetNativeWindow()->layer()->Add(&opaque_background_);
Hide();
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ gfx::ImageSkia launcher_background =
+ *rb.GetImageSkiaNamed(IDR_AURA_LAUNCHER_BACKGROUND);
+ launcher_background_left_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+ launcher_background, SkBitmapOperations::ROTATION_90_CW);
+ launcher_background_right_ = gfx::ImageSkiaOperations::CreateRotatedImage(
+ launcher_background, SkBitmapOperations::ROTATION_270_CW);
}
+ DockedAlignment alignment_;
+
+ // The animator for the background transitions.
+ internal::BackgroundAnimator background_animator_;
+
+ // The alpha to use for drawing image assets covering the docked background.
+ int alpha_;
+
+ // Solid black background that can be made fully opaque.
+ ui::Layer opaque_background_;
+
+ // Backgrounds created from shelf background by 90 or 270 degree rotation.
+ gfx::ImageSkia launcher_background_left_;
+ gfx::ImageSkia launcher_background_right_;
+
DISALLOW_COPY_AND_ASSIGN(DockedBackgroundWidget);
};
+namespace {
+
// Returns true if a window is a popup or a transient child.
bool IsPopupOrTransient(const aura::Window* window) {
return (window->type() == aura::client::WINDOW_TYPE_POPUP ||
@@ -97,10 +193,10 @@ bool IsUsedByLayout(const aura::Window* window) {
void UndockWindow(aura::Window* window) {
gfx::Rect previous_bounds = window->bounds();
- aura::Window* previous_parent = window->parent();
+ aura::Window* old_parent = window->parent();
aura::client::ParentWindowWithContext(window, window, gfx::Rect());
- if (window->parent() != previous_parent)
- wm::ReparentTransientChildrenOfChild(window->parent(), window);
+ if (window->parent() != old_parent)
+ wm::ReparentTransientChildrenOfChild(window, old_parent, window->parent());
// Start maximize or fullscreen (affecting packaged apps) animation from
// previous window bounds.
window->layer()->SetBounds(previous_bounds);
@@ -224,7 +320,46 @@ struct CompareWindowPos {
} // namespace
////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager public implementation:
+// A class that observes launcher shelf for bounds changes.
+class DockedWindowLayoutManager::ShelfWindowObserver : public WindowObserver {
+ public:
+ explicit ShelfWindowObserver(
+ DockedWindowLayoutManager* docked_layout_manager)
+ : docked_layout_manager_(docked_layout_manager) {
+ DCHECK(docked_layout_manager_->launcher()->shelf_widget());
+ docked_layout_manager_->launcher()->shelf_widget()->GetNativeView()
+ ->AddObserver(this);
+ }
+
+ virtual ~ShelfWindowObserver() {
+ if (docked_layout_manager_->launcher() &&
+ docked_layout_manager_->launcher()->shelf_widget())
+ docked_layout_manager_->launcher()->shelf_widget()->GetNativeView()
+ ->RemoveObserver(this);
+ }
+
+ // aura::WindowObserver:
+ virtual void OnWindowBoundsChanged(aura::Window* window,
+ const gfx::Rect& old_bounds,
+ const gfx::Rect& new_bounds) OVERRIDE {
+ shelf_bounds_in_screen_ = ScreenAsh::ConvertRectToScreen(
+ window->parent(), new_bounds);
+ docked_layout_manager_->OnShelfBoundsChanged();
+ }
+
+ const gfx::Rect& shelf_bounds_in_screen() const {
+ return shelf_bounds_in_screen_;
+ }
+
+ private:
+ DockedWindowLayoutManager* docked_layout_manager_;
+ gfx::Rect shelf_bounds_in_screen_;
+
+ DISALLOW_COPY_AND_ASSIGN(ShelfWindowObserver);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// DockedWindowLayoutManager public implementation:
DockedWindowLayoutManager::DockedWindowLayoutManager(
aura::Window* dock_container, WorkspaceController* workspace_controller)
: dock_container_(dock_container),
@@ -252,6 +387,12 @@ DockedWindowLayoutManager::~DockedWindowLayoutManager() {
}
void DockedWindowLayoutManager::Shutdown() {
+ if (launcher_ && launcher_->shelf_widget()) {
+ ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher(
+ launcher_->shelf_widget()->GetNativeWindow());
+ shelf_layout_manager->RemoveObserver(this);
+ shelf_observer_.reset();
+ }
launcher_ = NULL;
for (size_t i = 0; i < dock_container_->children().size(); ++i) {
aura::Window* child = dock_container_->children()[i];
@@ -332,6 +473,12 @@ void DockedWindowLayoutManager::FinishDragging(DockedAction action,
void DockedWindowLayoutManager::SetLauncher(ash::Launcher* launcher) {
DCHECK(!launcher_);
launcher_ = launcher;
+ if (launcher_->shelf_widget()) {
+ ShelfLayoutManager* shelf_layout_manager = ShelfLayoutManager::ForLauncher(
+ launcher_->shelf_widget()->GetNativeWindow());
+ shelf_layout_manager->AddObserver(this);
+ shelf_observer_.reset(new ShelfWindowObserver(this));
+ }
}
DockedAlignment DockedWindowLayoutManager::GetAlignmentOfWindow(
@@ -416,8 +563,13 @@ bool DockedWindowLayoutManager::CanDockWindow(aura::Window* window,
return true;
}
+void DockedWindowLayoutManager::OnShelfBoundsChanged() {
+ Relayout();
+ UpdateDockBounds(DockedWindowLayoutManagerObserver::DISPLAY_INSETS_CHANGED);
+}
+
////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, aura::LayoutManager implementation:
+// DockedWindowLayoutManager, aura::LayoutManager implementation:
void DockedWindowLayoutManager::OnWindowResized() {
MaybeMinimizeChildrenExcept(dragged_window_);
Relayout();
@@ -482,10 +634,16 @@ void DockedWindowLayoutManager::SetChildBounds(
const gfx::Rect& requested_bounds) {
// Whenever one of our windows is moved or resized enforce layout.
SetChildBoundsDirect(child, requested_bounds);
+ if (IsPopupOrTransient(child))
+ return;
+ ShelfLayoutManager* shelf_layout = internal::ShelfLayoutManager::ForLauncher(
+ dock_container_);
+ if (shelf_layout)
+ shelf_layout->UpdateVisibilityState();
}
////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, ash::ShellObserver implementation:
+// DockedWindowLayoutManager, ash::ShellObserver implementation:
void DockedWindowLayoutManager::OnDisplayWorkAreaInsetsChanged() {
Relayout();
@@ -551,7 +709,15 @@ void DockedWindowLayoutManager::OnShelfAlignmentChanged(
}
/////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, WindowStateObserver implementation:
+// DockedWindowLayoutManager, ShelfLayoutManagerObserver implementation:
+void DockedWindowLayoutManager::OnBackgroundUpdated(
+ ShelfBackgroundType background_type,
+ BackgroundAnimatorChangeType change_type) {
+ background_widget_->SetPaintsBackground(background_type, change_type);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// DockedWindowLayoutManager, WindowStateObserver implementation:
void DockedWindowLayoutManager::OnWindowShowTypeChanged(
wm::WindowState* window_state,
@@ -565,18 +731,19 @@ void DockedWindowLayoutManager::OnWindowShowTypeChanged(
return;
if (window_state->IsMinimized()) {
MinimizeDockedWindow(window_state);
- } else if (window_state->IsMaximizedOrFullscreen()) {
- // Reparenting changes the source bounds for the animation if a window is
- // visible so hide it here and show later when it is already in the desktop.
- UndockWindow(window);
- RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
+ } else if (window_state->IsMaximizedOrFullscreen() ||
+ window_state->IsSnapped()) {
+ if (window != dragged_window_) {
+ UndockWindow(window);
+ RecordUmaAction(DOCKED_ACTION_MAXIMIZE, DOCKED_ACTION_SOURCE_UNKNOWN);
+ }
} else if (old_type == wm::SHOW_TYPE_MINIMIZED) {
RestoreDockedWindow(window_state);
}
}
/////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, WindowObserver implementation:
+// DockedWindowLayoutManager, WindowObserver implementation:
void DockedWindowLayoutManager::OnWindowBoundsChanged(
aura::Window* window,
@@ -615,7 +782,8 @@ void DockedWindowLayoutManager::OnWindowDestroying(aura::Window* window) {
////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager, aura::client::ActivationChangeObserver implementation:
+// DockedWindowLayoutManager, aura::client::ActivationChangeObserver
+// implementation:
void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
@@ -635,7 +803,7 @@ void DockedWindowLayoutManager::OnWindowActivated(aura::Window* gained_active,
}
////////////////////////////////////////////////////////////////////////////////
-// DockLayoutManager private implementation:
+// DockedWindowLayoutManager private implementation:
void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
aura::Window* child) {
@@ -653,10 +821,17 @@ void DockedWindowLayoutManager::MaybeMinimizeChildrenExcept(
if (window == child || !IsUsedByLayout(window))
continue;
int room_needed = GetWindowHeightCloseTo(window, 0) + kMinDockGap;
- if (available_room > room_needed)
+ if (available_room > room_needed) {
available_room -= room_needed;
- else
+ } else {
+ // Slow down minimizing animations. Lock duration so that it is not
+ // overridden by other ScopedLayerAnimationSettings down the stack.
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kMinimizeDurationMs));
+ settings.LockTransitionDuration();
wm::GetWindowState(window)->Minimize();
+ }
}
}
@@ -673,7 +848,7 @@ void DockedWindowLayoutManager::RestoreDockedWindow(
wm::WindowState* window_state) {
aura::Window* window = window_state->window();
DCHECK(!IsPopupOrTransient(window));
- // Always place restored window at the top shuffling the other windows down.
+ // Always place restored window at the bottom shuffling the other windows up.
// TODO(varkha): add a separate container for docked windows to keep track
// of ordering.
gfx::Display display = Shell::GetScreen()->GetDisplayNearestWindow(
@@ -687,7 +862,7 @@ void DockedWindowLayoutManager::RestoreDockedWindow(
return;
}
gfx::Rect bounds(window->bounds());
- bounds.set_y(work_area.y() - bounds.height());
+ bounds.set_y(work_area.bottom());
window->SetBounds(bounds);
window->Show();
MaybeMinimizeChildrenExcept(window);
@@ -805,8 +980,10 @@ void DockedWindowLayoutManager::Relayout() {
}
// Position docked windows as well as the window being dragged.
- const gfx::Rect work_area =
+ gfx::Rect work_area =
Shell::GetScreen()->GetDisplayNearestWindow(dock_container_).work_area();
+ if (shelf_observer_)
+ work_area.Subtract(shelf_observer_->shelf_bounds_in_screen());
int available_room = CalculateWindowHeightsAndRemainingRoom(work_area,
&visible_windows);
FanOutChildren(work_area,
@@ -983,14 +1160,14 @@ void DockedWindowLayoutManager::UpdateDockBounds(
observer_list_,
OnDockBoundsChanging(bounds, reason));
// Show or hide background for docked area.
- background_widget_->SetBounds(docked_bounds_);
- if (docked_width_ > 0) {
+ gfx::Rect background_bounds(docked_bounds_);
+ if (shelf_observer_)
+ background_bounds.Subtract(shelf_observer_->shelf_bounds_in_screen());
+ background_widget_->SetBackgroundBounds(background_bounds, alignment_);
+ if (docked_width_ > 0)
background_widget_->Show();
- background_widget_->GetNativeWindow()->layer()->SetOpacity(
- kDockBackgroundOpacity);
- } else {
+ else
background_widget_->Hide();
- }
}
void DockedWindowLayoutManager::UpdateStacking(aura::Window* active_window) {
diff --git a/ash/wm/dock/docked_window_layout_manager.h b/ash/wm/dock/docked_window_layout_manager.h
index 9cb38265c7..8894bed42f 100644
--- a/ash/wm/dock/docked_window_layout_manager.h
+++ b/ash/wm/dock/docked_window_layout_manager.h
@@ -41,6 +41,7 @@ namespace ash {
class Launcher;
namespace internal {
+class DockedBackgroundWidget;
class DockedWindowLayoutManagerObserver;
class DockedWindowResizerTest;
class ShelfLayoutManager;
@@ -74,6 +75,7 @@ class ASH_EXPORT DockedWindowLayoutManager
public aura::WindowObserver,
public aura::client::ActivationChangeObserver,
public keyboard::KeyboardControllerObserver,
+ public ShelfLayoutManagerObserver,
public wm::WindowStateObserver {
public:
// Maximum width of the docked windows area.
@@ -132,6 +134,9 @@ class ASH_EXPORT DockedWindowLayoutManager
// Returns true if currently dragged window is docked at the screen edge.
bool is_dragged_window_docked() const { return is_dragged_window_docked_; }
+ // Updates docked layout when launcher shelf bounds change.
+ void OnShelfBoundsChanged();
+
// aura::LayoutManager:
virtual void OnWindowResized() OVERRIDE;
virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
@@ -148,6 +153,11 @@ class ASH_EXPORT DockedWindowLayoutManager
aura::Window* root_window) OVERRIDE;
virtual void OnShelfAlignmentChanged(aura::Window* root_window) OVERRIDE;
+ // ShelfLayoutManagerObserver:
+ virtual void OnBackgroundUpdated(
+ ShelfBackgroundType background_type,
+ BackgroundAnimatorChangeType change_type) OVERRIDE;
+
// wm::WindowStateObserver:
virtual void OnWindowShowTypeChanged(wm::WindowState* window_state,
wm::WindowShowType old_type) OVERRIDE;
@@ -165,6 +175,7 @@ class ASH_EXPORT DockedWindowLayoutManager
aura::Window* lost_active) OVERRIDE;
private:
+ class ShelfWindowObserver;
friend class DockedWindowLayoutManagerTest;
friend class DockedWindowResizerTest;
@@ -255,6 +266,7 @@ class ASH_EXPORT DockedWindowLayoutManager
// The launcher to respond to launcher alignment changes.
Launcher* launcher_;
+
// Workspace controller that can be checked for fullscreen mode.
WorkspaceController* workspace_controller_;
// Tracks if any window in the same root window is in fullscreen mode.
@@ -279,8 +291,11 @@ class ASH_EXPORT DockedWindowLayoutManager
// Used in UMA metrics.
base::Time last_action_time_;
+ // Observes launcher shelf for bounds changes.
+ scoped_ptr<ShelfWindowObserver> shelf_observer_;
+
// Widget used to paint a background for the docked area.
- scoped_ptr<views::Widget> background_widget_;
+ scoped_ptr<DockedBackgroundWidget> background_widget_;
// Observers of dock bounds changes.
ObserverList<DockedWindowLayoutManagerObserver> observer_list_;
diff --git a/ash/wm/dock/docked_window_layout_manager_unittest.cc b/ash/wm/dock/docked_window_layout_manager_unittest.cc
index 9d31f4dd56..4b626e4927 100644
--- a/ash/wm/dock/docked_window_layout_manager_unittest.cc
+++ b/ash/wm/dock/docked_window_layout_manager_unittest.cc
@@ -18,7 +18,7 @@
#include "ash/test/launcher_test_api.h"
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/panels/panel_layout_manager.h"
#include "ash/wm/window_resizer.h"
@@ -48,7 +48,7 @@ class DockedWindowLayoutManagerTest
virtual void SetUp() OVERRIDE {
AshTestBase::SetUp();
UpdateDisplay("600x600");
- ASSERT_TRUE(test::TestLauncherDelegate::instance());
+ ASSERT_TRUE(test::TestShelfDelegate::instance());
shelf_view_test_.reset(new test::ShelfViewTestAPI(
test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view()));
@@ -72,9 +72,9 @@ class DockedWindowLayoutManagerTest
aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
NULL, window_type_, 0, bounds);
if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
- test::TestLauncherDelegate* launcher_delegate =
- test::TestLauncherDelegate::instance();
- launcher_delegate->AddLauncherItem(window);
+ test::TestShelfDelegate* shelf_delegate =
+ test::TestShelfDelegate::instance();
+ shelf_delegate->AddLauncherItem(window);
PanelLayoutManager* manager =
static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
layout_manager());
@@ -89,9 +89,9 @@ class DockedWindowLayoutManagerTest
aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
delegate, window_type_, 0, bounds);
if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
- test::TestLauncherDelegate* launcher_delegate =
- test::TestLauncherDelegate::instance();
- launcher_delegate->AddLauncherItem(window);
+ test::TestShelfDelegate* shelf_delegate =
+ test::TestShelfDelegate::instance();
+ shelf_delegate->AddLauncherItem(window);
PanelLayoutManager* manager =
static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
layout_manager());
diff --git a/ash/wm/dock/docked_window_resizer.cc b/ash/wm/dock/docked_window_resizer.cc
index 3380287f38..cd06beec37 100644
--- a/ash/wm/dock/docked_window_resizer.cc
+++ b/ash/wm/dock/docked_window_resizer.cc
@@ -75,23 +75,14 @@ void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) {
}
gfx::Point offset;
gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
- bool set_tracked_by_workspace = MaybeSnapToEdge(bounds, &offset);
-
- // Temporarily clear kWindowTrackedByWorkspaceKey for windows that are snapped
- // to screen edges e.g. when they are docked. This prevents the windows from
- // getting snapped to other nearby windows during the drag.
- wm::WindowState* window_state = wm::GetWindowState(GetTarget());
- bool was_tracked_by_workspace = window_state->tracked_by_workspace();
- if (set_tracked_by_workspace)
- window_state->SetTrackedByWorkspace(false);
- gfx::Point modified_location(location.x() + offset.x(),
- location.y() + offset.y());
+ MaybeSnapToEdge(bounds, &offset);
+ gfx::Point modified_location(location);
+ modified_location.Offset(offset.x(), offset.y());
base::WeakPtr<DockedWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
next_window_resizer_->Drag(modified_location, event_flags);
if (!resizer)
return;
- window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
DockedWindowLayoutManager* new_dock_layout =
GetDockedLayoutManagerAtPoint(last_location_);
@@ -125,25 +116,12 @@ void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) {
}
void DockedWindowResizer::CompleteDrag(int event_flags) {
- // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
- // don't get forced into the workspace that may be shrunken because of docked
- // windows.
- wm::WindowState* window_state = wm::GetWindowState(GetTarget());
- bool was_tracked_by_workspace = window_state->tracked_by_workspace();
- window_state->SetTrackedByWorkspace(false);
// The root window can change when dragging into a different screen.
next_window_resizer_->CompleteDrag(event_flags);
FinishedDragging();
- window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
}
void DockedWindowResizer::RevertDrag() {
- // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
- // don't get forced into the workspace that may be shrunken because of docked
- // windows.
- wm::WindowState* window_state = wm::GetWindowState(GetTarget());
- bool was_tracked_by_workspace = window_state->tracked_by_workspace();
- window_state->SetTrackedByWorkspace(false);
next_window_resizer_->RevertDrag();
// Restore docked state to what it was before the drag if necessary.
if (is_docked_ != was_docked_) {
@@ -154,7 +132,6 @@ void DockedWindowResizer::RevertDrag() {
dock_layout_->UndockDraggedWindow();
}
FinishedDragging();
- window_state->SetTrackedByWorkspace(was_tracked_by_workspace);
}
aura::Window* DockedWindowResizer::GetTarget() {
@@ -188,11 +165,11 @@ DockedWindowResizer::DockedWindowResizer(WindowResizer* next_window_resizer,
is_docked_ = was_docked_;
}
-bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
+void DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
gfx::Point* offset) {
// Windows only snap magnetically when they were previously docked.
if (!was_docked_)
- return false;
+ return;
DockedAlignment dock_alignment = dock_layout_->CalculateAlignment();
gfx::Rect dock_bounds = ScreenAsh::ConvertRectFromScreen(
GetTarget()->parent(),
@@ -207,18 +184,15 @@ bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
const int distance = bounds.x() - dock_bounds.x();
if (distance < kSnapToDockDistance && distance > 0) {
offset->set_x(-distance);
- return true;
+ return;
}
}
if (dock_alignment == DOCKED_ALIGNMENT_RIGHT ||
dock_alignment == DOCKED_ALIGNMENT_NONE) {
const int distance = dock_bounds.right() - bounds.right();
- if (distance < kSnapToDockDistance && distance > 0) {
+ if (distance < kSnapToDockDistance && distance > 0)
offset->set_x(distance);
- return true;
- }
}
- return false;
}
void DockedWindowResizer::StartedDragging() {
@@ -246,7 +220,9 @@ void DockedWindowResizer::StartedDragging() {
aura::Window* docked_container = Shell::GetContainer(
GetTarget()->GetRootWindow(),
kShellWindowId_DockedContainer);
- wm::ReparentChildWithTransientChildren(docked_container, GetTarget());
+ wm::ReparentChildWithTransientChildren(GetTarget(),
+ GetTarget()->parent(),
+ docked_container);
}
if (is_docked_)
dock_layout_->DockDraggedWindow(GetTarget());
@@ -313,7 +289,9 @@ DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion(
if ((is_resized || !is_attached_panel) &&
is_docked_ != (window->parent() == dock_container)) {
if (is_docked_) {
- wm::ReparentChildWithTransientChildren(dock_container, window);
+ wm::ReparentChildWithTransientChildren(window,
+ window->parent(),
+ dock_container);
action = DOCKED_ACTION_DOCK;
} else if (window->parent()->id() == kShellWindowId_DockedContainer) {
// Reparent the window back to workspace.
@@ -326,8 +304,11 @@ DockedAction DockedWindowResizer::MaybeReparentWindowOnDragCompletion(
// Reparenting will cause Relayout and possible dock shrinking.
aura::Window* previous_parent = window->parent();
aura::client::ParentWindowWithContext(window, window, near_last_location);
- if (window->parent() != previous_parent)
- wm::ReparentTransientChildrenOfChild(window->parent(), window);
+ if (window->parent() != previous_parent) {
+ wm::ReparentTransientChildrenOfChild(window,
+ previous_parent,
+ window->parent());
+ }
action = was_docked_ ? DOCKED_ACTION_UNDOCK : DOCKED_ACTION_NONE;
}
} else {
diff --git a/ash/wm/dock/docked_window_resizer.h b/ash/wm/dock/docked_window_resizer.h
index b5e5ece697..929259e59e 100644
--- a/ash/wm/dock/docked_window_resizer.h
+++ b/ash/wm/dock/docked_window_resizer.h
@@ -54,9 +54,9 @@ class ASH_EXPORT DockedWindowResizer : public WindowResizer {
DockedWindowResizer(WindowResizer* next_window_resizer,
const Details& details);
- // Checks if the provided window bounds should snap to the side of a screen.
- // If so the offset returned gives the necessary adjustment to snap.
- bool MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset);
+ // If the provided window bounds should snap to the side of a screen,
+ // returns the offset that gives the necessary adjustment to snap.
+ void MaybeSnapToEdge(const gfx::Rect& bounds, gfx::Point* offset);
// Tracks the window's initial position and attachment at the start of a drag
// and informs the DockLayoutManager that a drag has started if necessary.
diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc
index 0c1a4c6f3a..82b726e1fe 100644
--- a/ash/wm/dock/docked_window_resizer_unittest.cc
+++ b/ash/wm/dock/docked_window_resizer_unittest.cc
@@ -17,15 +17,17 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/cursor_manager_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/dock/docked_window_layout_manager.h"
#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/panels/panel_layout_manager.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/snap_sizer.h"
#include "base/command_line.h"
#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/window_tree_client.h"
#include "ui/aura/root_window.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/base/hit_test.h"
@@ -76,9 +78,9 @@ class DockedWindowResizerTest
0,
bounds);
if (window_type_ == aura::client::WINDOW_TYPE_PANEL) {
- test::TestLauncherDelegate* launcher_delegate =
- test::TestLauncherDelegate::instance();
- launcher_delegate->AddLauncherItem(window);
+ test::TestShelfDelegate* shelf_delegate =
+ test::TestShelfDelegate::instance();
+ shelf_delegate->AddLauncherItem(window);
PanelLayoutManager* manager =
static_cast<PanelLayoutManager*>(
Shell::GetContainer(window->GetRootWindow(),
@@ -89,6 +91,28 @@ class DockedWindowResizerTest
return window;
}
+ aura::Window* CreateModalWindow(const gfx::Rect& bounds) {
+ aura::Window* window = new aura::Window(&delegate_);
+ window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+ window->SetType(aura::client::WINDOW_TYPE_NORMAL);
+ window->Init(ui::LAYER_TEXTURED);
+ window->Show();
+
+ if (bounds.IsEmpty()) {
+ ParentWindowInPrimaryRootWindow(window);
+ } else {
+ gfx::Display display =
+ Shell::GetScreen()->GetDisplayMatching(bounds);
+ aura::Window* root = ash::Shell::GetInstance()->display_controller()->
+ GetRootWindowForDisplayId(display.id());
+ gfx::Point origin = bounds.origin();
+ wm::ConvertPointFromScreen(root, &origin);
+ window->SetBounds(gfx::Rect(origin, bounds.size()));
+ aura::client::ParentWindowWithContext(window, root, bounds);
+ }
+ return window;
+ }
+
static WindowResizer* CreateSomeWindowResizer(
aura::Window* window,
const gfx::Point& point_in_parent,
@@ -238,7 +262,7 @@ TEST_P(DockedWindowResizerTest, AttachRightPrecise) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -253,7 +277,7 @@ TEST_P(DockedWindowResizerTest, AttachRightOvershoot) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), +4);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -294,7 +318,7 @@ TEST_P(DockedWindowResizerTest, AttachLeftPrecise) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), 0);
- // The window should be attached and snapped to the left dock.
+ // The window should be docked at the left edge.
EXPECT_EQ(window->GetRootWindow()->bounds().x(),
window->GetBoundsInScreen().x());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -309,7 +333,7 @@ TEST_P(DockedWindowResizerTest, AttachLeftOvershoot) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_LEFT, window.get(), -4);
- // The window should be attached and snapped to the left dock.
+ // The window should be docked at the left edge.
EXPECT_EQ(window->GetRootWindow()->bounds().x(),
window->GetBoundsInScreen().x());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -341,7 +365,7 @@ TEST_P(DockedWindowResizerTest, AttachRightChangeShelf) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -381,7 +405,7 @@ TEST_P(DockedWindowResizerTest, AttachTryDetach) {
gfx::Rect(0, 0, ideal_width() + 10, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and docked at the right edge.
+ // The window should be docked at the right edge.
// Its width should shrink to ideal width.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
@@ -423,7 +447,7 @@ TEST_P(DockedWindowResizerTest, AttachMinimizeRestore) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -449,7 +473,7 @@ TEST_P(DockedWindowResizerTest, AttachMaximize) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -473,7 +497,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindows) {
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 50);
- // Both windows should be attached and snapped to the right edge.
+ // Both windows should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -509,7 +533,7 @@ TEST_P(DockedWindowResizerTest, AttachOneAutoHideShelf) {
scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // w1 should be attached and snapped to the right edge.
+ // w1 should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -554,7 +578,7 @@ TEST_P(DockedWindowResizerTest, AttachOnTwoSides) {
gfx::Rect initial_bounds(w2->bounds());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w2.get(), 50);
- // The first window should be attached and snapped to the right edge.
+ // The first window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -575,7 +599,7 @@ TEST_P(DockedWindowResizerTest, RevertDragRestoresAttachment) {
scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -621,7 +645,7 @@ TEST_P(DockedWindowResizerTest, DragAcrossDisplays) {
EXPECT_EQ(root_windows[0], window->GetRootWindow());
DragRelativeToEdge(DOCKED_EDGE_RIGHT, window.get(), 0);
- // The window should be attached and snapped to the right edge.
+ // The window should be docked at the right edge.
EXPECT_EQ(window->GetRootWindow()->bounds().right(),
window->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
@@ -686,7 +710,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindowsDetachOne) {
ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -696,7 +720,7 @@ TEST_P(DockedWindowResizerTest, AttachTwoWindowsDetachOne) {
EXPECT_EQ(w1->bounds().width(), docked_width(manager));
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
- // Both windows should now be attached and snapped to the right edge.
+ // Both windows should now be docked at the right edge.
EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
w2->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
@@ -758,7 +782,7 @@ TEST_P(DockedWindowResizerTest, AttachWindowMaximizeOther) {
ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -857,7 +881,7 @@ TEST_P(DockedWindowResizerTest, AttachOneTestSticky) {
ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_LEFT, w1.get(), 20);
- // A window should be attached and snapped to the left edge.
+ // A window should be docked at the left edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().x(),
w1->GetBoundsInScreen().x());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -956,7 +980,7 @@ TEST_P(DockedWindowResizerTest, ResizeOneOfTwoWindows) {
ScreenAsh::GetDisplayWorkAreaBoundsInParent(w2.get()).width());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -966,7 +990,7 @@ TEST_P(DockedWindowResizerTest, ResizeOneOfTwoWindows) {
EXPECT_EQ(w1->bounds().width(), docked_width(manager));
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w2.get(), 100);
- // Both windows should now be attached and snapped to the right edge.
+ // Both windows should now be docked at the right edge.
EXPECT_EQ(w2->GetRootWindow()->bounds().right(),
w2->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w2->parent()->id());
@@ -1097,7 +1121,7 @@ TEST_P(DockedWindowResizerTest, ResizingKeepsWidth) {
scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->GetBoundsInScreen().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -1226,7 +1250,7 @@ TEST_P(DockedWindowResizerTest, DragToShelf) {
ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).width());
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
w1->GetBoundsInScreen().right());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
@@ -1278,7 +1302,7 @@ TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) {
DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, window.get(), 20);
- // A window should be attached and snapped to the right edge.
+ // A window should be docked at the right edge.
EXPECT_EQ(internal::kShellWindowId_DockedContainer, window->parent()->id());
EXPECT_EQ(internal::kShellWindowId_DockedContainer, child->parent()->id());
@@ -1302,6 +1326,105 @@ TEST_P(DockedWindowResizerTest, DragWindowWithTransientChild) {
child->GetBoundsInScreen().origin().ToString());
}
+// Tests that reparenting windows during the drag does not affect system modal
+// windows that are transient children of the dragged windows.
+TEST_P(DockedWindowResizerTest, DragWindowWithModalTransientChild) {
+ if (!SupportsHostWindowResize())
+ return;
+
+ // Create a window.
+ scoped_ptr<aura::Window> window(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
+ gfx::Rect bounds(window->bounds());
+
+ // Start dragging the window.
+ ASSERT_NO_FATAL_FAILURE(DragStart(window.get()));
+ gfx::Vector2d move_vector(40, test_panels() ? -60 : 60);
+ DragMove(move_vector.x(), move_vector.y());
+ EXPECT_EQ(CorrectContainerIdDuringDrag(), window->parent()->id());
+
+ // While still dragging create a modal window and make it a transient child of
+ // the |window|.
+ scoped_ptr<aura::Window> child(CreateModalWindow(gfx::Rect(20, 20, 150, 20)));
+ window->AddTransientChild(child.get());
+ EXPECT_EQ(window.get(), child->transient_parent());
+ EXPECT_EQ(internal::kShellWindowId_SystemModalContainer,
+ child->parent()->id());
+
+ // End the drag, the |window| should have moved (if it is a panel it will
+ // no longer be attached to the shelf since we dragged it above).
+ DragEnd();
+ bounds.Offset(move_vector);
+ EXPECT_EQ(bounds.ToString(), window->GetBoundsInScreen().ToString());
+
+ // The original |window| should be in the default container (not docked or
+ // attached).
+ EXPECT_EQ(internal::kShellWindowId_DefaultContainer, window->parent()->id());
+ // The transient |child| should still be in system modal container.
+ EXPECT_EQ(internal::kShellWindowId_SystemModalContainer,
+ child->parent()->id());
+ // The |child| should not have moved.
+ EXPECT_EQ(gfx::Point(20, 20).ToString(),
+ child->GetBoundsInScreen().origin().ToString());
+ // The |child| should still be a transient child of |window|.
+ EXPECT_EQ(window.get(), child->transient_parent());
+}
+
+// Tests that side snapping a window undocks it, closes the dock and then snaps.
+TEST_P(DockedWindowResizerTest, SideSnapDocked) {
+ if (!SupportsHostWindowResize() || test_panels())
+ return;
+
+ scoped_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 201, 201)));
+ wm::WindowState* window_state = wm::GetWindowState(w1.get());
+ DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
+ // A window should be docked at the right edge.
+ EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
+ w1->GetBoundsInScreen().right());
+ EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
+ DockedWindowLayoutManager* manager =
+ static_cast<DockedWindowLayoutManager*>(w1->parent()->layout_manager());
+ EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
+ EXPECT_EQ(w1->bounds().width(), docked_width(manager));
+ EXPECT_TRUE(window_state->IsDocked());
+ EXPECT_FALSE(window_state->IsSnapped());
+
+ // Side snap at right edge.
+ internal::SnapSizer::SnapWindow(window_state,
+ internal::SnapSizer::RIGHT_EDGE);
+ // The window should be snapped at the right edge and the dock should close.
+ gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()));
+ EXPECT_EQ(0, docked_width(manager));
+ EXPECT_EQ(work_area.height(), w1->bounds().height());
+ EXPECT_EQ(work_area.right(), w1->bounds().right());
+ EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id());
+ EXPECT_FALSE(window_state->IsDocked());
+ EXPECT_TRUE(window_state->IsSnapped());
+
+ // Dock again.
+ DragToVerticalPositionAndToEdge(DOCKED_EDGE_RIGHT, w1.get(), 20);
+ // A window should be docked at the right edge.
+ EXPECT_EQ(w1->GetRootWindow()->bounds().right(),
+ w1->GetBoundsInScreen().right());
+ EXPECT_EQ(internal::kShellWindowId_DockedContainer, w1->parent()->id());
+ EXPECT_EQ(DOCKED_ALIGNMENT_RIGHT, docked_alignment(manager));
+ EXPECT_EQ(w1->bounds().width(), docked_width(manager));
+ EXPECT_TRUE(window_state->IsDocked());
+ EXPECT_FALSE(window_state->IsSnapped());
+
+ // Side snap at left edge.
+ internal::SnapSizer::SnapWindow(window_state,
+ internal::SnapSizer::LEFT_EDGE);
+ // The window should be snapped at the right edge and the dock should close.
+ EXPECT_EQ(work_area.ToString(),
+ ScreenAsh::GetDisplayWorkAreaBoundsInParent(w1.get()).ToString());
+ EXPECT_EQ(0, docked_width(manager));
+ EXPECT_EQ(work_area.height(), w1->bounds().height());
+ EXPECT_EQ(work_area.x(), w1->bounds().x());
+ EXPECT_EQ(internal::kShellWindowId_DefaultContainer, w1->parent()->id());
+ EXPECT_FALSE(window_state->IsDocked());
+ EXPECT_TRUE(window_state->IsSnapped());
+}
+
// Tests run twice - on both panels and normal windows
INSTANTIATE_TEST_CASE_P(NormalOrPanel,
DockedWindowResizerTest,
diff --git a/ash/wm/gestures/long_press_affordance_handler.cc b/ash/wm/gestures/long_press_affordance_handler.cc
index 45a995e692..2a96f0707f 100644
--- a/ash/wm/gestures/long_press_affordance_handler.cc
+++ b/ash/wm/gestures/long_press_affordance_handler.cc
@@ -65,6 +65,7 @@ views::Widget* CreateAffordanceWidget(aura::Window* root_window) {
params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
params.keep_on_top = true;
params.accept_events = false;
+ params.can_activate = false;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.context = root_window;
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
diff --git a/ash/wm/gestures/shelf_gesture_handler.cc b/ash/wm/gestures/shelf_gesture_handler.cc
index 9dcd3f2794..f9fb1b8d43 100644
--- a/ash/wm/gestures/shelf_gesture_handler.cc
+++ b/ash/wm/gestures/shelf_gesture_handler.cc
@@ -43,7 +43,7 @@ bool ShelfGestureHandler::ProcessGestureEvent(const ui::GestureEvent& event) {
ShelfLayoutManager* shelf = controller->GetShelfLayoutManager();
- const aura::Window* fullscreen = controller->GetTopmostFullscreenWindow();
+ const aura::Window* fullscreen = controller->GetWindowForFullscreenMode();
if (fullscreen &&
ash::wm::GetWindowState(fullscreen)->hide_shelf_when_fullscreen()) {
return false;
diff --git a/ash/wm/header_painter.cc b/ash/wm/header_painter.cc
index fb0b2c4ce8..bf87015b17 100644
--- a/ash/wm/header_painter.cc
+++ b/ash/wm/header_painter.cc
@@ -9,7 +9,6 @@
#include "ash/root_window_controller.h"
#include "ash/wm/caption_buttons/frame_caption_button_container_view.h"
#include "ash/wm/solo_window_tracker.h"
-#include "ash/wm/window_state.h"
#include "base/logging.h" // DCHECK
#include "grit/ash_resources.h"
#include "third_party/skia/include/core/SkCanvas.h"
@@ -165,10 +164,8 @@ HeaderPainter::HeaderPainter()
HeaderPainter::~HeaderPainter() {
// Sometimes we are destroyed before the window closes, so ensure we clean up.
- if (window_) {
+ if (window_)
window_->RemoveObserver(this);
- wm::GetWindowState(window_)->RemoveObserver(this);
- }
}
void HeaderPainter::Init(
@@ -203,7 +200,6 @@ void HeaderPainter::Init(
// Observer removes itself in OnWindowDestroying() below, or in the destructor
// if we go away before the window.
window_->AddObserver(this);
- wm::GetWindowState(window_)->AddObserver(this);
// Solo-window header updates are handled by the WorkspaceLayoutManager when
// this window is added to the desktop.
@@ -262,17 +258,6 @@ int HeaderPainter::GetThemeBackgroundXInset() const {
return kThemeFrameImageInsetX;
}
-bool HeaderPainter::ShouldUseMinimalHeaderStyle(Themed header_themed) const {
- // Use the minimalistic header style whenever |frame_| is maximized or
- // fullscreen EXCEPT:
- // - If the user has installed a theme with custom images for the header.
- // - For windows which are not tracked by the workspace code (which are used
- // for tab dragging).
- return (frame_->IsMaximized() || frame_->IsFullscreen()) &&
- header_themed == THEMED_NO &&
- wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace();
-}
-
void HeaderPainter::PaintHeader(gfx::Canvas* canvas,
HeaderMode header_mode,
int theme_frame_id,
@@ -433,7 +418,7 @@ void HeaderPainter::PaintTitleBar(gfx::Canvas* canvas,
views::WidgetDelegate* delegate = frame_->widget_delegate();
if (delegate && delegate->ShouldShowWindowTitle()) {
gfx::Rect title_bounds = GetTitleBounds(title_font);
- SkColor title_color = frame_->IsMaximized() ?
+ SkColor title_color = (frame_->IsMaximized() || frame_->IsFullscreen()) ?
kMaximizedWindowTitleTextColor : kNonMaximizedWindowTitleTextColor;
canvas->DrawStringInt(delegate->GetWindowTitle(),
title_font,
@@ -489,16 +474,6 @@ void HeaderPainter::OnThemeChanged() {
}
///////////////////////////////////////////////////////////////////////////////
-// WindowStateObserver overrides:
-void HeaderPainter::OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
- bool old) {
- // When 'TrackedByWorkspace' changes, we are going to paint the header
- // differently. Schedule a paint to ensure everything is updated correctly.
- if (window_state->tracked_by_workspace())
- header_view_->SchedulePaint();
-}
-
-///////////////////////////////////////////////////////////////////////////////
// aura::WindowObserver overrides:
void HeaderPainter::OnWindowDestroying(aura::Window* destroying) {
@@ -507,7 +482,6 @@ void HeaderPainter::OnWindowDestroying(aura::Window* destroying) {
// Must be removed here and not in the destructor, as the aura::Window is
// already destroyed when our destructor runs.
window_->RemoveObserver(this);
- wm::GetWindowState(window_)->RemoveObserver(this);
window_ = NULL;
}
@@ -550,11 +524,7 @@ int HeaderPainter::GetCaptionButtonContainerCenterY() const {
}
int HeaderPainter::GetHeaderCornerRadius() const {
- // Use square corners for maximized and fullscreen windows when they are
- // tracked by the workspace code. (Windows which are not tracked by the
- // workspace code are used for tab dragging.)
- bool square_corners = ((frame_->IsMaximized() || frame_->IsFullscreen())) &&
- wm::GetWindowState(frame_->GetNativeWindow())->tracked_by_workspace();
+ bool square_corners = (frame_->IsMaximized() || frame_->IsFullscreen());
const int kCornerRadius = 2;
return square_corners ? 0 : kCornerRadius;
}
@@ -571,8 +541,8 @@ int HeaderPainter::GetHeaderOpacity(
return kFullyOpaque;
}
- // The header is fully opaque when using the minimalistic header style.
- if (ShouldUseMinimalHeaderStyle(THEMED_NO))
+ // Maximized and fullscreen windows are fully opaque.
+ if (frame_->IsMaximized() || frame_->IsFullscreen())
return kFullyOpaque;
// Solo header is very transparent.
diff --git a/ash/wm/header_painter.h b/ash/wm/header_painter.h
index ea326af9d6..accc6e370d 100644
--- a/ash/wm/header_painter.h
+++ b/ash/wm/header_painter.h
@@ -6,7 +6,6 @@
#define ASH_WM_HEADER_PAINTER_H_
#include "ash/ash_export.h"
-#include "ash/wm/window_state_observer.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h" // OVERRIDE
#include "base/gtest_prod_util.h"
@@ -36,8 +35,7 @@ class FrameCaptionButtonContainerView;
// Helper class for painting the window header.
class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
- public gfx::AnimationDelegate,
- public wm::WindowStateObserver {
+ public gfx::AnimationDelegate {
public:
// Opacity values for the window header in various states, from 0 to 255.
static int kActiveWindowOpacity;
@@ -49,11 +47,6 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
INACTIVE
};
- enum Themed {
- THEMED_YES,
- THEMED_NO
- };
-
HeaderPainter();
virtual ~HeaderPainter();
@@ -92,9 +85,6 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
// Returns the amount that the theme background should be inset.
int GetThemeBackgroundXInset() const;
- // Returns true if the header should be painted using a minimalistic style.
- bool ShouldUseMinimalHeaderStyle(Themed header_themed) const;
-
// Paints the header.
// |theme_frame_overlay_id| is 0 if no overlay image should be used.
void PaintHeader(gfx::Canvas* canvas,
@@ -124,6 +114,11 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
header_height_ = header_height;
}
+ // Returns the header height.
+ int header_height() const {
+ return header_height_;
+ }
+
// Schedule a re-paint of the entire title.
void SchedulePaintForTitle(const gfx::Font& title_font);
@@ -136,27 +131,12 @@ class ASH_EXPORT HeaderPainter : public aura::WindowObserver,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE;
- // ash::WindowStateObserver override:
- virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
- bool old) OVERRIDE;
-
// Overridden from gfx::AnimationDelegate
virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
private:
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, CreateAndDeleteSingleWindow);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeader);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithApp);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderWithPanel);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderModal);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderConstrained);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderNotDrawn);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, UseSoloWindowHeaderMultiDisplay);
FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, GetHeaderOpacity);
FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, TitleIconAlignment);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest, ChildWindowVisibility);
- FRIEND_TEST_ALL_PREFIXES(HeaderPainterTest,
- NoCrashShutdownWithAlwaysOnTopWindow);
// Returns the header bounds in the coordinates of |header_view_|. The header
// is assumed to be positioned at the top left corner of |header_view_| and to
diff --git a/ash/wm/header_painter_unittest.cc b/ash/wm/header_painter_unittest.cc
index 551658a8d0..8ce8917a58 100644
--- a/ash/wm/header_painter_unittest.cc
+++ b/ash/wm/header_painter_unittest.cc
@@ -123,29 +123,6 @@ TEST_F(HeaderPainterTest, GetHeaderOpacity) {
0));
}
-// Test that the minimal header style is used in the proper situations.
-TEST_F(HeaderPainterTest, MinimalHeaderStyle) {
- // Create a widget and a painter for it.
- scoped_ptr<Widget> w(CreateTestWidget());
- scoped_ptr<HeaderPainter> p(CreateTestPainter(w.get()));
- w->Show();
-
- // Regular non-maximized windows should not use the minimal header style.
- EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
-
- // Regular maximized windows should use the minimal header style.
- w->Maximize();
- EXPECT_TRUE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
-
- // Test cases where the maximized window should not use the minimal header
- // style.
- EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_YES));
-
- wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(false);
- EXPECT_FALSE(p->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO));
- wm::GetWindowState(w->GetNativeWindow())->SetTrackedByWorkspace(true);
-}
-
// Ensure the title text is vertically aligned with the window icon.
TEST_F(HeaderPainterTest, TitleIconAlignment) {
scoped_ptr<Widget> w(CreateTestWidget());
diff --git a/ash/wm/immersive_fullscreen_controller.cc b/ash/wm/immersive_fullscreen_controller.cc
index ba6eb3c0c1..28aa1dcd0d 100644
--- a/ash/wm/immersive_fullscreen_controller.cc
+++ b/ash/wm/immersive_fullscreen_controller.cc
@@ -287,8 +287,12 @@ void ImmersiveFullscreenController::SetEnabled(bool enabled) {
if (reveal_state_ == REVEALED) {
// Reveal was unsuccessful. Reacquire the revealed locks if appropriate.
- UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+ UpdateLocatedEventRevealedLock(NULL);
UpdateFocusRevealedLock();
+ } else {
+ // Clearing focus is important because it closes focus-related popups like
+ // the touch selection handles.
+ widget_->GetFocusManager()->ClearFocus();
}
} else {
// Stop cursor-at-top tracking.
@@ -332,7 +336,7 @@ void ImmersiveFullscreenController::SetupForTest() {
}
gfx::Point cursor_pos(0, bottommost_in_screen + 100);
aura::Env::GetInstance()->set_last_mouse_location(cursor_pos);
- UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+ UpdateLocatedEventRevealedLock(NULL);
}
////////////////////////////////////////////////////////////////////////////////
@@ -349,30 +353,29 @@ void ImmersiveFullscreenController::OnMouseEvent(ui::MouseEvent* event) {
return;
}
- // Mouse hover should not initiate revealing the top-of-window views while
- // |native_window_| is inactive.
- if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
- return;
+ // Mouse hover can initiate revealing the top-of-window views while |widget_|
+ // is inactive.
- // Mouse hover should not initiate revealing the top-of-window views while
- // a window has mouse capture.
- if (aura::client::GetCaptureWindow(native_window_))
- return;
-
- if (IsRevealed())
- UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
-
- // Trigger a reveal if the cursor pauses at the top of the screen for a
- // while.
- if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED)
+ if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
+ top_edge_hover_timer_.Stop();
+ UpdateLocatedEventRevealedLock(event);
+ } else if (event->type() != ui::ET_MOUSE_CAPTURE_CHANGED) {
+ // Trigger a reveal if the cursor pauses at the top of the screen for a
+ // while.
UpdateTopEdgeHoverTimer(event);
+ }
}
void ImmersiveFullscreenController::OnTouchEvent(ui::TouchEvent* event) {
if (!enabled_ || event->type() != ui::ET_TOUCH_PRESSED)
return;
- UpdateLocatedEventRevealedLock(event, ALLOW_REVEAL_WHILE_CLOSING_NO);
+ // Touch should not initiate revealing the top-of-window views while |widget_|
+ // is inactive.
+ if (!widget_->IsActive())
+ return;
+
+ UpdateLocatedEventRevealedLock(event);
}
void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) {
@@ -380,15 +383,17 @@ void ImmersiveFullscreenController::OnGestureEvent(ui::GestureEvent* event) {
return;
// Touch gestures should not initiate revealing the top-of-window views while
- // |native_window_| is inactive.
- if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive())
+ // |widget_| is inactive.
+ if (!widget_->IsActive())
return;
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
if (ShouldHandleGestureEvent(GetEventLocationInScreen(*event))) {
gesture_begun_ = true;
- event->SetHandled();
+ // Do not consume the event. Otherwise, we end up consuming all
+ // ui::ET_GESTURE_SCROLL_BEGIN events in the top-of-window views
+ // when the top-of-window views are revealed.
}
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
@@ -436,21 +441,7 @@ void ImmersiveFullscreenController::OnWidgetDestroying(views::Widget* widget) {
void ImmersiveFullscreenController::OnWidgetActivationChanged(
views::Widget* widget,
bool active) {
- // Mouse hover should not initiate revealing the top-of-window views while
- // |native_window_| is inactive.
- top_edge_hover_timer_.Stop();
-
UpdateFocusRevealedLock();
-
- // Allow the top-of-window views to stay revealed if all of the revealed locks
- // were released in the process of activating |widget| but the mouse is still
- // hovered above the top-of-window views. For instance, if the bubble which
- // has been keeping the top-of-window views revealed is hidden but the mouse
- // is hovered above the top-of-window views, the top-of-window views should
- // stay revealed. We cannot call UpdateLocatedEventRevealedLock() from
- // BubbleManager::UpdateRevealedLock() because |widget| is not yet active
- // at that time.
- UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_YES);
}
////////////////////////////////////////////////////////////////////////////////
@@ -520,19 +511,17 @@ void ImmersiveFullscreenController::EnableWindowObservers(bool enable) {
return;
observers_enabled_ = enable;
- views::Widget* widget =
- views::Widget::GetWidgetForNativeWindow(native_window_);
- views::FocusManager* focus_manager = widget->GetFocusManager();
+ views::FocusManager* focus_manager = widget_->GetFocusManager();
if (enable) {
- widget->AddObserver(this);
+ widget_->AddObserver(this);
focus_manager->AddFocusChangeListener(this);
Shell::GetInstance()->AddPreTargetHandler(this);
native_window_->AddObserver(this);
RecreateBubbleManager();
} else {
- widget->RemoveObserver(this);
+ widget_->RemoveObserver(this);
focus_manager->RemoveFocusChangeListener(this);
Shell::GetInstance()->RemovePreTargetHandler(this);
native_window_->RemoveObserver(this);
@@ -550,12 +539,22 @@ void ImmersiveFullscreenController::EnableWindowObservers(bool enable) {
void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer(
ui::MouseEvent* event) {
DCHECK(enabled_);
- // Stop the timer if the top-of-window views are already revealed.
- if (reveal_state_ == SLIDING_OPEN || reveal_state_ == REVEALED) {
- top_edge_hover_timer_.Stop();
+ DCHECK(reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED);
+
+ // Check whether |native_window_| is the event target's parent window instead
+ // of checking for activation. This allows the timer to be started when
+ // |widget_| is inactive but prevents starting the timer if the mouse is over
+ // a portion of the top edge obscured by an unrelated widget.
+ if (!top_edge_hover_timer_.IsRunning() &&
+ !native_window_->Contains(static_cast<aura::Window*>(event->target()))) {
return;
}
+ // Mouse hover should not initiate revealing the top-of-window views while a
+ // window has mouse capture.
+ if (aura::client::GetCaptureWindow(native_window_))
+ return;
+
gfx::Point location_in_screen = GetEventLocationInScreen(*event);
if (ShouldIgnoreMouseEventAtLocation(location_in_screen))
return;
@@ -591,8 +590,7 @@ void ImmersiveFullscreenController::UpdateTopEdgeHoverTimer(
}
void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock(
- ui::LocatedEvent* event,
- AllowRevealWhileClosing allow_reveal_while_closing) {
+ ui::LocatedEvent* event) {
if (!enabled_)
return;
DCHECK(!event || event->IsMouseEvent() || event->IsTouchEvent());
@@ -601,18 +599,14 @@ void ImmersiveFullscreenController::UpdateLocatedEventRevealedLock(
// views are sliding closed or are closed with the following exceptions:
// - Hovering at y = 0 which is handled in OnMouseEvent().
// - Doing a SWIPE_OPEN edge gesture which is handled in OnGestureEvent().
- if (reveal_state_ == CLOSED ||
- (reveal_state_ == SLIDING_CLOSED &&
- allow_reveal_while_closing == ALLOW_REVEAL_WHILE_CLOSING_NO)) {
+ if (reveal_state_ == CLOSED || reveal_state_ == SLIDING_CLOSED)
return;
- }
- // Neither the mouse nor touch should keep the top-of-window views revealed if
- // |native_window_| is not active.
- if (!views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive()) {
- located_event_revealed_lock_.reset();
- return;
- }
+ // For the sake of simplicity, ignore |widget_|'s activation in computing
+ // whether the top-of-window views should stay revealed. Ideally, the
+ // top-of-window views would stay revealed only when the mouse cursor is
+ // hovered above a non-obscured portion of the top-of-window views. The
+ // top-of-window views may be partially obscured when |widget_| is inactive.
// Ignore all events while a window has capture. This keeps the top-of-window
// views revealed during a drag.
@@ -679,10 +673,8 @@ void ImmersiveFullscreenController::UpdateFocusRevealedLock() {
return;
bool hold_lock = false;
- views::Widget* widget =
- views::Widget::GetWidgetForNativeWindow(native_window_);
- if (widget->IsActive()) {
- views::View* focused_view = widget->GetFocusManager()->GetFocusedView();
+ if (widget_->IsActive()) {
+ views::View* focused_view = widget_->GetFocusManager()->GetFocusedView();
if (top_container_->Contains(focused_view))
hold_lock = true;
} else {
@@ -729,7 +721,7 @@ bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe(
// Swipes while |native_window_| is inactive should have been filtered out in
// OnGestureEvent().
- DCHECK(views::Widget::GetWidgetForNativeWindow(native_window_)->IsActive());
+ DCHECK(widget_->IsActive());
if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
if (swipe_type == SWIPE_OPEN && !located_event_revealed_lock_.get()) {
@@ -743,11 +735,13 @@ bool ImmersiveFullscreenController::UpdateRevealedLocksForSwipe(
located_event_revealed_lock_.reset();
focus_revealed_lock_.reset();
- if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED)
+ if (reveal_state_ == SLIDING_CLOSED || reveal_state_ == CLOSED) {
+ widget_->GetFocusManager()->ClearFocus();
return true;
+ }
// Ending the reveal was unsuccessful. Reaquire the locks if appropriate.
- UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+ UpdateLocatedEventRevealedLock(NULL);
UpdateFocusRevealedLock();
}
}
@@ -808,7 +802,7 @@ void ImmersiveFullscreenController::OnSlideOpenAnimationCompleted() {
// The user may not have moved the mouse since the reveal was initiated.
// Update the revealed lock to reflect the mouse's current state.
- UpdateLocatedEventRevealedLock(NULL, ALLOW_REVEAL_WHILE_CLOSING_NO);
+ UpdateLocatedEventRevealedLock(NULL);
}
void ImmersiveFullscreenController::MaybeEndReveal(Animate animate) {
@@ -880,6 +874,7 @@ bool ImmersiveFullscreenController::ShouldIgnoreMouseEventAtLocation(
bool ImmersiveFullscreenController::ShouldHandleGestureEvent(
const gfx::Point& location) const {
+ DCHECK(widget_->IsActive());
if (reveal_state_ == REVEALED) {
std::vector<gfx::Rect> hit_bounds_in_screen(
delegate_->GetVisibleBoundsInScreen());
diff --git a/ash/wm/immersive_fullscreen_controller.h b/ash/wm/immersive_fullscreen_controller.h
index 9d0f757c1e..7e0ca78bd9 100644
--- a/ash/wm/immersive_fullscreen_controller.h
+++ b/ash/wm/immersive_fullscreen_controller.h
@@ -143,10 +143,6 @@ class ASH_EXPORT ImmersiveFullscreenController
private:
friend class ImmersiveFullscreenControllerTest;
- enum AllowRevealWhileClosing {
- ALLOW_REVEAL_WHILE_CLOSING_YES,
- ALLOW_REVEAL_WHILE_CLOSING_NO
- };
enum Animate {
ANIMATE_NO,
ANIMATE_SLOW,
@@ -176,12 +172,7 @@ class ASH_EXPORT ImmersiveFullscreenController
// Updates |located_event_revealed_lock_| based on the current mouse state and
// the current touch state.
// |event| is NULL if the source event is not known.
- // |allow_reveal_while_closing| indicates whether the mouse and touch
- // are allowed to initiate a reveal while the top-of-window views are sliding
- // closed.
- void UpdateLocatedEventRevealedLock(
- ui::LocatedEvent* event,
- AllowRevealWhileClosing allow_reveal_while_closing);
+ void UpdateLocatedEventRevealedLock(ui::LocatedEvent* event);
// Acquires |located_event_revealed_lock_| if it is not already held.
void AcquireLocatedEventRevealedLock();
diff --git a/ash/wm/immersive_fullscreen_controller_unittest.cc b/ash/wm/immersive_fullscreen_controller_unittest.cc
index 9d0b21b69e..672e976456 100644
--- a/ash/wm/immersive_fullscreen_controller_unittest.cc
+++ b/ash/wm/immersive_fullscreen_controller_unittest.cc
@@ -138,27 +138,12 @@ class ImmersiveFullscreenControllerTest : public ash::test::AshTestBase {
new MockImmersiveFullscreenControllerDelegate(top_container_));
controller_.reset(new ImmersiveFullscreenController);
controller_->Init(delegate_.get(), widget_, top_container_);
- SetAnimationsDisabled(true);
+ controller_->SetupForTest();
// The mouse is moved so that it is not over |top_container_| by
// AshTestBase.
}
- // Enable or disable the ImmersiveFullscreenController's animations. When the
- // ImmersiveFullscreenController's animations are disabled, some behavior is
- // slightly different. In particular, the behavior is different when there
- // is a transfer in which lock keeps the top-of-window views revealed (eg
- // bubble keeps top-of-window views revealed -> mouse keeps top-of-window
- // views revealed). It is necessary to temporarily enable the
- // ImmersiveFullscreenController's animations to get the correct behavior in
- // tests.
- void SetAnimationsDisabled(bool disabled) {
- controller_->animations_disabled_for_test_ = disabled;
- // Force any in progress animations to finish.
- if (disabled)
- controller_->animation_->End();
- }
-
// Attempt to reveal the top-of-window views via |modality|.
// The top-of-window views can only be revealed via mouse hover or a gesture.
void AttemptReveal(Modality modality) {
@@ -398,7 +383,7 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
top_container_bounds_in_screen.bottom() + 50);
EXPECT_FALSE(controller()->IsRevealed());
- // The mouse position cannot cause a reveal when TopContainerView's widget
+ // The mouse position cannot cause a reveal when the top container's widget
// has capture.
views::Widget* widget = top_container()->GetWidget();
widget->SetCapture(top_container());
@@ -406,7 +391,7 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
EXPECT_FALSE(controller()->IsRevealed());
widget->ReleaseCapture();
- // The mouse position cannot end the reveal while TopContainerView's widget
+ // The mouse position cannot end the reveal while the top container's widget
// has capture.
AttemptReveal(MODALITY_MOUSE);
EXPECT_TRUE(controller()->IsRevealed());
@@ -420,6 +405,64 @@ TEST_F(ImmersiveFullscreenControllerTest, OnMouseEvent) {
EXPECT_FALSE(controller()->IsRevealed());
}
+// Test mouse event processing for top-of-screen reveal triggering when the
+// top container's widget is inactive.
+TEST_F(ImmersiveFullscreenControllerTest, Inactive) {
+ // Set up initial state.
+ views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
+ NULL,
+ CurrentContext(),
+ gfx::Rect(0, 0, 200, 200));
+ popup_widget->Show();
+ ASSERT_FALSE(top_container()->GetWidget()->IsActive());
+
+ controller()->SetEnabled(true);
+ ASSERT_TRUE(controller()->IsEnabled());
+ ASSERT_FALSE(controller()->IsRevealed());
+
+ gfx::Rect top_container_bounds_in_screen =
+ top_container()->GetBoundsInScreen();
+ gfx::Rect popup_bounds_in_screen = popup_widget->GetWindowBoundsInScreen();
+ ASSERT_EQ(top_container_bounds_in_screen.origin().ToString(),
+ popup_bounds_in_screen.origin().ToString());
+ ASSERT_GT(top_container_bounds_in_screen.right(),
+ popup_bounds_in_screen.right());
+
+ // The top-of-window views should stay hidden if the cursor is at the top edge
+ // but above an obscured portion of the top-of-window views.
+ MoveMouse(popup_bounds_in_screen.x(),
+ top_container_bounds_in_screen.y());
+ EXPECT_FALSE(controller()->IsRevealed());
+
+ // The top-of-window views should reveal if the cursor is at the top edge and
+ // above an unobscured portion of the top-of-window views.
+ MoveMouse(top_container_bounds_in_screen.right() - 1,
+ top_container_bounds_in_screen.y());
+ EXPECT_TRUE(controller()->IsRevealed());
+
+ // The top-of-window views should stay revealed if the cursor is moved off
+ // of the top edge.
+ MoveMouse(top_container_bounds_in_screen.right() - 1,
+ top_container_bounds_in_screen.bottom() - 1);
+ EXPECT_TRUE(controller()->IsRevealed());
+
+ // Moving way off of the top-of-window views should end the immersive reveal.
+ MoveMouse(top_container_bounds_in_screen.right() - 1,
+ top_container_bounds_in_screen.bottom() + 50);
+ EXPECT_FALSE(controller()->IsRevealed());
+
+ // Moving way off of the top-of-window views in a region where the
+ // top-of-window views are obscured should also end the immersive reveal.
+ // Ideally, the immersive reveal would end immediately when the cursor moves
+ // to an obscured portion of the top-of-window views.
+ MoveMouse(top_container_bounds_in_screen.right() - 1,
+ top_container_bounds_in_screen.y());
+ EXPECT_TRUE(controller()->IsRevealed());
+ MoveMouse(top_container_bounds_in_screen.x(),
+ top_container_bounds_in_screen.bottom() + 50);
+ EXPECT_FALSE(controller()->IsRevealed());
+}
+
// Test mouse event processing for top-of-screen reveal triggering when the user
// has a vertical display layout (primary display above/below secondary display)
// and the immersive fullscreen window is on the bottom display.
@@ -495,6 +538,20 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseEventsVerticalDisplayLayout) {
// the bottom region of the secondary display.
event_generator.MoveMouseTo(x, y_top_edge - 20);
EXPECT_FALSE(controller()->IsRevealed());
+
+ // Test that it is possible to reveal the top-of-window views by overshooting
+ // the top edge slightly when the top container's widget is not active.
+ views::Widget* popup_widget = views::Widget::CreateWindowWithContextAndBounds(
+ NULL,
+ CurrentContext(),
+ gfx::Rect(0, 200, 100, 100));
+ popup_widget->Show();
+ ASSERT_FALSE(top_container()->GetWidget()->IsActive());
+ ASSERT_FALSE(top_container()->GetBoundsInScreen().Intersects(
+ popup_widget->GetWindowBoundsInScreen()));
+ event_generator.MoveMouseTo(x, y_top_edge + 1);
+ MoveMouse(x, y_top_edge - 2);
+ EXPECT_TRUE(controller()->IsRevealed());
}
// Test behavior when the mouse becomes hovered without moving.
@@ -517,9 +574,9 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
EXPECT_FALSE(controller()->IsRevealed());
// 2) Test that if the mouse becomes hovered without moving because of a
- // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true)
- // and there are no locks keeping the top-of-window views revealed, that mouse
- // hover does not prevent the top-of-window views from closing.
+ // reveal in ImmersiveFullscreenController::SetEnabled(true) and there are no
+ // locks keeping the top-of-window views revealed, that mouse hover does not
+ // prevent the top-of-window views from closing.
controller()->SetEnabled(false);
SetHovered(true);
EXPECT_FALSE(controller()->IsRevealed());
@@ -527,10 +584,9 @@ TEST_F(ImmersiveFullscreenControllerTest, MouseHoveredWithoutMoving) {
EXPECT_FALSE(controller()->IsRevealed());
// 3) Test that if the mouse becomes hovered without moving because of a
- // reveal in ImmersiveFullscreenController::controller()->SetEnabled(true)
- // and there is a lock keeping the top-of-window views revealed, that the
- // top-of-window views do not hide till the mouse moves off of the
- // top-of-window views.
+ // reveal in ImmersiveFullscreenController::SetEnabled(true) and there is a
+ // lock keeping the top-of-window views revealed, that the top-of-window views
+ // do not hide till the mouse moves off of the top-of-window views.
controller()->SetEnabled(false);
SetHovered(true);
lock.reset(controller()->GetRevealedLock(
@@ -593,7 +649,11 @@ TEST_F(ImmersiveFullscreenControllerTest, EndRevealViaGesture) {
EXPECT_TRUE(controller()->IsRevealed());
AttemptUnreveal(MODALITY_GESTURE);
EXPECT_FALSE(controller()->IsRevealed());
- top_container()->GetFocusManager()->ClearFocus();
+
+ // The top-of-window views should no longer have focus. Clearing focus is
+ // important because it closes focus-related popup windows like the touch
+ // selection handles.
+ EXPECT_FALSE(top_container()->HasFocus());
// If some other code is holding onto a lock, a gesture should not be able to
// end the reveal.
@@ -676,10 +736,9 @@ TEST_F(ImmersiveFullscreenControllerTest, Focus) {
EXPECT_FALSE(controller()->IsRevealed());
}
-// Test how activation affects whether the top-of-window views are revealed.
-// The behavior when a bubble is activated is tested in
-// ImmersiveFullscreenControllerTest.Bubbles.
-TEST_F(ImmersiveFullscreenControllerTest, Activation) {
+// Test how transient windows affect whether the top-of-window views are
+// revealed.
+TEST_F(ImmersiveFullscreenControllerTest, Transient) {
views::Widget* top_container_widget = top_container()->GetWidget();
controller()->SetEnabled(true);
@@ -692,36 +751,34 @@ TEST_F(ImmersiveFullscreenControllerTest, Activation) {
transient_params.ownership =
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
transient_params.parent = top_container_widget->GetNativeView();
- transient_params.bounds = gfx::Rect(0, 0, 100, 100);
+ transient_params.bounds = gfx::Rect(0, 100, 100, 100);
scoped_ptr<views::Widget> transient_widget(new views::Widget());
transient_widget->Init(transient_params);
- transient_widget->Show();
EXPECT_FALSE(controller()->IsRevealed());
- top_container_widget->Activate();
AttemptReveal(MODALITY_MOUSE);
EXPECT_TRUE(controller()->IsRevealed());
- transient_widget->Activate();
+ transient_widget->Show();
SetHovered(false);
EXPECT_TRUE(controller()->IsRevealed());
transient_widget.reset();
EXPECT_FALSE(controller()->IsRevealed());
- // 2) Test that activating a non-transient window ends the reveal if any.
+ // 2) Test that activating a non-transient window does not keep the
+ // top-of-window views revealed.
views::Widget::InitParams non_transient_params;
non_transient_params.ownership =
views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
non_transient_params.context = top_container_widget->GetNativeView();
- non_transient_params.bounds = gfx::Rect(0, 0, 100, 100);
+ non_transient_params.bounds = gfx::Rect(0, 100, 100, 100);
scoped_ptr<views::Widget> non_transient_widget(new views::Widget());
non_transient_widget->Init(non_transient_params);
- non_transient_widget->Show();
EXPECT_FALSE(controller()->IsRevealed());
- top_container_widget->Activate();
AttemptReveal(MODALITY_MOUSE);
EXPECT_TRUE(controller()->IsRevealed());
- non_transient_widget->Activate();
+ non_transient_widget->Show();
+ SetHovered(false);
EXPECT_FALSE(controller()->IsRevealed());
}
@@ -774,11 +831,7 @@ TEST_F(ImmersiveFullscreenControllerTest, Bubbles) {
bubble_widget3->Show();
SetHovered(true);
EXPECT_TRUE(controller()->IsRevealed());
-
- SetAnimationsDisabled(false);
- // Activating |top_container_widget| will close |bubble_widget3|.
top_container_widget->Activate();
- SetAnimationsDisabled(true);
EXPECT_TRUE(controller()->IsRevealed());
// 3) Test that the top-of-window views stay revealed as long as at least one
diff --git a/ash/wm/mru_window_tracker_unittest.cc b/ash/wm/mru_window_tracker_unittest.cc
index 0cebd9e285..924060db7b 100644
--- a/ash/wm/mru_window_tracker_unittest.cc
+++ b/ash/wm/mru_window_tracker_unittest.cc
@@ -4,7 +4,7 @@
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
diff --git a/ash/wm/overview/window_overview.cc b/ash/wm/overview/window_overview.cc
index e7d914b6b3..4542f18ad4 100644
--- a/ash/wm/overview/window_overview.cc
+++ b/ash/wm/overview/window_overview.cc
@@ -21,6 +21,7 @@
#include "ui/aura/window.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
+#include "ui/views/background.h"
#include "ui/views/widget/widget.h"
namespace ash {
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 79d428f204..db5255b3c1 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -300,6 +300,7 @@ WindowSelector::WindowSelector(const WindowList& windows,
}
if (mode == WindowSelector::CYCLE) {
+ cycle_start_time_ = base::Time::Now();
event_handler_.reset(new WindowSelectorEventFilter(this));
if (timer_enabled_)
start_overview_timer_.Reset();
@@ -327,6 +328,11 @@ WindowSelector::~WindowSelector() {
// Clearing the window list resets the ignored_by_shelf flag on the windows.
windows_.clear();
UpdateShelfVisibility();
+
+ if (!cycle_start_time_.is_null()) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime",
+ base::Time::Now() - cycle_start_time_);
+ }
}
void WindowSelector::Step(WindowSelector::Direction direction) {
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index 84b5aa9e02..faee73d1d7 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -118,6 +118,9 @@ class ASH_EXPORT WindowSelector
base::DelayTimer<WindowSelector> start_overview_timer_;
scoped_ptr<WindowOverview> window_overview_;
+ // The time when window cycling was started.
+ base::Time cycle_start_time_;
+
// Weak pointer to the selector delegate which will be called when a
// selection is made.
WindowSelectorDelegate* delegate_;
diff --git a/ash/wm/overview/window_selector_controller.cc b/ash/wm/overview/window_selector_controller.cc
index a148357669..fcbb9ae7b8 100644
--- a/ash/wm/overview/window_selector_controller.cc
+++ b/ash/wm/overview/window_selector_controller.cc
@@ -72,18 +72,6 @@ bool WindowSelectorController::IsSelecting() {
void WindowSelectorController::OnWindowSelected(aura::Window* window) {
window_selector_.reset();
-
- // If there is a fullscreen window on this display and it was not selected
- // it should exit fullscreen mode.
- internal::RootWindowController* controller =
- internal::GetRootWindowController(window->GetRootWindow());
- aura::Window* fullscreen_window = NULL;
- if (controller)
- fullscreen_window = controller->GetTopmostFullscreenWindow();
- if (fullscreen_window && fullscreen_window != window) {
- wm::GetWindowState(fullscreen_window)->ToggleFullscreen();
- }
-
wm::ActivateWindow(window);
last_selection_time_ = base::Time::Now();
Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false);
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index a8e8d08ecc..988a586b0f 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -11,7 +11,7 @@
#include "ash/test/launcher_test_api.h"
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/overview/window_selector.h"
#include "ash/wm/overview/window_selector_controller.h"
@@ -105,7 +105,7 @@ class WindowSelectorTest : public test::AshTestBase {
virtual void SetUp() OVERRIDE {
test::AshTestBase::SetUp();
- ASSERT_TRUE(test::TestLauncherDelegate::instance());
+ ASSERT_TRUE(test::TestShelfDelegate::instance());
shelf_view_test_.reset(new test::ShelfViewTestAPI(
test::LauncherTestAPI(Launcher::ForPrimaryDisplay()).shelf_view()));
@@ -127,7 +127,7 @@ class WindowSelectorTest : public test::AshTestBase {
aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
NULL, aura::client::WINDOW_TYPE_PANEL, 0, bounds);
- test::TestLauncherDelegate::instance()->AddLauncherItem(window);
+ test::TestShelfDelegate::instance()->AddLauncherItem(window);
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
return window;
}
@@ -284,12 +284,13 @@ TEST_F(WindowSelectorTest, FullscreenWindow) {
EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
EXPECT_FALSE(panel1->IsVisible());
- // Entering overview and selecting another window should exit fullscreen.
+ // Entering overview and selecting another window, the previous window remains
+ // fullscreen.
// TODO(flackr): Currently the panel remains hidden, but should become visible
// again.
ToggleOverview();
ClickWindow(window2.get());
- EXPECT_FALSE(wm::GetWindowState(window1.get())->IsFullscreen());
+ EXPECT_TRUE(wm::GetWindowState(window1.get())->IsFullscreen());
}
// Tests that the shelf dimming state is removed while in overview and restored
diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc
index bafe29f188..3c01e520f9 100644
--- a/ash/wm/panels/panel_frame_view.cc
+++ b/ash/wm/panels/panel_frame_view.cc
@@ -29,6 +29,7 @@ PanelFrameView::PanelFrameView(views::Widget* frame, FrameType frame_type)
title_font_(gfx::Font(views::NativeWidgetAura::GetWindowTitleFont())),
frame_border_hit_test_controller_(
new FrameBorderHitTestController(frame_)) {
+ DCHECK(!frame_->widget_delegate()->CanMaximize());
if (frame_type != FRAME_NONE)
InitHeaderPainter();
}
@@ -115,9 +116,7 @@ void PanelFrameView::OnPaint(gfx::Canvas* canvas) {
return;
bool paint_as_active = ShouldPaintAsActive();
int theme_frame_id = 0;
- if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO))
- theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL;
- else if (paint_as_active)
+ if (paint_as_active)
theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE;
else
theme_frame_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE;
diff --git a/ash/wm/panels/panel_layout_manager.cc b/ash/wm/panels/panel_layout_manager.cc
index ee6a75e2fa..ec7027b612 100644
--- a/ash/wm/panels/panel_layout_manager.cc
+++ b/ash/wm/panels/panel_layout_manager.cc
@@ -353,9 +353,10 @@ void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
// back to appropriate container and ignore it.
// TODO(varkha): Updating bounds during a drag can cause problems and a more
// general solution is needed. See http://crbug.com/251813 .
+ aura::Window* old_parent = child->parent();
aura::client::ParentWindowWithContext(
child, child, child->GetRootWindow()->GetBoundsInScreen());
- wm::ReparentTransientChildrenOfChild(child->parent(), child);
+ wm::ReparentTransientChildrenOfChild(child, old_parent, child->parent());
DCHECK(child->parent()->id() != kShellWindowId_PanelContainer);
return;
}
diff --git a/ash/wm/panels/panel_layout_manager_unittest.cc b/ash/wm/panels/panel_layout_manager_unittest.cc
index 0a71814cb6..5351afa2b1 100644
--- a/ash/wm/panels/panel_layout_manager_unittest.cc
+++ b/ash/wm/panels/panel_layout_manager_unittest.cc
@@ -21,7 +21,7 @@
#include "ash/test/launcher_test_api.h"
#include "ash/test/shelf_view_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_util.h"
#include "base/basictypes.h"
@@ -50,7 +50,7 @@ class PanelLayoutManagerTest : public test::AshTestBase {
virtual void SetUp() OVERRIDE {
test::AshTestBase::SetUp();
- ASSERT_TRUE(test::TestLauncherDelegate::instance());
+ ASSERT_TRUE(test::TestShelfDelegate::instance());
shelf_view_test_.reset(new test::ShelfViewTestAPI(
GetShelfView(Launcher::ForPrimaryDisplay())));
@@ -67,12 +67,11 @@ class PanelLayoutManagerTest : public test::AshTestBase {
aura::client::WINDOW_TYPE_PANEL,
0,
bounds);
- test::TestLauncherDelegate* launcher_delegate =
- test::TestLauncherDelegate::instance();
- launcher_delegate->AddLauncherItem(window);
- PanelLayoutManager* manager =
- static_cast<PanelLayoutManager*>(GetPanelContainer(window)->
- layout_manager());
+ test::TestShelfDelegate* shelf_delegate =
+ test::TestShelfDelegate::instance();
+ shelf_delegate->AddLauncherItem(window);
+ PanelLayoutManager* manager = static_cast<PanelLayoutManager*>(
+ GetPanelContainer(window)->layout_manager());
manager->Relayout();
shelf_view_test()->RunMessageLoopUntilAnimationsDone();
return window;
diff --git a/ash/wm/panels/panel_window_resizer.cc b/ash/wm/panels/panel_window_resizer.cc
index d7c0652373..739544f97d 100644
--- a/ash/wm/panels/panel_window_resizer.cc
+++ b/ash/wm/panels/panel_window_resizer.cc
@@ -198,9 +198,10 @@ void PanelWindowResizer::StartedDragging() {
// is reparented to a container in the root window that has that window.
aura::Window* target = GetTarget();
aura::Window* target_root = target->GetRootWindow();
+ aura::Window* old_parent = target->parent();
aura::client::ParentWindowWithContext(
target, target_root, target_root->GetBoundsInScreen());
- wm::ReparentTransientChildrenOfChild(target->parent(), target);
+ wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
}
}
@@ -215,9 +216,10 @@ void PanelWindowResizer::FinishDragging() {
// is reparented to a container in the root window that has that location.
aura::Window* target = GetTarget();
aura::Window* target_root = target->GetRootWindow();
+ aura::Window* old_parent = target->parent();
aura::client::ParentWindowWithContext(
target, target_root, gfx::Rect(last_location_, gfx::Size()));
- wm::ReparentTransientChildrenOfChild(target->parent(), GetTarget());
+ wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
}
// If we started the drag in one root window and moved into another root
diff --git a/ash/wm/panels/panel_window_resizer_unittest.cc b/ash/wm/panels/panel_window_resizer_unittest.cc
index 0e96afd257..f264b2b2b9 100644
--- a/ash/wm/panels/panel_window_resizer_unittest.cc
+++ b/ash/wm/panels/panel_window_resizer_unittest.cc
@@ -16,7 +16,7 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/cursor_manager_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/wm/drag_window_resizer.h"
#include "ash/wm/panels/panel_layout_manager.h"
#include "ash/wm/window_state.h"
@@ -41,7 +41,7 @@ class PanelWindowResizerTest : public test::AshTestBase {
UpdateDisplay("600x400");
test::ShellTestApi test_api(Shell::GetInstance());
model_ = test_api.shelf_model();
- launcher_delegate_ = test::TestLauncherDelegate::instance();
+ shelf_delegate_ = test::TestShelfDelegate::instance();
}
virtual void TearDown() OVERRIDE {
@@ -65,7 +65,7 @@ class PanelWindowResizerTest : public test::AshTestBase {
aura::client::WINDOW_TYPE_PANEL,
0,
bounds);
- launcher_delegate_->AddLauncherItem(window);
+ shelf_delegate_->AddLauncherItem(window);
PanelLayoutManager* manager =
static_cast<PanelLayoutManager*>(
Shell::GetContainer(window->GetRootWindow(),
@@ -188,7 +188,7 @@ class PanelWindowResizerTest : public test::AshTestBase {
scoped_ptr<WindowResizer> resizer_;
internal::PanelLayoutManager* panel_layout_manager_;
ShelfModel* model_;
- test::TestLauncherDelegate* launcher_delegate_;
+ test::TestShelfDelegate* shelf_delegate_;
DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest);
};
diff --git a/ash/wm/session_state_animator.cc b/ash/wm/session_state_animator.cc
index 38091bf114..de8cd42d4d 100644
--- a/ash/wm/session_state_animator.cc
+++ b/ash/wm/session_state_animator.cc
@@ -613,22 +613,5 @@ void SessionStateAnimator::RunAnimationForWindow(
}
}
-void SessionStateAnimator::CreateForeground() {
- if (foreground_)
- return;
- aura::Window* window = Shell::GetContainer(
- Shell::GetPrimaryRootWindow(),
- internal::kShellWindowId_PowerButtonAnimationContainer);
- HideWindowImmediately(window, NULL);
- foreground_.reset(
- new ColoredWindowController(window, "SessionStateAnimatorForeground"));
- foreground_->SetColor(SK_ColorWHITE);
- foreground_->GetWidget()->Show();
-}
-
-void SessionStateAnimator::DropForeground() {
- foreground_.reset();
-}
-
} // namespace internal
} // namespace ash
diff --git a/ash/wm/session_state_animator.h b/ash/wm/session_state_animator.h
index 19ead2f26c..4cb183e3ce 100644
--- a/ash/wm/session_state_animator.h
+++ b/ash/wm/session_state_animator.h
@@ -6,7 +6,6 @@
#define ASH_WM_SESSION_STATE_ANIMATOR_H_
#include "ash/ash_export.h"
-#include "ash/wm/workspace/colored_window_controller.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/timer/timer.h"
@@ -140,12 +139,6 @@ class ASH_EXPORT SessionStateAnimator {
static void GetContainers(int container_mask,
aura::Window::Windows* containers);
- // Create |foreground_| layer if it doesn't already exist, but makes it
- // completely transparent.
- void CreateForeground();
- // Destroy |foreground_| when it is not needed anymore.
- void DropForeground();
-
// Apply animation |type| to all containers included in |container_mask| with
// specified |speed|.
void StartAnimation(int container_mask,
@@ -178,10 +171,6 @@ class ASH_EXPORT SessionStateAnimator {
AnimationSpeed speed,
ui::LayerAnimationObserver* observer);
- // White foreground that is used during shutdown animation to "fade
- // everything into white".
- scoped_ptr<ColoredWindowController> foreground_;
-
DISALLOW_COPY_AND_ASSIGN(SessionStateAnimator);
};
diff --git a/ash/wm/solo_window_tracker.cc b/ash/wm/solo_window_tracker.cc
index 71299d2cc5..633d3bd450 100644
--- a/ash/wm/solo_window_tracker.cc
+++ b/ash/wm/solo_window_tracker.cc
@@ -60,7 +60,7 @@ bool IsValidCandidate(aura::Window* window) {
window->layer() &&
window->layer()->type() != ui::LAYER_NOT_DRAWN &&
window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE &&
- !window->GetProperty(ash::kConstrainedWindowKey);
+ !window->GetProperty(aura::client::kConstrainedWindowKey);
}
// Schedule's a paint of the window's entire bounds.
diff --git a/ash/wm/solo_window_tracker_unittest.cc b/ash/wm/solo_window_tracker_unittest.cc
index 5792008d92..431ae00955 100644
--- a/ash/wm/solo_window_tracker_unittest.cc
+++ b/ash/wm/solo_window_tracker_unittest.cc
@@ -29,10 +29,14 @@ namespace {
class WindowRepaintChecker : public aura::WindowObserver {
public:
explicit WindowRepaintChecker(aura::Window* window)
- : is_paint_scheduled_(false) {
- window->AddObserver(this);
+ : window_(window),
+ is_paint_scheduled_(false) {
+ window_->AddObserver(this);
}
+
virtual ~WindowRepaintChecker() {
+ if (window_)
+ window_->RemoveObserver(this);
}
bool IsPaintScheduledAndReset() {
@@ -47,10 +51,12 @@ class WindowRepaintChecker : public aura::WindowObserver {
const gfx::Rect& region) OVERRIDE {
is_paint_scheduled_ = true;
}
- virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
- window->RemoveObserver(this);
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
+ DCHECK_EQ(window_, window);
+ window_ = NULL;
}
+ aura::Window* window_;
bool is_paint_scheduled_;
DISALLOW_COPY_AND_ASSIGN(WindowRepaintChecker);
@@ -272,7 +278,7 @@ TEST_F(SoloWindowTrackerTest, Constrained) {
// Create a fake constrained window.
scoped_ptr<aura::Window> w2(CreateWindowInPrimary());
- w2->SetProperty(ash::kConstrainedWindowKey, true);
+ w2->SetProperty(aura::client::kConstrainedWindowKey, true);
w2->Show();
// Despite two windows, the first window should still be considered "solo"
diff --git a/ash/wm/sticky_keys.cc b/ash/wm/sticky_keys.cc
index efdfd16017..7910a072ad 100644
--- a/ash/wm/sticky_keys.cc
+++ b/ash/wm/sticky_keys.cc
@@ -172,6 +172,7 @@ StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag,
current_state_(DISABLED),
event_from_myself_(false),
preparing_to_enable_(false),
+ scroll_delta_(0),
delegate_(delegate) {
}
@@ -223,11 +224,28 @@ bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
if (event_from_myself_ || current_state_ == DISABLED)
return false;
DCHECK(current_state_ == ENABLED || current_state_ == LOCKED);
-
preparing_to_enable_ = false;
- AppendModifier(event);
- if (current_state_ == ENABLED) {
+
+ // We detect a direction change if the current |scroll_delta_| is assigned
+ // and the offset of the current scroll event has the opposing sign.
+ bool direction_changed = false;
+ if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) {
+ int offset = event->y_offset();
+ if (scroll_delta_)
+ direction_changed = offset * scroll_delta_ <= 0;
+ scroll_delta_ = offset;
+ }
+
+ if (!direction_changed)
+ AppendModifier(event);
+
+ // We want to modify all the scroll events in the scroll sequence, which ends
+ // with a fling start event. We also stop when the scroll sequence changes
+ // direction.
+ if (current_state_ == ENABLED &&
+ (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
current_state_ = DISABLED;
+ scroll_delta_ = 0;
DispatchEventAndReleaseModifier(event);
return true;
}
@@ -268,8 +286,9 @@ bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
case TARGET_MODIFIER_UP:
if (preparing_to_enable_) {
preparing_to_enable_ = false;
+ scroll_delta_ = 0;
current_state_ = ENABLED;
- modifier_up_event_.reset(event->Copy());
+ modifier_up_event_.reset(new ui::KeyEvent(*event));
return true;
}
return false;
diff --git a/ash/wm/sticky_keys.h b/ash/wm/sticky_keys.h
index 7b8f6f1dee..1a2789214b 100644
--- a/ash/wm/sticky_keys.h
+++ b/ash/wm/sticky_keys.h
@@ -222,6 +222,11 @@ class ASH_EXPORT StickyKeysHandler {
// the ENABLED state.
bool preparing_to_enable_;
+ // Tracks the scroll direction of the current scroll sequence. Sticky keys
+ // stops modifying the scroll events of the sequence when the direction
+ // changes. If no sequence is tracked, the value is 0.
+ int scroll_delta_;
+
// The modifier up key event to be sent on non modifier key on ENABLED state.
scoped_ptr<ui::KeyEvent> modifier_up_event_;
diff --git a/ash/wm/sticky_keys_unittest.cc b/ash/wm/sticky_keys_unittest.cc
index e8e8975c19..6f11347ecb 100644
--- a/ash/wm/sticky_keys_unittest.cc
+++ b/ash/wm/sticky_keys_unittest.cc
@@ -44,7 +44,7 @@ class EventBuffer : public ui::EventHandler {
private:
// ui::EventHandler overrides:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE {
- events_.push_back(event->Copy());
+ events_.push_back(new ui::KeyEvent(*event));
}
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
@@ -92,7 +92,7 @@ class MockStickyKeysHandlerDelegate :
delegate_->OnShortcutPressed();
}
- events_.push_back(event->Copy());
+ events_.push_back(new ui::KeyEvent(*event));
}
virtual void DispatchMouseEvent(ui::MouseEvent* event,
@@ -203,6 +203,21 @@ class StickyKeysTest : public test::AshTestBase,
return event;
}
+ ui::ScrollEvent* GenerateFlingScrollEvent(int fling_delta,
+ bool is_cancel) {
+ scoped_xevent_.InitFlingScrollEvent(
+ kScrollDeviceId, // deviceid
+ 0, // x_velocity
+ fling_delta, // y_velocity
+ 0, // x_velocity_ordinal
+ fling_delta, // y_velocity_ordinal
+ is_cancel); // is_cancel
+ ui::ScrollEvent* event = new ui::ScrollEvent(scoped_xevent_);
+ ui::Event::DispatcherApi dispatcher(event);
+ dispatcher.set_target(target_);
+ return event;
+ }
+
// Creates a synthesized KeyEvent that is not backed by a native event.
ui::KeyEvent* GenerateSynthesizedKeyEvent(
bool is_key_press, ui::KeyboardCode code) {
@@ -501,9 +516,9 @@ TEST_F(StickyKeysTest, MouseEventLocked) {
EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
}
-TEST_F(StickyKeysTest, ScrollEvents) {
+TEST_F(StickyKeysTest, ScrollEventOneshot) {
ui::SetUpScrollDeviceForTest(kScrollDeviceId);
- // Australlian scrolling is enabled by default for some reason.
+ // Disable Australlian scrolling.
ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
scoped_ptr<ui::ScrollEvent> ev;
@@ -518,20 +533,32 @@ TEST_F(StickyKeysTest, ScrollEvents) {
// Enable sticky keys.
EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
- kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
- sticky_key.HandleKeyEvent(kev.get());
- kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
- sticky_key.HandleKeyEvent(kev.get());
+ SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
- // Test scroll event is correctly modified.
- ev.reset(GenerateScrollEvent(scroll_deltas[i]));
+ // Test a scroll sequence. Sticky keys should only be disabled at the end
+ // of the scroll sequence. Fling cancel event starts the scroll sequence.
+ ev.reset(GenerateFlingScrollEvent(0, true));
+ sticky_key.HandleScrollEvent(ev.get());
+ EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+ EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+ // Scrolls should all be modified but not disable sticky keys.
+ for (int j = 0; j < 3; ++j) {
+ ev.reset(GenerateScrollEvent(scroll_deltas[i]));
+ sticky_key.HandleScrollEvent(ev.get());
+ EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+ EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+ }
+
+ // Fling start event ends scroll sequence.
+ ev.reset(GenerateFlingScrollEvent(scroll_deltas[i], false));
sticky_key.HandleScrollEvent(ev.get());
EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
ASSERT_EQ(2U, mock_delegate->GetEventCount());
- EXPECT_EQ(ui::ET_SCROLL, mock_delegate->GetEvent(0)->type());
+ EXPECT_EQ(ui::ET_SCROLL_FLING_START, mock_delegate->GetEvent(0)->type());
EXPECT_FLOAT_EQ(scroll_deltas[i],
static_cast<const ui::ScrollEvent*>(
mock_delegate->GetEvent(0))->y_offset());
@@ -540,23 +567,78 @@ TEST_F(StickyKeysTest, ScrollEvents) {
static_cast<const ui::KeyEvent*>(mock_delegate->GetEvent(1))
->key_code());
}
+}
- // Lock sticky keys.
+TEST_F(StickyKeysTest, ScrollDirectionChanged) {
+ ui::SetUpScrollDeviceForTest(kScrollDeviceId);
+ // Disable Australlian scrolling.
+ ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
+
+ scoped_ptr<ui::ScrollEvent> ev;
+ scoped_ptr<ui::KeyEvent> kev;
+ MockStickyKeysHandlerDelegate* mock_delegate =
+ new MockStickyKeysHandlerDelegate(this);
+ StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
+
+ // Test direction change with both boundary value and negative value.
+ const int direction_change_values[2] = {0, -10};
for (int i = 0; i < 2; ++i) {
- kev.reset(GenerateKey(true, ui::VKEY_CONTROL));
- sticky_key.HandleKeyEvent(kev.get());
- kev.reset(GenerateKey(false, ui::VKEY_CONTROL));
- sticky_key.HandleKeyEvent(kev.get());
+ SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+ EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+ // Fling cancel starts scroll sequence.
+ ev.reset(GenerateFlingScrollEvent(0, true));
+ sticky_key.HandleScrollEvent(ev.get());
+ EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+
+ // Test that changing directions in a scroll sequence will
+ // return sticky keys to DISABLED state.
+ for (int j = 0; j < 3; ++j) {
+ ev.reset(GenerateScrollEvent(10));
+ sticky_key.HandleScrollEvent(ev.get());
+ EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+ EXPECT_EQ(StickyKeysHandler::ENABLED, sticky_key.current_state());
+ }
+
+ ev.reset(GenerateScrollEvent(direction_change_values[i]));
+ sticky_key.HandleScrollEvent(ev.get());
+ EXPECT_FALSE(ev->flags() & ui::EF_CONTROL_DOWN);
+ EXPECT_EQ(StickyKeysHandler::DISABLED, sticky_key.current_state());
}
+}
+
+TEST_F(StickyKeysTest, ScrollEventLocked) {
+ ui::SetUpScrollDeviceForTest(kScrollDeviceId);
+ // Disable Australlian scrolling.
+ ui::DeviceDataManager::GetInstance()->set_natural_scroll_enabled(true);
+
+ scoped_ptr<ui::ScrollEvent> ev;
+ scoped_ptr<ui::KeyEvent> kev;
+ MockStickyKeysHandlerDelegate* mock_delegate =
+ new MockStickyKeysHandlerDelegate(this);
+ StickyKeysHandler sticky_key(ui::EF_CONTROL_DOWN, mock_delegate);
+
+ // Lock sticky keys.
+ SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+ SendActivateStickyKeyPattern(&sticky_key, ui::VKEY_CONTROL);
+ EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
// Test scroll events are correctly modified in locked state.
for (int i = 0; i < 5; ++i) {
+ // Fling cancel starts scroll sequence.
+ ev.reset(GenerateFlingScrollEvent(0, true));
+ sticky_key.HandleScrollEvent(ev.get());
+
ev.reset(GenerateScrollEvent(10));
sticky_key.HandleScrollEvent(ev.get());
EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
ev.reset(GenerateScrollEvent(-10));
sticky_key.HandleScrollEvent(ev.get());
EXPECT_TRUE(ev->flags() & ui::EF_CONTROL_DOWN);
+
+ // Fling start ends scroll sequence.
+ ev.reset(GenerateFlingScrollEvent(-10, false));
+ sticky_key.HandleScrollEvent(ev.get());
}
EXPECT_EQ(StickyKeysHandler::LOCKED, sticky_key.current_state());
diff --git a/ash/wm/system_gesture_event_filter_unittest.cc b/ash/wm/system_gesture_event_filter_unittest.cc
index 328c9ca60b..aaf6634cd5 100644
--- a/ash/wm/system_gesture_event_filter_unittest.cc
+++ b/ash/wm/system_gesture_event_filter_unittest.cc
@@ -15,7 +15,7 @@
#include "ash/test/ash_test_base.h"
#include "ash/test/display_manager_test_api.h"
#include "ash/test/shell_test_api.h"
-#include "ash/test/test_launcher_delegate.h"
+#include "ash/test/test_shelf_delegate.h"
#include "ash/volume_control_delegate.h"
#include "ash/wm/gestures/long_press_affordance_handler.h"
#include "ash/wm/window_state.h"
diff --git a/ash/wm/system_modal_container_layout_manager.cc b/ash/wm/system_modal_container_layout_manager.cc
index 99889d2f59..2c2cdebb57 100644
--- a/ash/wm/system_modal_container_layout_manager.cc
+++ b/ash/wm/system_modal_container_layout_manager.cc
@@ -21,6 +21,7 @@
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/gfx/screen.h"
+#include "ui/views/background.h"
#include "ui/views/corewm/compound_event_filter.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
diff --git a/ash/wm/window_animations.cc b/ash/wm/window_animations.cc
index 13abd4a8c2..894e11b018 100644
--- a/ash/wm/window_animations.cc
+++ b/ash/wm/window_animations.cc
@@ -59,9 +59,6 @@ const float kWindowAnimation_ShowOpacity = 1.f;
// TODO(sky): if we end up sticking with 0, nuke the code doing the rotation.
const float kWindowAnimation_MinimizeRotate = 0.f;
-// Tween type when cross fading a workspace window.
-const gfx::Tween::Type kCrossFadeTweenType = gfx::Tween::EASE_IN_OUT;
-
// Scales for AshWindow above/below current workspace.
const float kLayerScaleAboveSize = 1.1f;
const float kLayerScaleBelowSize = .9f;
@@ -108,8 +105,8 @@ void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
rotation_about_pivot->SetReversed(show);
- base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
- kLayerAnimationsForMinimizeDurationMS);
+ base::TimeDelta duration = window->layer()->GetAnimator()->
+ GetTransitionDuration();
scoped_ptr<ui::LayerAnimationElement> transition(
ui::LayerAnimationElement::CreateInterpolatedTransformElement(
@@ -139,6 +136,10 @@ void AddLayerAnimationsForMinimize(aura::Window* window, bool show) {
void AnimateShowWindow_Minimize(aura::Window* window) {
window->layer()->set_delegate(window);
window->layer()->SetOpacity(kWindowAnimation_HideOpacity);
+ ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(
+ kLayerAnimationsForMinimizeDurationMS);
+ settings.SetTransitionDuration(duration);
AddLayerAnimationsForMinimize(window, true);
// Now that the window has been restored, we need to clear its animation style
@@ -324,7 +325,8 @@ base::TimeDelta CrossFadeImpl(aura::Window* window,
const bool old_on_top = (old_bounds.width() > new_bounds.width());
// Shorten the animation if there's not much visual movement.
- const base::TimeDelta duration = GetCrossFadeDuration(old_bounds, new_bounds);
+ const base::TimeDelta duration = GetCrossFadeDuration(window,
+ old_bounds, new_bounds);
// Scale up the old layer while translating to new position.
{
@@ -416,23 +418,10 @@ void CrossFadeToBounds(aura::Window* window, const gfx::Rect& new_bounds) {
CrossFadeImpl(window, old_layer, gfx::Tween::EASE_OUT);
}
-void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace,
- aura::Window* window,
- ui::Layer* old_layer) {
- ui::Layer* layer_parent = new_workspace->layer()->parent();
- layer_parent->Add(old_layer);
- const bool restoring = old_layer->bounds().width() > window->bounds().width();
- if (restoring)
- layer_parent->StackAbove(old_layer, new_workspace->layer());
- else
- layer_parent->StackBelow(old_layer, new_workspace->layer());
-
- CrossFadeImpl(window, old_layer, kCrossFadeTweenType);
-}
-
-base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds,
+base::TimeDelta GetCrossFadeDuration(aura::Window* window,
+ const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) {
- if (views::corewm::WindowAnimationsDisabled(NULL))
+ if (views::corewm::WindowAnimationsDisabled(window))
return base::TimeDelta();
int old_area = old_bounds.width() * old_bounds.height();
diff --git a/ash/wm/window_animations.h b/ash/wm/window_animations.h
index b0e4a2efec..f084bcc838 100644
--- a/ash/wm/window_animations.h
+++ b/ash/wm/window_animations.h
@@ -44,17 +44,10 @@ extern const int kCrossFadeDurationMS;
ASH_EXPORT void CrossFadeToBounds(aura::Window* window,
const gfx::Rect& new_bounds);
-// Cross fades |layer| (which is a clone of |window|s layer before it was
-// resized) to |window|s current bounds. |new_workspace| is the Window of the
-// workspace |window| was added to.
-// This takes ownership of |layer|.
-ASH_EXPORT void CrossFadeWindowBetweenWorkspaces(aura::Window* new_workspace,
- aura::Window* window,
- ui::Layer* layer);
-
// Returns the duration of the cross-fade animation based on the |old_bounds|
-// and |new_bounds| of the window.
-ASH_EXPORT base::TimeDelta GetCrossFadeDuration(const gfx::Rect& old_bounds,
+// and |new_bounds| of the |window|.
+ASH_EXPORT base::TimeDelta GetCrossFadeDuration(aura::Window* window,
+ const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds);
ASH_EXPORT bool AnimateOnChildWindowVisibilityChanged(aura::Window* window,
diff --git a/ash/wm/window_animations_unittest.cc b/ash/wm/window_animations_unittest.cc
index c2ea1f6365..eeeb120b09 100644
--- a/ash/wm/window_animations_unittest.cc
+++ b/ash/wm/window_animations_unittest.cc
@@ -6,13 +6,16 @@
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
+#include "ash/wm/window_state.h"
#include "ash/wm/workspace_controller.h"
#include "base/time/time.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/animation/animation_container_element.h"
using aura::Window;
@@ -33,6 +36,37 @@ class WindowAnimationsTest : public ash::test::AshTestBase {
DISALLOW_COPY_AND_ASSIGN(WindowAnimationsTest);
};
+// Listens to animation scheduled notifications. Remembers the transition
+// duration of the first sequence.
+class MinimizeAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+ explicit MinimizeAnimationObserver(ui::LayerAnimator* animator)
+ : animator_(animator) {
+ animator_->AddObserver(this);
+ // RemoveObserver is called when the first animation is scheduled and so
+ // there should be no need for now to remove it in destructor.
+ };
+ base::TimeDelta duration() { return duration_; }
+
+ protected:
+ // ui::LayerAnimationObserver:
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ duration_ = animator_->GetTransitionDuration();
+ animator_->RemoveObserver(this);
+ }
+ virtual void OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {}
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {}
+
+ private:
+ ui::LayerAnimator* animator_;
+ base::TimeDelta duration_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinimizeAnimationObserver);
+};
+
TEST_F(WindowAnimationsTest, HideShowBrightnessGrayscaleAnimation) {
scoped_ptr<aura::Window> window(CreateTestWindowInShellWithId(0));
window->Show();
@@ -133,5 +167,77 @@ TEST_F(WindowAnimationsTest, CrossFadeToBounds) {
Step(base::TimeTicks::Now() + base::TimeDelta::FromSeconds(1));
}
+TEST_F(WindowAnimationsTest, LockAnimationDuration) {
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ scoped_ptr<Window> window(CreateTestWindowInShellWithId(0));
+ Layer* layer = window->layer();
+ window->SetBounds(gfx::Rect(5, 10, 320, 240));
+ window->Show();
+
+ // Test that it is possible to override transition duration when it is not
+ // locked.
+ {
+ ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
+ settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+ {
+ ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
+ // Duration is not locked so it gets overridden.
+ settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+ wm::GetWindowState(window.get())->Minimize();
+ EXPECT_TRUE(layer->GetAnimator()->is_animating());
+ // Expect duration from the inner scope
+ EXPECT_EQ(50,
+ layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+ }
+ window->Show();
+ layer->GetAnimator()->StopAnimating();
+ }
+
+ // Test that it is possible to lock transition duration
+ {
+ ui::ScopedLayerAnimationSettings settings1(layer->GetAnimator());
+ settings1.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+ // Duration is locked in outer scope.
+ settings1.LockTransitionDuration();
+ {
+ ui::ScopedLayerAnimationSettings settings2(layer->GetAnimator());
+ // Transition duration setting is ignored.
+ settings2.SetTransitionDuration(base::TimeDelta::FromMilliseconds(50));
+ wm::GetWindowState(window.get())->Minimize();
+ EXPECT_TRUE(layer->GetAnimator()->is_animating());
+ // Expect duration from the outer scope
+ EXPECT_EQ(1000,
+ layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+ }
+ window->Show();
+ layer->GetAnimator()->StopAnimating();
+ }
+
+ // Test that duration respects default.
+ {
+ // Query default duration.
+ MinimizeAnimationObserver observer(layer->GetAnimator());
+ wm::GetWindowState(window.get())->Minimize();
+ EXPECT_TRUE(layer->GetAnimator()->is_animating());
+ base::TimeDelta default_duration(observer.duration());
+ window->Show();
+ layer->GetAnimator()->StopAnimating();
+
+ ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
+ settings.LockTransitionDuration();
+ // Setting transition duration is ignored since duration is locked
+ settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(1000));
+ wm::GetWindowState(window.get())->Minimize();
+ EXPECT_TRUE(layer->GetAnimator()->is_animating());
+ // Expect default duration (200ms for stock ash minimizing animation).
+ EXPECT_EQ(default_duration.InMilliseconds(),
+ layer->GetAnimator()->GetTransitionDuration().InMilliseconds());
+ window->Show();
+ layer->GetAnimator()->StopAnimating();
+ }
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc
index 428804f66f..d6bf24a59c 100644
--- a/ash/wm/window_cycle_list.cc
+++ b/ash/wm/window_cycle_list.cc
@@ -72,8 +72,6 @@ int WindowCycleList::GetWindowIndex(aura::Window* window) {
}
void WindowCycleList::OnWindowDestroyed(aura::Window* window) {
- window->RemoveObserver(this);
-
WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
DCHECK(i != windows_.end());
int removed_index = static_cast<int>(i - windows_.begin());
diff --git a/ash/wm/window_positioner.cc b/ash/wm/window_positioner.cc
index 48e70a2a3d..9b31113f8c 100644
--- a/ash/wm/window_positioner.cc
+++ b/ash/wm/window_positioner.cc
@@ -58,8 +58,7 @@ bool UseAutoWindowManager(const aura::Window* window) {
if (disable_auto_positioning)
return false;
const wm::WindowState* window_state = wm::GetWindowState(window);
- return window_state->tracked_by_workspace() &&
- window_state->window_position_managed();
+ return !window_state->is_dragged() && window_state->window_position_managed();
}
// Check if a given |window| can be managed. This includes that it's state is
diff --git a/ash/wm/window_state.cc b/ash/wm/window_state.cc
index 1a06a58da1..4bc1484f9b 100644
--- a/ash/wm/window_state.cc
+++ b/ash/wm/window_state.cc
@@ -4,6 +4,7 @@
#include "ash/wm/window_state.h"
+#include "ash/ash_switches.h"
#include "ash/root_window_controller.h"
#include "ash/screen_ash.h"
#include "ash/shell_window_ids.h"
@@ -12,6 +13,8 @@
#include "ash/wm/window_state_observer.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_types.h"
+#include "base/auto_reset.h"
+#include "base/command_line.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
@@ -29,7 +32,6 @@ bool WindowState::IsMaximizedOrFullscreenState(ui::WindowShowState show_state) {
WindowState::WindowState(aura::Window* window)
: window_(window),
- tracked_by_workspace_(true),
window_position_managed_(false),
bounds_changed_by_user_(false),
panel_attached_(true),
@@ -42,13 +44,28 @@ WindowState::WindowState(aura::Window* window)
hide_shelf_when_fullscreen_(true),
animate_to_fullscreen_(true),
minimum_visibility_(false),
+ in_set_window_show_type_(false),
window_show_type_(ToWindowShowType(GetShowState())) {
window_->AddObserver(this);
+
+#if defined(OS_CHROMEOS)
+ // NOTE(pkotwicz): Animating to immersive fullscreen does not look good. When
+ // the kAshEnableImmersiveFullscreenForAllWindows flag is set most windows
+ // can be put into immersive fullscreen. It is not worth the added complexity
+ // to only animate to fullscreen if the window is put into immersive
+ // fullscreen.
+ animate_to_fullscreen_ = !CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableImmersiveFullscreenForAllWindows);
+#endif
}
WindowState::~WindowState() {
}
+bool WindowState::HasDelegate() const {
+ return delegate_;
+}
+
void WindowState::SetDelegate(scoped_ptr<WindowStateDelegate> delegate) {
DCHECK(!delegate_.get());
delegate_ = delegate.Pass();
@@ -88,6 +105,11 @@ bool WindowState::IsDocked() const {
window_->parent()->id() == internal::kShellWindowId_DockedContainer;
}
+bool WindowState::IsSnapped() const {
+ return window_show_type_ == SHOW_TYPE_LEFT_SNAPPED ||
+ window_show_type_ == SHOW_TYPE_RIGHT_SNAPPED;
+}
+
bool WindowState::CanMaximize() const {
return window_->GetProperty(aura::client::kCanMaximizeKey);
}
@@ -136,7 +158,7 @@ void WindowState::SnapLeft(const gfx::Rect& bounds) {
}
void WindowState::SnapRight(const gfx::Rect& bounds) {
- SnapWindow(SHOW_TYPE_LEFT_SNAPPED, bounds);
+ SnapWindow(SHOW_TYPE_RIGHT_SNAPPED, bounds);
}
void WindowState::Minimize() {
@@ -235,54 +257,67 @@ void WindowState::RemoveObserver(WindowStateObserver* observer) {
observer_list_.RemoveObserver(observer);
}
-void WindowState::SetTrackedByWorkspace(bool tracked_by_workspace) {
- if (tracked_by_workspace_ == tracked_by_workspace)
- return;
- bool old = tracked_by_workspace_;
- tracked_by_workspace_ = tracked_by_workspace;
- FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
- OnTrackedByWorkspaceChanged(this, old));
-}
-
void WindowState::OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) {
DCHECK_EQ(window, window_);
- if (key == aura::client::kShowStateKey) {
- window_show_type_ = ToWindowShowType(GetShowState());
- ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old);
- // TODO(oshima): Notify only when the state has changed.
- // Doing so break a few tests now.
- FOR_EACH_OBSERVER(
- WindowStateObserver, observer_list_,
- OnWindowShowTypeChanged(this, ToWindowShowType(old_state)));
- }
-}
-
-void WindowState::OnWindowDestroying(aura::Window* window) {
- window_->RemoveObserver(this);
+ if (key == aura::client::kShowStateKey)
+ SetWindowShowType(ToWindowShowType(GetShowState()));
}
void WindowState::SnapWindow(WindowShowType left_or_right,
const gfx::Rect& bounds) {
- if (IsMaximizedOrFullscreen()) {
- // Before we can set the bounds we need to restore the window.
- // Restoring the window will set the window to its restored bounds.
- // To avoid an unnecessary bounds changes (which may have side effects)
- // we set the restore bounds to the bounds we want, restore the window,
- // then reset the restore bounds. This way no unnecessary bounds
- // changes occurs and the original restore bounds is remembered.
- gfx::Rect restore_bounds_in_screen =
- GetRestoreBoundsInScreen();
- SetRestoreBoundsInParent(bounds);
- Restore();
- SetRestoreBoundsInScreen(restore_bounds_in_screen);
- } else {
+ if (window_show_type_ == left_or_right) {
window_->SetBounds(bounds);
+ return;
}
+
+ // Compute the bounds that the window will restore to. If the window does not
+ // already have restore bounds, it will be restored (when un-snapped) to the
+ // last bounds that it had before getting snapped.
+ gfx::Rect restore_bounds_in_screen(HasRestoreBounds() ?
+ GetRestoreBoundsInScreen() : window_->GetBoundsInScreen());
+ // Set the window's restore bounds so that WorkspaceLayoutManager knows
+ // which width to use when the snapped window is moved to the edge.
+ SetRestoreBoundsInParent(bounds);
+
+ bool was_maximized = IsMaximizedOrFullscreen();
+ // Before we can set the bounds we need to restore the window.
+ // Restoring the window will set the window to its restored bounds set above.
+ // Restore will cause OnWindowPropertyChanged() so it needs to be done
+ // before notifying that the WindowShowType has changed to |left_or_right|.
+ if (was_maximized)
+ Restore();
DCHECK(left_or_right == SHOW_TYPE_LEFT_SNAPPED ||
left_or_right == SHOW_TYPE_RIGHT_SNAPPED);
- window_show_type_ = left_or_right;
+ SetWindowShowType(left_or_right);
+ // TODO(varkha): Ideally the bounds should be changed in a LayoutManager upon
+ // observing the WindowShowType change.
+ // If the window is a child of kShellWindowId_DockedContainer such as during
+ // a drag, the window's bounds are not set in
+ // WorkspaceLayoutManager::OnWindowShowTypeChanged(). Set them here. Skip
+ // setting the bounds otherwise to avoid stopping the slide animation which
+ // was started as a result of OnWindowShowTypeChanged().
+ if (IsDocked())
+ window_->SetBounds(bounds);
+ SetRestoreBoundsInScreen(restore_bounds_in_screen);
+}
+
+void WindowState::SetWindowShowType(WindowShowType new_window_show_type) {
+ if (in_set_window_show_type_)
+ return;
+ base::AutoReset<bool> resetter(&in_set_window_show_type_, true);
+
+ ui::WindowShowState new_window_state =
+ ToWindowShowState(new_window_show_type);
+ if (new_window_state != GetShowState())
+ window_->SetProperty(aura::client::kShowStateKey, new_window_state);
+ WindowShowType old_window_show_type = window_show_type_;
+ window_show_type_ = new_window_show_type;
+ if (old_window_show_type != window_show_type_) {
+ FOR_EACH_OBSERVER(WindowStateObserver, observer_list_,
+ OnWindowShowTypeChanged(this, old_window_show_type));
+ }
}
WindowState* GetActiveWindowState() {
diff --git a/ash/wm/window_state.h b/ash/wm/window_state.h
index 2bbdd035b0..618349c86d 100644
--- a/ash/wm/window_state.h
+++ b/ash/wm/window_state.h
@@ -49,6 +49,7 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
aura::Window* window() { return window_; }
const aura::Window* window() const { return window_; }
+ bool HasDelegate() const;
void SetDelegate(scoped_ptr<WindowStateDelegate> delegate);
// Returns the window's current show state.
@@ -69,6 +70,7 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
bool IsNormalShowState() const;
bool IsActive() const;
bool IsDocked() const;
+ bool IsSnapped() const;
// Checks if the window can change its state accordingly.
bool CanMaximize() const;
@@ -172,12 +174,8 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
void AddObserver(WindowStateObserver* observer);
void RemoveObserver(WindowStateObserver* observer);
- // Whether the window is tracked by workspace code. Default is
- // true. If set to false the workspace does not switch the current
- // workspace, nor does it attempt to impose constraints on the
- // bounds of the window. This is intended for tab dragging.
- bool tracked_by_workspace() const { return tracked_by_workspace_; }
- void SetTrackedByWorkspace(bool tracked_by_workspace);
+ // Whether the window is being dragged.
+ bool is_dragged() const { return !!window_resizer_; }
// Whether or not the window's position can be managed by the
// auto management logic.
@@ -250,18 +248,19 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
virtual void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) OVERRIDE;
- virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
private:
// Snaps the window to left or right of the desktop with given bounds.
void SnapWindow(WindowShowType left_or_right,
const gfx::Rect& bounds);
+ // Sets the window show type and updates the show state if necessary.
+ void SetWindowShowType(WindowShowType new_window_show_type);
+
// The owner of this window settings.
aura::Window* window_;
scoped_ptr<WindowStateDelegate> delegate_;
- bool tracked_by_workspace_;
bool window_position_managed_;
bool bounds_changed_by_user_;
bool panel_attached_;
@@ -283,6 +282,9 @@ class ASH_EXPORT WindowState : public aura::WindowObserver {
ObserverList<WindowStateObserver> observer_list_;
+ // True when in SetWindowShowType(). This is used to avoid reentrance.
+ bool in_set_window_show_type_;
+
WindowShowType window_show_type_;
DISALLOW_COPY_AND_ASSIGN(WindowState);
diff --git a/ash/wm/window_state_observer.h b/ash/wm/window_state_observer.h
index 75008a8c1d..f177d5fbeb 100644
--- a/ash/wm/window_state_observer.h
+++ b/ash/wm/window_state_observer.h
@@ -14,11 +14,6 @@ class WindowState;
class ASH_EXPORT WindowStateObserver {
public:
- // Called when the tracked_by_workspace has changed.
- virtual void OnTrackedByWorkspaceChanged(
- WindowState* window,
- bool old_value) {}
-
// Called when the window's show type has changed. This is different from
// kWindowShowStatekey property change as this will be invoked when the window
// gets left/right maximized, and auto positioned. |old_type| is the value
diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc
index 8d320a9c2e..def4b6d5ba 100644
--- a/ash/wm/window_util.cc
+++ b/ash/wm/window_util.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "ash/ash_constants.h"
+#include "ash/screen_ash.h"
#include "ash/shell.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_state.h"
@@ -62,12 +63,19 @@ void CenterWindow(aura::Window* window) {
const gfx::Display display =
Shell::GetScreen()->GetDisplayNearestWindow(window);
gfx::Rect center = display.work_area();
- gfx::Size size = window_state->HasRestoreBounds() ?
- window_state->GetRestoreBoundsInScreen().size() :
- window->bounds().size();
- center.ClampToCenteredSize(size);
- window_state->SetRestoreBoundsInScreen(center);
- window_state->Restore();
+ gfx::Size size = window->bounds().size();
+ if (window_state->IsSnapped()) {
+ if (window_state->HasRestoreBounds())
+ size = window_state->GetRestoreBoundsInScreen().size();
+ center.ClampToCenteredSize(size);
+ window_state->SetRestoreBoundsInScreen(center);
+ window_state->Restore();
+ } else {
+ center = ScreenAsh::ConvertRectFromScreen(window->parent(),
+ center);
+ center.ClampToCenteredSize(size);
+ window->SetBounds(center);
+ }
}
void AdjustBoundsToEnsureMinimumWindowVisibility(const gfx::Rect& visible_area,
@@ -115,16 +123,22 @@ bool MoveWindowToEventRoot(aura::Window* window, const ui::Event& event) {
return true;
}
-void ReparentChildWithTransientChildren(aura::Window* window,
- aura::Window* child) {
- window->AddChild(child);
- ReparentTransientChildrenOfChild(window, child);
+void ReparentChildWithTransientChildren(aura::Window* child,
+ aura::Window* old_parent,
+ aura::Window* new_parent) {
+ if (child->parent() == old_parent)
+ new_parent->AddChild(child);
+ ReparentTransientChildrenOfChild(child, old_parent, new_parent);
}
-void ReparentTransientChildrenOfChild(aura::Window* window,
- aura::Window* child) {
- for (size_t i = 0; i < child->transient_children().size(); ++i)
- ReparentChildWithTransientChildren(window, child->transient_children()[i]);
+void ReparentTransientChildrenOfChild(aura::Window* child,
+ aura::Window* old_parent,
+ aura::Window* new_parent) {
+ for (size_t i = 0; i < child->transient_children().size(); ++i) {
+ ReparentChildWithTransientChildren(child->transient_children()[i],
+ old_parent,
+ new_parent);
+ }
}
} // namespace wm
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index fb12959359..fd77505c40 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -68,13 +68,18 @@ ASH_EXPORT void AdjustBoundsToEnsureWindowVisibility(
ASH_EXPORT bool MoveWindowToEventRoot(aura::Window* window,
const ui::Event& event);
-// Adds |child| and all its transient children to |window|.
-void ReparentChildWithTransientChildren(aura::Window* window,
- aura::Window* child);
-
-// Changes the parent of all transient children of a |child| to |window|.
-void ReparentTransientChildrenOfChild(aura::Window* window,
- aura::Window* child);
+// Changes the parent of a |child| and all its transient children that are
+// themselves children of |old_parent| to |new_parent|.
+void ReparentChildWithTransientChildren(aura::Window* child,
+ aura::Window* old_parent,
+ aura::Window* new_parent);
+
+// Changes the parent of all transient children of a |child| to |new_parent|.
+// Does not change parent of the transient children that are not themselves
+// children of |old_parent|.
+void ReparentTransientChildrenOfChild(aura::Window* child,
+ aura::Window* old_parent,
+ aura::Window* new_parent);
} // namespace wm
} // namespace ash
diff --git a/ash/wm/workspace/colored_window_controller.cc b/ash/wm/workspace/colored_window_controller.cc
deleted file mode 100644
index bffff5486f..0000000000
--- a/ash/wm/workspace/colored_window_controller.cc
+++ /dev/null
@@ -1,92 +0,0 @@
-// 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/workspace/colored_window_controller.h"
-
-#include "ash/shell_window_ids.h"
-#include "ash/wm/window_state.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/root_window.h"
-#include "ui/gfx/canvas.h"
-#include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
-
-namespace ash {
-namespace internal {
-
-// View implementation responsible for rendering the background.
-class ColoredWindowController::View : public views::WidgetDelegateView {
- public:
- explicit View(ColoredWindowController* controller);
- virtual ~View();
-
- // Closes the hosting widget.
- void Close();
-
- // WidgetDelegate overrides:
- virtual views::View* GetContentsView() OVERRIDE;
-
- private:
- ColoredWindowController* controller_;
-
- DISALLOW_COPY_AND_ASSIGN(View);
-};
-
-ColoredWindowController::View::View(ColoredWindowController* controller)
- : controller_(controller) {
-}
-
-ColoredWindowController::View::~View() {
- if (controller_)
- controller_->view_ = NULL;
-}
-
-void ColoredWindowController::View::Close() {
- controller_ = NULL;
- GetWidget()->Close();
-}
-
-views::View* ColoredWindowController::View::GetContentsView() {
- return this;
-}
-
-ColoredWindowController::ColoredWindowController(aura::Window* parent,
- const std::string& window_name)
- : view_(new View(this)) {
- views::Widget* widget = new views::Widget;
- views::Widget::InitParams params(
- views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.delegate = view_;
- params.parent = parent;
- params.can_activate = false;
- params.accept_events = false;
- params.layer_type = ui::LAYER_SOLID_COLOR;
- widget->Init(params);
- // Do this so the parent doesn't attempt to enforce any bounds constraints on
- // us.
- wm::GetWindowState(widget->GetNativeView())->SetTrackedByWorkspace(false);
- widget->GetNativeView()->SetProperty(aura::client::kAnimationsDisabledKey,
- true);
- widget->GetNativeView()->SetName(window_name);
- // The bounds should match the parent exactly. We don't go through
- // Widget::SetBounds() as that may try to place on a different display.
- widget->GetNativeWindow()->SetBounds(gfx::Rect(parent->bounds()));
-}
-
-ColoredWindowController::~ColoredWindowController() {
- if (view_)
- view_->Close();
-}
-
-void ColoredWindowController::SetColor(SkColor color) {
- if (view_)
- view_->GetWidget()->GetNativeView()->layer()->SetColor(color);
-}
-
-views::Widget* ColoredWindowController::GetWidget() {
- return view_ ? view_->GetWidget() : NULL;
-}
-
-} // namespace internal
-} // namespace ash
diff --git a/ash/wm/workspace/colored_window_controller.h b/ash/wm/workspace/colored_window_controller.h
deleted file mode 100644
index d70a83b7a3..0000000000
--- a/ash/wm/workspace/colored_window_controller.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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.
-
-#ifndef ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
-#define ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
-
-#include <string>
-
-#include "ash/ash_export.h"
-#include "base/basictypes.h"
-
-typedef unsigned int SkColor;
-
-namespace aura {
-class Window;
-}
-
-namespace views {
-class Widget;
-}
-
-namespace ash {
-namespace internal {
-
-// ColoredWindowController creates a Widget whose layer is LAYER_SOLID_COLOR.
-// The Widget is sized to the supplied Window and parented to the specified
-// Window. It is used for animations.
-class ASH_EXPORT ColoredWindowController {
- public:
- ColoredWindowController(aura::Window* parent, const std::string& window_name);
- ~ColoredWindowController();
-
- // Changes the background color.
- void SetColor(SkColor color);
-
- views::Widget* GetWidget();
-
- private:
- class View;
-
- // View responsible for rendering the background. This is non-NULL if the
- // widget containing it is deleted. It is owned by the widget.
- View* view_;
-
- DISALLOW_COPY_AND_ASSIGN(ColoredWindowController);
-};
-
-} // namespace internal
-} // namespace ash
-
-#endif // ASH_WM_WORKSPACE_COLORED_WINDOW_CONTROLLER_H_
diff --git a/ash/wm/workspace/desktop_background_fade_controller.cc b/ash/wm/workspace/desktop_background_fade_controller.cc
deleted file mode 100644
index abbdf578c8..0000000000
--- a/ash/wm/workspace/desktop_background_fade_controller.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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/workspace/desktop_background_fade_controller.h"
-
-#include "ash/wm/window_animations.h"
-#include "ash/wm/workspace/colored_window_controller.h"
-#include "base/time/time.h"
-#include "ui/aura/window.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-namespace internal {
-
-DesktopBackgroundFadeController::DesktopBackgroundFadeController(
- aura::Window* parent,
- aura::Window* position_above,
- base::TimeDelta duration,
- Direction direction) {
- SkColor start_color, target_color;
- gfx::Tween::Type tween_type;
- if (direction == FADE_OUT) {
- start_color = SkColorSetARGB(0, 0, 0, 0);
- target_color = SK_ColorBLACK;
- tween_type = gfx::Tween::EASE_IN_OUT;
- } else {
- start_color = SK_ColorBLACK;
- target_color = SkColorSetARGB(0, 0, 0, 0);
- tween_type = gfx::Tween::EASE_IN_OUT;
- }
-
- window_controller_.reset(
- new ColoredWindowController(parent, "DesktopFade"));
-
- // Force the window to be directly on top of the desktop.
- aura::Window* fade_window = window_controller_->GetWidget()->GetNativeView();
- parent->StackChildBelow(fade_window, position_above);
- parent->StackChildAbove(fade_window, position_above);
- window_controller_->SetColor(start_color);
- views::corewm::SetWindowVisibilityAnimationTransition(
- window_controller_->GetWidget()->GetNativeView(),
- views::corewm::ANIMATE_NONE);
- window_controller_->GetWidget()->Show();
- {
- ui::ScopedLayerAnimationSettings scoped_setter(
- fade_window->layer()->GetAnimator());
- scoped_setter.AddObserver(this);
- scoped_setter.SetTweenType(tween_type);
- scoped_setter.SetTransitionDuration(duration);
- window_controller_->SetColor(target_color);
- }
-}
-
-DesktopBackgroundFadeController::~DesktopBackgroundFadeController() {
- StopObservingImplicitAnimations();
-}
-
-void DesktopBackgroundFadeController::OnImplicitAnimationsCompleted() {
- window_controller_.reset();
-}
-
-} // namespace internal
-} // namespace ash
diff --git a/ash/wm/workspace/desktop_background_fade_controller.h b/ash/wm/workspace/desktop_background_fade_controller.h
deleted file mode 100644
index 3cc0137da6..0000000000
--- a/ash/wm/workspace/desktop_background_fade_controller.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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.
-
-#ifndef ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
-#define ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
-
-#include "ash/ash_export.h"
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "ui/compositor/layer_animation_observer.h"
-
-namespace aura {
-class Window;
-}
-
-namespace base {
-class TimeDelta;
-}
-
-namespace ash {
-namespace internal {
-
-class ColoredWindowController;
-
-// DesktopBackgroundFadeController handles fading in or out the desktop. It is
-// used when maximizing or restoring a window. It is implemented as a colored
-// layer whose opacity varies. This results in fading in or out all the windows
-// the DesktopBackgroundFadeController is placed on top of. This is used
-// instead of varying the opacity for two reasons:
-// . The window showing background and the desktop workspace do not have a
-// common parent that can be animated. This could be fixed, but wouldn't
-// address the following.
-// . When restoring the window is moved back to the desktop workspace. If we
-// animated the opacity of the desktop workspace the cross fade would be
-// effected.
-class ASH_EXPORT DesktopBackgroundFadeController
- : public ui::ImplicitAnimationObserver {
- public:
- // Direction to fade.
- enum Direction {
- FADE_IN,
- FADE_OUT,
- };
-
- // Creates a new DesktopBackgroundFadeController. |parent| is the Window to
-// parent the newly created window to. The newly created window is stacked
-// directly on top of |position_above|. The window animating the fade is
-// destroyed as soon as the animation completes.
- DesktopBackgroundFadeController(aura::Window* parent,
- aura::Window* position_above,
- base::TimeDelta duration,
- Direction direction);
- virtual ~DesktopBackgroundFadeController();
-
- private:
- // ImplicitAnimationObserver overrides:
- virtual void OnImplicitAnimationsCompleted() OVERRIDE;
-
- scoped_ptr<ColoredWindowController> window_controller_;
-
- DISALLOW_COPY_AND_ASSIGN(DesktopBackgroundFadeController);
-};
-
-} // namespace internal
-} // namespace ash
-
-#endif // ASH_WM_WORKSPACE_DESKTOP_BACKGROUND_FADE_CONTROLLER_H_
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc
index 0e69c85ccc..83a0e3fdcb 100644
--- a/ash/wm/workspace/phantom_window_controller.cc
+++ b/ash/wm/workspace/phantom_window_controller.cc
@@ -15,11 +15,11 @@
#include "ui/gfx/animation/slide_animation.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/skia_util.h"
+#include "ui/views/background.h"
#include "ui/views/painter.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
-
namespace ash {
namespace internal {
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index a0ae8cc370..fe7d748d19 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -20,6 +20,7 @@
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
#include "ui/base/ui_base_types.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/views/corewm/window_util.h"
@@ -71,7 +72,9 @@ WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
shelf_(NULL),
window_(window),
work_area_in_parent_(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
- window->parent())) {
+ window->parent())),
+ is_fullscreen_(GetRootWindowController(
+ window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) {
}
WorkspaceLayoutManager::~WorkspaceLayoutManager() {
@@ -84,7 +87,8 @@ void WorkspaceLayoutManager::SetShelf(internal::ShelfLayoutManager* shelf) {
void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
AdjustWindowBoundsWhenAdded(wm::GetWindowState(child));
BaseLayoutManager::OnWindowAddedToLayout(child);
- UpdateDesktopVisibility();
+ UpdateShelfVisibility();
+ UpdateFullscreenState();
WindowPositioner::RearrangeVisibleWindowOnShow(child);
}
@@ -96,7 +100,8 @@ void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
BaseLayoutManager::OnWindowRemovedFromLayout(child);
- UpdateDesktopVisibility();
+ UpdateShelfVisibility();
+ UpdateFullscreenState();
}
void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
@@ -105,34 +110,32 @@ void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
if (child->TargetVisibility()) {
WindowPositioner::RearrangeVisibleWindowOnShow(child);
} else {
- if (wm::GetWindowState(child)->IsFullscreen()) {
- ash::Shell::GetInstance()->NotifyFullscreenStateChange(
- false, child->GetRootWindow());
- }
+ if (wm::GetWindowState(child)->IsFullscreen())
+ UpdateFullscreenState();
WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
}
- UpdateDesktopVisibility();
+ UpdateShelfVisibility();
}
void WorkspaceLayoutManager::SetChildBounds(
Window* child,
const gfx::Rect& requested_bounds) {
- if (!wm::GetWindowState(child)->tracked_by_workspace()) {
+ wm::WindowState* window_state = wm::GetWindowState(child);
+ if (window_state->is_dragged()) {
SetChildBoundsDirect(child, requested_bounds);
- return;
- }
- gfx::Rect child_bounds(requested_bounds);
- // Some windows rely on this to set their initial bounds.
- if (!SetMaximizedOrFullscreenBounds(wm::GetWindowState(child))) {
+ } else if (!SetMaximizedOrFullscreenBounds(window_state)) {
+ // Some windows rely on this to set their initial bounds.
// Non-maximized/full-screen windows have their size constrained to the
// work-area.
+ gfx::Rect child_bounds(requested_bounds);
child_bounds.set_width(std::min(work_area_in_parent_.width(),
child_bounds.width()));
- child_bounds.set_height(
- std::min(work_area_in_parent_.height(), child_bounds.height()));
+ child_bounds.set_height(std::min(work_area_in_parent_.height(),
+ child_bounds.height()));
+ AdjustSnappedBounds(window_state, &child_bounds);
SetChildBoundsDirect(child, child_bounds);
}
- UpdateDesktopVisibility();
+ UpdateShelfVisibility();
}
void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
@@ -154,11 +157,10 @@ void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
}
}
-void WorkspaceLayoutManager::OnTrackedByWorkspaceChanged(
- wm::WindowState* window_state,
- bool old){
- if (window_state->tracked_by_workspace())
- SetMaximizedOrFullscreenBounds(window_state);
+void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) {
+ BaseLayoutManager::OnWindowStackingChanged(window);
+ UpdateShelfVisibility();
+ UpdateFullscreenState();
}
void WorkspaceLayoutManager::OnWindowShowTypeChanged(
@@ -189,9 +191,7 @@ void WorkspaceLayoutManager::OnWindowShowTypeChanged(
if (old_state != new_state &&
(new_state == ui::SHOW_STATE_FULLSCREEN ||
old_state == ui::SHOW_STATE_FULLSCREEN)) {
- ash::Shell::GetInstance()->NotifyFullscreenStateChange(
- new_state == ui::SHOW_STATE_FULLSCREEN,
- window_state->window()->GetRootWindow());
+ UpdateFullscreenState();
}
UpdateBoundsFromShowState(window_state, old_state);
@@ -206,7 +206,7 @@ void WorkspaceLayoutManager::ShowStateChanged(
wm::WindowState* state,
ui::WindowShowState last_show_state) {
BaseLayoutManager::ShowStateChanged(state, last_show_state);
- UpdateDesktopVisibility();
+ UpdateShelfVisibility();
}
void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
@@ -219,7 +219,7 @@ void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
wm::WindowState* window_state,
AdjustWindowReason reason) {
- if (!window_state->tracked_by_workspace())
+ if (window_state->is_dragged())
return;
// Do not cross fade here: the window's layer hierarchy may be messed up for
@@ -249,8 +249,9 @@ void WorkspaceLayoutManager::AdjustWindowBoundsForWorkAreaChange(
work_area_in_parent_, &bounds);
break;
}
+ AdjustSnappedBounds(window_state, &bounds);
if (window_state->window()->bounds() != bounds)
- window_state->window()->SetBounds(bounds);
+ SetChildBoundsAnimated(window_state->window(), bounds);
}
void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
@@ -263,7 +264,7 @@ void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
if (window_state->window()->bounds().IsEmpty())
return;
- if (!window_state->tracked_by_workspace())
+ if (window_state->is_dragged())
return;
if (SetMaximizedOrFullscreenBounds(window_state))
@@ -281,21 +282,33 @@ void WorkspaceLayoutManager::AdjustWindowBoundsWhenAdded(
ash::wm::AdjustBoundsToEnsureWindowVisibility(
display_area, min_width, min_height, &bounds);
+ AdjustSnappedBounds(window_state, &bounds);
if (window->bounds() != bounds)
window->SetBounds(bounds);
}
-void WorkspaceLayoutManager::UpdateDesktopVisibility() {
+void WorkspaceLayoutManager::UpdateShelfVisibility() {
if (shelf_)
shelf_->UpdateVisibilityState();
}
+void WorkspaceLayoutManager::UpdateFullscreenState() {
+ bool is_fullscreen = GetRootWindowController(
+ window_->GetRootWindow())->GetWindowForFullscreenMode() != NULL;
+ if (is_fullscreen != is_fullscreen_) {
+ ash::Shell::GetInstance()->NotifyFullscreenStateChange(
+ is_fullscreen, window_->GetRootWindow());
+ is_fullscreen_ = is_fullscreen;
+ }
+}
+
void WorkspaceLayoutManager::UpdateBoundsFromShowState(
wm::WindowState* window_state,
ui::WindowShowState last_show_state) {
aura::Window* window = window_state->window();
// See comment in SetMaximizedOrFullscreenBounds() as to why we use parent in
// these calculation.
+ // TODO(varkha): Change the switch statement below to use wm::WindowShowType.
switch (window_state->GetShowState()) {
case ui::SHOW_STATE_DEFAULT:
case ui::SHOW_STATE_NORMAL: {
@@ -317,13 +330,20 @@ void WorkspaceLayoutManager::UpdateBoundsFromShowState(
bounds_in_parent.SetRect(0, 0, 0, 0);
}
if (!bounds_in_parent.IsEmpty()) {
- gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible(
- window->parent()->parent(),
- bounds_in_parent);
- if (last_show_state == ui::SHOW_STATE_MINIMIZED)
- SetChildBoundsDirect(window, new_bounds);
- else
- CrossFadeToBounds(window, new_bounds);
+ if ((last_show_state == ui::SHOW_STATE_DEFAULT ||
+ last_show_state == ui::SHOW_STATE_NORMAL) &&
+ window_state->IsSnapped()) {
+ AdjustSnappedBounds(window_state, &bounds_in_parent);
+ SetChildBoundsAnimated(window, bounds_in_parent);
+ } else {
+ gfx::Rect new_bounds = BaseLayoutManager::BoundsWithScreenEdgeVisible(
+ window->parent()->parent(),
+ bounds_in_parent);
+ if (last_show_state == ui::SHOW_STATE_MINIMIZED)
+ SetChildBoundsDirect(window, new_bounds);
+ else
+ CrossFadeToBounds(window, new_bounds);
+ }
}
window_state->ClearRestoreBounds();
break;
@@ -363,8 +383,7 @@ void WorkspaceLayoutManager::UpdateBoundsFromShowState(
bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
wm::WindowState* window_state) {
- if (!window_state->tracked_by_workspace())
- return false;
+ DCHECK(!window_state->is_dragged());
// During animations there is a transform installed on the workspace
// windows. For this reason this code uses the parent so that the transform is
@@ -385,5 +404,33 @@ bool WorkspaceLayoutManager::SetMaximizedOrFullscreenBounds(
return false;
}
+void WorkspaceLayoutManager::AdjustSnappedBounds(wm::WindowState* window_state,
+ gfx::Rect* bounds) {
+ if (window_state->is_dragged() || !window_state->IsSnapped())
+ return;
+ gfx::Rect maximized_bounds = ScreenAsh::GetMaximizedWindowBoundsInParent(
+ window_state->window()->parent()->parent());
+ if (window_state->window_show_type() == wm::SHOW_TYPE_LEFT_SNAPPED)
+ bounds->set_x(maximized_bounds.x());
+ else if (window_state->window_show_type() == wm::SHOW_TYPE_RIGHT_SNAPPED)
+ bounds->set_x(maximized_bounds.right() - bounds->width());
+ bounds->set_y(maximized_bounds.y());
+ // TODO(varkha): Set width to 50% here for snapped windows.
+ bounds->set_height(maximized_bounds.height());
+}
+
+void WorkspaceLayoutManager::SetChildBoundsAnimated(Window* child,
+ const gfx::Rect& bounds) {
+ const int kBoundsChangeSlideDurationMs = 120;
+
+ ui::Layer* layer = child->layer();
+ ui::ScopedLayerAnimationSettings slide_settings(layer->GetAnimator());
+ slide_settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ slide_settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kBoundsChangeSlideDurationMs));
+ SetChildBoundsDirect(child, bounds);
+}
+
} // namespace internal
} // namespace ash
diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h
index 30a88f2e4a..ab4876c5b2 100644
--- a/ash/wm/workspace/workspace_layout_manager.h
+++ b/ash/wm/workspace/workspace_layout_manager.h
@@ -55,10 +55,7 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager {
virtual void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) OVERRIDE;
-
- // ash::WindowSettings::Observer overrides:
- virtual void OnTrackedByWorkspaceChanged(wm::WindowState* window_state,
- bool old) OVERRIDE;
+ virtual void OnWindowStackingChanged(aura::Window* window) OVERRIDE;
// WindowStateObserver overrides:
virtual void OnWindowShowTypeChanged(wm::WindowState* window_state,
@@ -76,7 +73,12 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager {
void AdjustWindowBoundsWhenAdded(wm::WindowState* window_state);
- void UpdateDesktopVisibility();
+ // Updates the visibility state of the shelf.
+ void UpdateShelfVisibility();
+
+ // Updates the fullscreen state of the workspace and notifies Shell if it
+ // has changed.
+ void UpdateFullscreenState();
// Updates the bounds of the window for a show state change from
// |last_show_state|.
@@ -87,6 +89,13 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager {
// window are set and true is returned. Does nothing otherwise.
bool SetMaximizedOrFullscreenBounds(wm::WindowState* window_state);
+ // Adjusts the |bounds| so that they are flush with the edge of the
+ // workspace if the window represented by |window_state| is side snapped.
+ void AdjustSnappedBounds(wm::WindowState* window_state, gfx::Rect* bounds);
+
+ // Animates the window bounds to |bounds|.
+ void SetChildBoundsAnimated(aura::Window* child, const gfx::Rect& bounds);
+
internal::ShelfLayoutManager* shelf_;
aura::Window* window_;
@@ -94,6 +103,9 @@ class ASH_EXPORT WorkspaceLayoutManager : public BaseLayoutManager {
// workspace switch.
gfx::Rect work_area_in_parent_;
+ // True if this workspace is currently in fullscreen mode.
+ bool is_fullscreen_;
+
DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager);
};
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 4302f1dc44..d12fb15bb7 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -11,6 +11,7 @@
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
+#include "ash/shell_observer.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
@@ -47,6 +48,38 @@ class MaximizeDelegateView : public views::WidgetDelegateView {
DISALLOW_COPY_AND_ASSIGN(MaximizeDelegateView);
};
+class TestShellObserver : public ShellObserver {
+ public:
+ TestShellObserver() : call_count_(0),
+ is_fullscreen_(false) {
+ Shell::GetInstance()->AddShellObserver(this);
+ }
+
+ virtual ~TestShellObserver() {
+ Shell::GetInstance()->RemoveShellObserver(this);
+ }
+
+ virtual void OnFullscreenStateChanged(bool is_fullscreen,
+ aura::Window* root_window) OVERRIDE {
+ call_count_++;
+ is_fullscreen_ = is_fullscreen;
+ }
+
+ int call_count() const {
+ return call_count_;
+ }
+
+ bool is_fullscreen() const {
+ return is_fullscreen_;
+ }
+
+ private:
+ int call_count_;
+ bool is_fullscreen_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestShellObserver);
+};
+
} // namespace
typedef test::AshTestBase WorkspaceLayoutManagerTest;
@@ -407,4 +440,42 @@ TEST_F(WorkspaceLayoutManagerTest, SizeToWorkArea) {
window->bounds().ToString());
}
+TEST_F(WorkspaceLayoutManagerTest, NotifyFullscreenChanges) {
+ TestShellObserver observer;
+ scoped_ptr<aura::Window> window1(
+ CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
+ scoped_ptr<aura::Window> window2(
+ CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 30, 40)));
+ wm::WindowState* window_state1 = wm::GetWindowState(window1.get());
+ wm::WindowState* window_state2 = wm::GetWindowState(window2.get());
+ window_state2->Activate();
+
+ window_state2->ToggleFullscreen();
+ EXPECT_EQ(1, observer.call_count());
+ EXPECT_TRUE(observer.is_fullscreen());
+
+ // When window1 moves to the front the fullscreen state should change.
+ window_state1->Activate();
+ EXPECT_EQ(2, observer.call_count());
+ EXPECT_FALSE(observer.is_fullscreen());
+
+ // It should change back if window2 becomes active again.
+ window_state2->Activate();
+ EXPECT_EQ(3, observer.call_count());
+ EXPECT_TRUE(observer.is_fullscreen());
+
+ window_state2->ToggleFullscreen();
+ EXPECT_EQ(4, observer.call_count());
+ EXPECT_FALSE(observer.is_fullscreen());
+
+ window_state2->ToggleFullscreen();
+ EXPECT_EQ(5, observer.call_count());
+ EXPECT_TRUE(observer.is_fullscreen());
+
+ // Closing the window should change the fullscreen state.
+ window2.reset();
+ EXPECT_EQ(6, observer.call_count());
+ EXPECT_FALSE(observer.is_fullscreen());
+}
+
} // namespace ash
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 3617da5225..b8cd504944 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -74,7 +74,7 @@ scoped_ptr<WindowResizer> CreateWindowResizer(
// is set by tab dragging code.
if (!window_state->IsNormalShowState() &&
(window_component != HTCAPTION ||
- window_state->tracked_by_workspace())) {
+ !window_state->is_dragged())) {
return scoped_ptr<WindowResizer>();
}
window_resizer = internal::WorkspaceWindowResizer::Create(
@@ -436,6 +436,7 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
if (!did_move_or_resize_ || details_.window_component != HTCAPTION)
return;
+ bool snapped = false;
// When the window is not in the normal show state, we do not snap the window.
// This happens when the user minimizes or maximizes the window by keyboard
// shortcut while dragging it. If the window is the result of dragging a tab
@@ -458,8 +459,11 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
if (window_state()->CanResize() &&
!dock_layout_->is_dragged_window_docked()) {
snap_sizer_->SnapWindowToTargetBounds();
+ snapped = true;
}
}
+ if (window_state()->IsSnapped() && !snapped)
+ window_state()->Restore();
}
void WorkspaceWindowResizer::RevertDrag() {
@@ -749,9 +753,9 @@ bool WorkspaceWindowResizer::UpdateMagnetismWindow(const gfx::Rect& bounds,
magnetism_window_ = NULL;
}
- // Avoid magnetically snapping to popups, menus, tooltips, controls and
- // windows that are not tracked by workspace.
- if (!window_state()->CanResize() || !window_state()->tracked_by_workspace())
+ // Avoid magnetically snapping windows that are not resizable.
+ // TODO(oshima): change this to window.type() == TYPE_NORMAL.
+ if (!window_state()->CanResize())
return false;
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index c049103ff3..599c4ea98e 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -596,6 +596,11 @@ TEST_F(WorkspaceWindowResizerTest, MouseMoveWithTouchDrag) {
// Assertions around dragging to the left/right edge of the screen.
TEST_F(WorkspaceWindowResizerTest, Edge) {
+ if (!SupportsHostWindowResize())
+ return;
+
+ // Resize host window to force insets update.
+ UpdateDisplay("800x700");
// TODO(varkha): Insets are reset after every drag because of
// http://crbug.com/292238.
// Window is wide enough not to get docked right away.
@@ -636,11 +641,12 @@ TEST_F(WorkspaceWindowResizerTest, Edge) {
}
// Test if the restore bounds is correct in multiple displays.
- window_state->ClearRestoreBounds();
-
if (!SupportsMultipleDisplays())
return;
+ // Restore the window to clear snapped state.
+ window_state->Restore();
+
UpdateDisplay("800x600,500x600");
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
@@ -654,7 +660,7 @@ TEST_F(WorkspaceWindowResizerTest, Edge) {
scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
window_.get(), gfx::Point(), HTCAPTION));
ASSERT_TRUE(resizer.get());
- resizer->Drag(CalculateDragPoint(*resizer, 499, 00), 0);
+ resizer->Drag(CalculateDragPoint(*resizer, 499, 0), 0);
int bottom =
ScreenAsh::GetDisplayWorkAreaBoundsInParent(window_.get()).bottom();
resizer->CompleteDrag(0);
@@ -783,8 +789,9 @@ TEST_F(WorkspaceWindowResizerTest, DontDragOffBottomWithMultiDisplay) {
window_->SetBounds(gfx::Rect(100, 200, 300, 20));
DCHECK_LT(window_->bounds().height(),
WorkspaceWindowResizer::kMinOnscreenHeight);
+ // Drag down avoiding dragging along the edge as that would side-snap.
scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
- window_.get(), gfx::Point(), HTCAPTION));
+ window_.get(), gfx::Point(10, 0), HTCAPTION));
ASSERT_TRUE(resizer.get());
resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0);
int expected_y = kRootHeight - window_->bounds().height() - 10;
@@ -802,8 +809,9 @@ TEST_F(WorkspaceWindowResizerTest, DontDragOffBottomWithMultiDisplay) {
{
window_->SetBounds(gfx::Rect(100, 200, 300, 400));
scoped_ptr<WindowResizer> resizer(CreateResizerForTest(
- window_.get(), gfx::Point(), HTCAPTION));
+ window_.get(), gfx::Point(10, 0), HTCAPTION));
ASSERT_TRUE(resizer.get());
+ // Drag down avoiding dragging along the edge as that would side-snap.
resizer->Drag(CalculateDragPoint(*resizer, 0, 400), 0);
int expected_y =
kRootHeight - WorkspaceWindowResizer::kMinOnscreenHeight - 10;
diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc
index 8d37858140..7bf6a28be9 100644
--- a/ash/wm/workspace_controller.cc
+++ b/ash/wm/workspace_controller.cc
@@ -4,6 +4,7 @@
#include "ash/wm/workspace_controller.h"
+#include "ash/root_window_controller.h"
#include "ash/shelf/shelf_layout_manager.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
@@ -30,6 +31,15 @@ namespace {
// animation (when logging in).
const int kInitialPauseTimeMS = 750;
+// Returns true if the |window| is docked and visible.
+bool IsDockedAndVisible(const aura::Window* window) {
+ return (window->parent()->id() == kShellWindowId_DockedContainer &&
+ window->IsVisible() &&
+ !wm::GetWindowState(window)->IsMinimized() &&
+ window->type() != aura::client::WINDOW_TYPE_POPUP &&
+ !window->transient_parent());
+}
+
} // namespace
WorkspaceController::WorkspaceController(aura::Window* viewport)
@@ -57,31 +67,42 @@ WorkspaceController::~WorkspaceController() {
WorkspaceWindowState WorkspaceController::GetWindowState() const {
if (!shelf_)
return WORKSPACE_WINDOW_STATE_DEFAULT;
+ const aura::Window* topmost_fullscreen_window = GetRootWindowController(
+ viewport_->GetRootWindow())->GetWindowForFullscreenMode();
+ if (topmost_fullscreen_window &&
+ !wm::GetWindowState(topmost_fullscreen_window)->ignored_by_shelf()) {
+ return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
+ }
+ // These are the container ids of containers which may contain windows that
+ // may overlap the launcher shelf and affect its transparency.
+ const int kWindowContainerIds[] = {
+ internal::kShellWindowId_DefaultContainer,
+ internal::kShellWindowId_DockedContainer,
+ };
const gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
- const aura::Window::Windows& windows(viewport_->children());
bool window_overlaps_launcher = false;
- bool has_maximized_window = false;
- for (aura::Window::Windows::const_iterator i = windows.begin();
- i != windows.end(); ++i) {
- wm::WindowState* window_state = wm::GetWindowState(*i);
- if (window_state->ignored_by_shelf())
- continue;
- ui::Layer* layer = (*i)->layer();
- if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
- continue;
- if (window_state->IsMaximized()) {
- // An untracked window may still be fullscreen so we keep iterating when
- // we hit a maximized window.
- has_maximized_window = true;
- } else if (window_state->IsFullscreen()) {
- return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
+ for (size_t idx = 0; idx < arraysize(kWindowContainerIds); idx++) {
+ const aura::Window* container = Shell::GetContainer(
+ viewport_->GetRootWindow(), kWindowContainerIds[idx]);
+ const aura::Window::Windows& windows(container->children());
+ for (aura::Window::Windows::const_iterator i = windows.begin();
+ i != windows.end(); ++i) {
+ wm::WindowState* window_state = wm::GetWindowState(*i);
+ if (window_state->ignored_by_shelf())
+ continue;
+ ui::Layer* layer = (*i)->layer();
+ if (!layer->GetTargetVisibility())
+ continue;
+ if (window_state->IsMaximized())
+ return WORKSPACE_WINDOW_STATE_MAXIMIZED;
+ if (!window_overlaps_launcher &&
+ ((*i)->bounds().Intersects(shelf_bounds) ||
+ IsDockedAndVisible(*i))) {
+ window_overlaps_launcher = true;
+ }
}
- if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
- window_overlaps_launcher = true;
}
- if (has_maximized_window)
- return WORKSPACE_WINDOW_STATE_MAXIMIZED;
return window_overlaps_launcher ?
WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc
index 48e08fc415..861578f648 100644
--- a/ash/wm/workspace_controller_unittest.cc
+++ b/ash/wm/workspace_controller_unittest.cc
@@ -103,7 +103,6 @@ class WorkspaceControllerTest : public test::AshTestBase {
aura::Window* window = CreateTestWindow();
window->SetBounds(bounds);
wm::WindowState* window_state = wm::GetWindowState(window);
- window_state->SetTrackedByWorkspace(true);
window_state->set_window_position_managed(true);
window->Show();
return window;
@@ -688,47 +687,6 @@ TEST_F(WorkspaceControllerTest, TransientParent) {
EXPECT_EQ(w2->parent(), w1->parent());
}
-// Verifies changing TrackedByWorkspace works.
-TEST_F(WorkspaceControllerTest, TrackedByWorkspace) {
- // Create a fullscreen window.
- scoped_ptr<Window> w1(CreateTestWindow());
- w1->Show();
- wm::ActivateWindow(w1.get());
- w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
- EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
- EXPECT_TRUE(w1->IsVisible());
-
- // Create a second fullscreen window and mark it not tracked by workspace
- // manager.
- scoped_ptr<Window> w2(CreateTestWindowUnparented());
- w2->SetBounds(gfx::Rect(1, 6, 25, 30));
- w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
- ParentWindowInPrimaryRootWindow(w2.get());
- w2->Show();
- wm::GetWindowState(w2.get())->SetTrackedByWorkspace(false);
- wm::ActivateWindow(w2.get());
-
- // Activating |w2| should force it to have the same parent as |w1|.
- EXPECT_EQ(w1->parent(), w2->parent());
- EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
- EXPECT_TRUE(w1->IsVisible());
- EXPECT_TRUE(w2->IsVisible());
-
- // Because |w2| isn't tracked we should be able to set the bounds of it.
- gfx::Rect bounds(w2->bounds());
- bounds.Offset(4, 5);
- w2->SetBounds(bounds);
- EXPECT_EQ(bounds.ToString(), w2->bounds().ToString());
-
- // Transition it to tracked by worskpace. It should end up in the desktop
- // workspace.
- wm::GetWindowState(w2.get())->SetTrackedByWorkspace(true);
- EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
- EXPECT_TRUE(w1->IsVisible());
- EXPECT_TRUE(w2->IsVisible());
- EXPECT_EQ(w1->parent(), w2->parent());
-}
-
// Test the placement of newly created windows.
TEST_F(WorkspaceControllerTest, BasicAutoPlacingOnCreate) {
if (!SupportsHostWindowResize())
@@ -1271,123 +1229,83 @@ class DragMaximizedNonTrackedWindowObserver
} // namespace
-// Verifies setting tracked by workspace to false and then dragging a fullscreen
-// window doesn't result in changing the window hierarchy (which typically
-// indicates new workspaces have been created).
-TEST_F(WorkspaceControllerTest, DragFullscreenNonTrackedWindow) {
- aura::test::EventGenerator generator(
- Shell::GetPrimaryRootWindow(), gfx::Point());
- generator.MoveMouseTo(5, 5);
-
- aura::test::TestWindowDelegate delegate;
- delegate.set_window_component(HTCAPTION);
- scoped_ptr<Window> w1(
- aura::test::CreateTestWindowWithDelegate(&delegate,
- aura::client::WINDOW_TYPE_NORMAL,
- gfx::Rect(5, 6, 7, 8),
- NULL));
- ParentWindowInPrimaryRootWindow(w1.get());
- w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
- w1->Show();
- wm::ActivateWindow(w1.get());
- DragMaximizedNonTrackedWindowObserver observer(w1.get());
- w1->parent()->parent()->AddObserver(&observer);
- const gfx::Rect max_bounds(w1->bounds());
+// Verifies that a new maximized window becomes visible after its activation
+// is requested, even though it does not become activated because a system
+// modal window is active.
+TEST_F(WorkspaceControllerTest, SwitchFromModal) {
+ scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
+ modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
+ modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
+ ParentWindowInPrimaryRootWindow(modal_window.get());
+ modal_window->Show();
+ wm::ActivateWindow(modal_window.get());
- generator.PressLeftButton();
- generator.MoveMouseTo(100, 100);
- // The bounds shouldn't change (drag should result in nothing happening
- // now.
- EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
+ scoped_ptr<Window> maximized_window(CreateTestWindow());
+ maximized_window->SetProperty(
+ aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ maximized_window->Show();
+ wm::ActivateWindow(maximized_window.get());
+ EXPECT_TRUE(maximized_window->IsVisible());
+}
- generator.ReleaseLeftButton();
- EXPECT_EQ(0, observer.change_count());
+namespace {
- // Set tracked to false and repeat, now the window should move.
- wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false);
- generator.MoveMouseTo(5, 5);
- generator.PressLeftButton();
- generator.MoveMouseBy(100, 100);
- EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
- max_bounds.width(), max_bounds.height()).ToString(),
- w1->bounds().ToString());
+// Subclass of WorkspaceControllerTest that runs tests with docked windows
+// enabled and disabled.
+class WorkspaceControllerTestDragging
+ : public WorkspaceControllerTest,
+ public testing::WithParamInterface<bool> {
+ public:
+ WorkspaceControllerTestDragging() {}
+ virtual ~WorkspaceControllerTestDragging() {}
+
+ // testing::Test:
+ virtual void SetUp() OVERRIDE {
+ WorkspaceControllerTest::SetUp();
+ if (!docked_windows_enabled()) {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ ash::switches::kAshDisableDockedWindows);
+ }
+ }
- generator.ReleaseLeftButton();
- wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true);
- // Marking the window tracked again should snap back to origin.
- EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
- EXPECT_EQ(0, observer.change_count());
+ bool docked_windows_enabled() const { return GetParam(); }
- w1->parent()->parent()->RemoveObserver(&observer);
-}
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTestDragging);
+};
-// Verifies setting tracked by workspace to false and then dragging a maximized
-// window can change the bound.
-TEST_F(WorkspaceControllerTest, DragMaximizedNonTrackedWindow) {
- aura::test::EventGenerator generator(
- Shell::GetPrimaryRootWindow(), gfx::Point());
- generator.MoveMouseTo(5, 5);
+} // namespace
+// Verifies that when dragging a window over the shelf overlap is detected
+// during and after the drag.
+TEST_P(WorkspaceControllerTestDragging, DragWindowOverlapShelf) {
aura::test::TestWindowDelegate delegate;
delegate.set_window_component(HTCAPTION);
scoped_ptr<Window> w1(
aura::test::CreateTestWindowWithDelegate(&delegate,
aura::client::WINDOW_TYPE_NORMAL,
- gfx::Rect(5, 6, 7, 8),
+ gfx::Rect(5, 5, 100, 50),
NULL));
ParentWindowInPrimaryRootWindow(w1.get());
- w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
- w1->Show();
- wm::ActivateWindow(w1.get());
- DragMaximizedNonTrackedWindowObserver observer(w1.get());
- w1->parent()->parent()->AddObserver(&observer);
- const gfx::Rect max_bounds(w1->bounds());
-
- generator.PressLeftButton();
- generator.MoveMouseTo(100, 100);
- // The bounds shouldn't change (drag should result in nothing happening
- // now.
- EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
- generator.ReleaseLeftButton();
- EXPECT_EQ(0, observer.change_count());
+ ShelfLayoutManager* shelf = shelf_layout_manager();
+ shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
- // Set tracked to false and repeat, now the window should move.
- wm::GetWindowState(w1.get())->SetTrackedByWorkspace(false);
- generator.MoveMouseTo(5, 5);
+ // Drag near the shelf
+ aura::test::EventGenerator generator(
+ Shell::GetPrimaryRootWindow(), gfx::Point());
+ generator.MoveMouseTo(10, 10);
generator.PressLeftButton();
- generator.MoveMouseBy(100, 100);
- EXPECT_EQ(gfx::Rect(max_bounds.x() + 100, max_bounds.y() + 100,
- max_bounds.width(), max_bounds.height()).ToString(),
- w1->bounds().ToString());
+ generator.MoveMouseTo(100, shelf->GetIdealBounds().y() - 20);
+ // Shelf should detect overlap. Overlap state stays after mouse is released.
+ EXPECT_TRUE(GetWindowOverlapsShelf());
generator.ReleaseLeftButton();
- wm::GetWindowState(w1.get())->SetTrackedByWorkspace(true);
- // Marking the window tracked again should snap back to origin.
- EXPECT_EQ(max_bounds.ToString(), w1->bounds().ToString());
- EXPECT_EQ(0, observer.change_count());
-
- w1->parent()->parent()->RemoveObserver(&observer);
+ EXPECT_TRUE(GetWindowOverlapsShelf());
}
-// Verifies that a new maximized window becomes visible after its activation
-// is requested, even though it does not become activated because a system
-// modal window is active.
-TEST_F(WorkspaceControllerTest, SwitchFromModal) {
- scoped_ptr<Window> modal_window(CreateTestWindowUnparented());
- modal_window->SetBounds(gfx::Rect(10, 11, 21, 22));
- modal_window->SetProperty(aura::client::kModalKey, ui::MODAL_TYPE_SYSTEM);
- ParentWindowInPrimaryRootWindow(modal_window.get());
- modal_window->Show();
- wm::ActivateWindow(modal_window.get());
-
- scoped_ptr<Window> maximized_window(CreateTestWindow());
- maximized_window->SetProperty(
- aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
- maximized_window->Show();
- wm::ActivateWindow(maximized_window.get());
- EXPECT_TRUE(maximized_window->IsVisible());
-}
+INSTANTIATE_TEST_CASE_P(DockedOrNot, WorkspaceControllerTestDragging,
+ ::testing::Bool());
} // namespace internal
} // namespace ash