summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authorflackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-20 15:19:04 +0000
committerflackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-02-20 15:19:04 +0000
commit582022050957ad3a4ec42eaab9f20177ec13e9e2 (patch)
tree555f4f6e685f13633c9c477bbc8fad0f5441be50 /ash
parentcd5664d9be154b4f051c47688803918a1a37d207 (diff)
downloadchromium_src-582022050957ad3a4ec42eaab9f20177ec13e9e2.zip
chromium_src-582022050957ad3a4ec42eaab9f20177ec13e9e2.tar.gz
chromium_src-582022050957ad3a4ec42eaab9f20177ec13e9e2.tar.bz2
Allow detaching and reattaching panels when dragged from/to launcher.
BUG=160646 TEST=PanelWindowResizerTest.PanelDetachReattach Review URL: https://chromiumcodereview.appspot.com/12281005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@183538 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/ash.gyp5
-rw-r--r--ash/root_window_controller.cc6
-rw-r--r--ash/root_window_controller.h1
-rw-r--r--ash/wm/panel_layout_manager.cc12
-rw-r--r--ash/wm/panel_layout_manager.h2
-rw-r--r--ash/wm/panel_window_event_filter.cc115
-rw-r--r--ash/wm/panel_window_event_filter.h60
-rw-r--r--ash/wm/panel_window_resizer.cc126
-rw-r--r--ash/wm/panel_window_resizer.h88
-rw-r--r--ash/wm/panel_window_resizer_unittest.cc137
-rw-r--r--ash/wm/stacking_controller.cc11
-rw-r--r--ash/wm/window_properties.cc1
-rw-r--r--ash/wm/window_properties.h3
-rw-r--r--ash/wm/workspace/workspace_window_resizer.cc7
14 files changed, 384 insertions, 190 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index ef1872d..7330065 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -353,8 +353,8 @@
'wm/panel_frame_view.h',
'wm/panel_layout_manager.cc',
'wm/panel_layout_manager.h',
- 'wm/panel_window_event_filter.cc',
- 'wm/panel_window_event_filter.h',
+ 'wm/panel_window_resizer.cc',
+ 'wm/panel_window_resizer.h',
'wm/partial_screenshot_view.cc',
'wm/partial_screenshot_view.h',
'wm/power_button_controller.cc',
@@ -626,6 +626,7 @@
'wm/drag_window_resizer_unittest.cc',
'wm/frame_painter_unittest.cc',
'wm/panel_layout_manager_unittest.cc',
+ 'wm/panel_window_resizer_unittest.cc',
'wm/power_button_controller_unittest.cc',
'wm/screen_dimmer_unittest.cc',
'wm/session_state_controller_impl2_unittest.cc',
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 19132d0..71c9101 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -22,7 +22,6 @@
#include "ash/wm/base_layout_manager.h"
#include "ash/wm/boot_splash_screen.h"
#include "ash/wm/panel_layout_manager.h"
-#include "ash/wm/panel_window_event_filter.h"
#include "ash/wm/property_util.h"
#include "ash/wm/root_window_layout_manager.h"
#include "ash/wm/screen_dimmer.h"
@@ -305,9 +304,8 @@ void RootWindowController::InitForPrimaryDisplay() {
internal::kShellWindowId_PanelContainer);
panel_layout_manager_ =
new internal::PanelLayoutManager(panel_container);
- panel_container->AddPreTargetHandler(
- new internal::PanelWindowEventFilter(
- panel_container, panel_layout_manager_));
+ panel_container_handler_.reset(
+ new ToplevelWindowEventHandler(panel_container));
panel_container->SetLayoutManager(panel_layout_manager_);
}
diff --git a/ash/root_window_controller.h b/ash/root_window_controller.h
index 4c6356b..83865c0 100644
--- a/ash/root_window_controller.h
+++ b/ash/root_window_controller.h
@@ -203,6 +203,7 @@ class ASH_EXPORT RootWindowController {
scoped_ptr<ToplevelWindowEventHandler> always_on_top_container_handler_;
scoped_ptr<ToplevelWindowEventHandler> modal_container_handler_;
scoped_ptr<ToplevelWindowEventHandler> lock_modal_container_handler_;
+ scoped_ptr<ToplevelWindowEventHandler> panel_container_handler_;
DISALLOW_COPY_AND_ASSIGN(RootWindowController);
};
diff --git a/ash/wm/panel_layout_manager.cc b/ash/wm/panel_layout_manager.cc
index aee2ca5..b7b6fc4 100644
--- a/ash/wm/panel_layout_manager.cc
+++ b/ash/wm/panel_layout_manager.cc
@@ -172,7 +172,8 @@ void PanelLayoutManager::StartDragging(aura::Window* panel) {
}
void PanelLayoutManager::FinishDragging() {
- DCHECK(dragged_panel_);
+ // Note, dragged panel may be null if the panel was just attached to the
+ // panel layout.
dragged_panel_ = NULL;
Relayout();
}
@@ -207,6 +208,9 @@ void PanelLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
}
void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
+}
+
+void PanelLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
PanelList::iterator found =
std::find(panel_windows_.begin(), panel_windows_.end(), child);
if (found != panel_windows_.end())
@@ -222,9 +226,6 @@ void PanelLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
Relayout();
}
-void PanelLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
-}
-
void PanelLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
bool visible) {
Relayout();
@@ -303,8 +304,7 @@ void PanelLayoutManager::OnWindowActivated(aura::Window* gained_active,
// Ignore if the panel that is not managed by this was activated.
if (gained_active &&
gained_active->type() == aura::client::WINDOW_TYPE_PANEL &&
- gained_active->GetRootWindow() ==
- launcher_->widget()->GetNativeView()->GetRootWindow()) {
+ gained_active->parent() == panel_container_) {
UpdateStacking(gained_active);
UpdateCallout(gained_active);
} else {
diff --git a/ash/wm/panel_layout_manager.h b/ash/wm/panel_layout_manager.h
index 9b22e06..9c94fef 100644
--- a/ash/wm/panel_layout_manager.h
+++ b/ash/wm/panel_layout_manager.h
@@ -60,6 +60,7 @@ class ASH_EXPORT PanelLayoutManager
void ToggleMinimize(aura::Window* panel);
+ ash::Launcher* launcher() { return launcher_; }
void SetLauncher(ash::Launcher* launcher);
// Overridden from aura::LayoutManager:
@@ -88,6 +89,7 @@ class ASH_EXPORT PanelLayoutManager
private:
friend class PanelLayoutManagerTest;
+ friend class PanelWindowResizerTest;
typedef std::list<aura::Window*> PanelList;
diff --git a/ash/wm/panel_window_event_filter.cc b/ash/wm/panel_window_event_filter.cc
deleted file mode 100644
index f101386..0000000
--- a/ash/wm/panel_window_event_filter.cc
+++ /dev/null
@@ -1,115 +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/panel_window_event_filter.h"
-
-#include "ash/wm/window_util.h"
-#include "base/message_loop.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/root_window.h"
-#include "ui/aura/window.h"
-#include "ui/aura/window_delegate.h"
-#include "ui/base/cursor/cursor.h"
-#include "ui/base/events/event.h"
-#include "ui/base/hit_test.h"
-#include "ui/base/ui_base_types.h"
-#include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/gfx/screen.h"
-
-namespace ash {
-namespace internal {
-
-PanelWindowEventFilter::PanelWindowEventFilter(
- aura::Window* panel_container,
- PanelLayoutManager* layout_manager)
- : panel_container_(panel_container),
- layout_manager_(layout_manager),
- dragged_panel_(NULL),
- drag_state_(DRAG_NONE) {
- panel_container_->AddObserver(this);
-}
-
-PanelWindowEventFilter::~PanelWindowEventFilter() {
- panel_container_->RemoveObserver(this);
- panel_container_->RemovePreTargetHandler(this);
-}
-
-void PanelWindowEventFilter::OnMouseEvent(ui::MouseEvent* event) {
- aura::Window* target = static_cast<aura::Window*>(event->target());
- switch (event->type()) {
- case ui::ET_MOUSE_PRESSED: {
- int hitResult = target->delegate()->
- GetNonClientComponent(event->location());
- if (hitResult == HTCAPTION) {
- if (drag_state_ == DRAG_NONE) {
- dragged_panel_ = target;
- drag_location_in_dragged_window_ = event->location();
- drag_state_ = DRAG_CLICKED;
- event->StopPropagation();
- }
- }
- return;
- }
-
- case ui::ET_MOUSE_DRAGGED:
- if (drag_state_ == DRAG_CLICKED) {
- drag_state_ = DRAG_STARTED;
- layout_manager_->StartDragging(dragged_panel_);
- }
- if (drag_state_ == DRAG_STARTED && HandleDrag(target, event))
- event->StopPropagation();
- return;
-
- case ui::ET_MOUSE_CAPTURE_CHANGED:
- if (drag_state_ == DRAG_STARTED) {
- FinishDrag();
- event->StopPropagation();
- } else if (drag_state_ == DRAG_CLICKED) {
- drag_state_ = DRAG_NONE;
- dragged_panel_ = NULL;
- event->StopPropagation();
- }
- return;
-
- case ui::ET_MOUSE_RELEASED:
- if (drag_state_ == DRAG_STARTED) {
- FinishDrag();
- event->StopPropagation();
- } else if (dragged_panel_ != NULL) {
- drag_state_ = DRAG_NONE;
- dragged_panel_ = NULL;
- event->StopPropagation();
- }
- return;
- default:
- return;
- }
-}
-
-void PanelWindowEventFilter::OnWindowDestroying(aura::Window* window) {
- CHECK_EQ(panel_container_, window);
- delete this;
-}
-
-bool PanelWindowEventFilter::HandleDrag(aura::Window* target,
- ui::LocatedEvent* event) {
- gfx::Rect target_bounds = dragged_panel_->bounds();
- gfx::Point event_location_in_parent(event->location());
- aura::Window::ConvertPointToTarget(target,
- target->parent(),
- &event_location_in_parent);
- target_bounds.set_x(
- event_location_in_parent.x() - drag_location_in_dragged_window_.x());
- dragged_panel_->SetBounds(target_bounds);
- return true;
-}
-
-void PanelWindowEventFilter::FinishDrag() {
- layout_manager_->FinishDragging();
- drag_state_ = DRAG_NONE;
- dragged_panel_ = NULL;
-}
-
-}
-}
diff --git a/ash/wm/panel_window_event_filter.h b/ash/wm/panel_window_event_filter.h
deleted file mode 100644
index 4a12338..0000000
--- a/ash/wm/panel_window_event_filter.h
+++ /dev/null
@@ -1,60 +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_PANEL_WINDOW_EVENT_FILTER_H
-#define ASH_WM_PANEL_WINDOW_EVENT_FILTER_H
-
-#include "ash/wm/panel_layout_manager.h"
-#include "ui/aura/window_observer.h"
-#include "ui/base/events/event_handler.h"
-#include "ui/gfx/point.h"
-#include "ui/gfx/rect.h"
-
-namespace aura {
-class Window;
-}
-
-namespace ui {
-class LocatedEvent;
-}
-
-namespace ash {
-namespace internal {
-
-class PanelWindowEventFilter : public ui::EventHandler,
- public aura::WindowObserver {
- public:
- PanelWindowEventFilter(aura::Window* panel_container,
- PanelLayoutManager* layout_manager);
- virtual ~PanelWindowEventFilter();
-
- // Overriden from ui::EventHandler:
- virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
-
- // Overridden from aura::WindowObserver:
- virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
-
- private:
- enum DragState {
- DRAG_NONE,
- DRAG_CLICKED,
- DRAG_STARTED
- };
-
- bool HandleDrag(aura::Window* target, ui::LocatedEvent* event);
- void FinishDrag();
-
- aura::Window* panel_container_;
- PanelLayoutManager* layout_manager_;
- gfx::Point drag_origin_;
- gfx::Point drag_location_in_dragged_window_;
- aura::Window* dragged_panel_;
- DragState drag_state_;
-
- DISALLOW_COPY_AND_ASSIGN(PanelWindowEventFilter);
-};
-
-}
-}
-#endif // ASH_WM_PANEL_EVENT_FILTER_H
diff --git a/ash/wm/panel_window_resizer.cc b/ash/wm/panel_window_resizer.cc
new file mode 100644
index 0000000..fee35a5
--- /dev/null
+++ b/ash/wm/panel_window_resizer.cc
@@ -0,0 +1,126 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/panel_window_resizer.h"
+
+#include "ash/launcher/launcher.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/cursor_manager.h"
+#include "ash/wm/panel_layout_manager.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/window_properties.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+
+namespace {
+const int kPanelSnapToLauncherDistance = 30;
+} // namespace
+
+PanelWindowResizer::~PanelWindowResizer() {
+ ash::Shell::GetInstance()->cursor_manager()->UnlockCursor();
+}
+
+// static
+PanelWindowResizer*
+PanelWindowResizer::Create(aura::Window* window,
+ const gfx::Point& location,
+ int window_component) {
+ Details details(window, location, window_component);
+ return details.is_resizable ? new PanelWindowResizer(details) : NULL;
+}
+
+void PanelWindowResizer::Drag(const gfx::Point& location, int event_flags) {
+ gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
+ if (bounds != details_.window->bounds()) {
+ if (!did_move_or_resize_) {
+ if (!details_.restore_bounds.IsEmpty())
+ ClearRestoreBounds(details_.window);
+ StartedDragging();
+ }
+ did_move_or_resize_ = true;
+ should_attach_ = AttachToLauncher(&bounds);
+ details_.window->SetBounds(bounds);
+ }
+}
+
+void PanelWindowResizer::CompleteDrag(int event_flags) {
+ if (should_attach_ != was_attached_) {
+ details_.window->SetProperty(internal::kPanelAttachedKey, should_attach_);
+ details_.window->SetDefaultParentByRootWindow(
+ details_.window->GetRootWindow(),
+ details_.window->bounds());
+ }
+ FinishDragging();
+}
+
+void PanelWindowResizer::RevertDrag() {
+ if (!did_move_or_resize_)
+ return;
+
+ details_.window->SetBounds(details_.initial_bounds_in_parent);
+
+ if (!details_.restore_bounds.IsEmpty())
+ SetRestoreBoundsInScreen(details_.window, details_.restore_bounds);
+ FinishDragging();
+}
+
+aura::Window* PanelWindowResizer::GetTarget() {
+ return details_.window;
+}
+
+PanelWindowResizer::PanelWindowResizer(const Details& details)
+ : details_(details),
+ panel_container_(NULL),
+ panel_layout_manager_(NULL),
+ did_move_or_resize_(false),
+ was_attached_(details_.window->GetProperty(internal::kPanelAttachedKey)),
+ should_attach_(was_attached_) {
+ DCHECK(details_.is_resizable);
+ ash::internal::RootWindowController* root_window_controller =
+ GetRootWindowController(details.window->GetRootWindow());
+ panel_container_ = root_window_controller->GetContainer(
+ internal::kShellWindowId_PanelContainer);
+ if (panel_container_) {
+ panel_layout_manager_ = static_cast<internal::PanelLayoutManager*>(
+ panel_container_->layout_manager());
+ }
+ ash::Shell::GetInstance()->cursor_manager()->LockCursor();
+}
+
+bool PanelWindowResizer::AttachToLauncher(gfx::Rect* bounds) {
+ bool should_attach = false;
+ if (panel_layout_manager_) {
+ int launcher_top = panel_layout_manager_->launcher()->widget()->
+ GetWindowBoundsInScreen().y();
+ if (bounds->y() >= (launcher_top - bounds->height() -
+ kPanelSnapToLauncherDistance)) {
+ should_attach = true;
+ bounds->set_y(launcher_top - bounds->height());
+ }
+ }
+ return should_attach;
+}
+
+void PanelWindowResizer::StartedDragging() {
+ if (was_attached_)
+ panel_layout_manager_->StartDragging(details_.window);
+}
+
+void PanelWindowResizer::FinishDragging() {
+ if (was_attached_)
+ panel_layout_manager_->FinishDragging();
+}
+
+} // namespace aura
diff --git a/ash/wm/panel_window_resizer.h b/ash/wm/panel_window_resizer.h
new file mode 100644
index 0000000..93cbb30
--- /dev/null
+++ b/ash/wm/panel_window_resizer.h
@@ -0,0 +1,88 @@
+// 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_PANEL_WINDOW_RESIZER_H_
+#define ASH_WM_PANEL_WINDOW_RESIZER_H_
+
+#include "ash/wm/window_resizer.h"
+#include "base/compiler_specific.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ash {
+
+namespace internal {
+class PanelLayoutManager;
+}
+
+// PanelWindowResizer is used by ToplevelWindowEventFilter to handle dragging,
+// moving or resizing panel window. These can be attached and detached from the
+// launcher.
+class ASH_EXPORT PanelWindowResizer : public WindowResizer {
+ public:
+ virtual ~PanelWindowResizer();
+
+ // Creates a new PanelWindowResizer. The caller takes ownership of the
+ // returned object. Returns NULL if not resizable.
+ static PanelWindowResizer* Create(aura::Window* window,
+ const gfx::Point& location,
+ int window_component);
+
+ // Returns true if the drag will result in changing the window in anyway.
+ bool is_resizable() const { return details_.is_resizable; }
+
+ bool changed_size() const {
+ return !(details_.bounds_change & kBoundsChange_Repositions);
+ }
+
+ // WindowResizer overides:
+ virtual void Drag(const gfx::Point& location, int event_flags) OVERRIDE;
+ virtual void CompleteDrag(int event_flags) OVERRIDE;
+ virtual void RevertDrag() OVERRIDE;
+ virtual aura::Window* GetTarget() OVERRIDE;
+
+ const gfx::Point& GetInitialLocationInParentForTest() const {
+ return details_.initial_location_in_parent;
+ }
+
+ private:
+ explicit PanelWindowResizer(const Details& details);
+
+ // Checks if the provided window bounds should attach to the launcher. If true
+ // the bounds are modified to snap the window to the launcher.
+ bool AttachToLauncher(gfx::Rect* bounds);
+
+ // Tracks the panel's initial position and attachment at the start of a drag
+ // and informs the PanelLayoutManager that a drag has started if necessary.
+ void StartedDragging();
+
+ // Informs the PanelLayoutManager that the drag is complete if it was informed
+ // of the drag start.
+ void FinishDragging();
+
+ const Details details_;
+
+ // Panel container window.
+ aura::Window* panel_container_;
+
+ // Weak pointer, owned by panel container.
+ internal::PanelLayoutManager* panel_layout_manager_;
+
+ // Set to true once Drag() is invoked and the bounds of the window change.
+ bool did_move_or_resize_;
+
+ // True if the window started attached to the launcher.
+ const bool was_attached_;
+
+ // True if the window should attach to the launcher after releasing.
+ bool should_attach_;
+
+ DISALLOW_COPY_AND_ASSIGN(PanelWindowResizer);
+};
+
+} // namespace aura
+
+#endif // ASH_WM_PANEL_WINDOW_RESIZER_H_
diff --git a/ash/wm/panel_window_resizer_unittest.cc b/ash/wm/panel_window_resizer_unittest.cc
new file mode 100644
index 0000000..aff789d
--- /dev/null
+++ b/ash/wm/panel_window_resizer_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright (c) 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/panel_window_resizer.h"
+
+#include "ash/launcher/launcher.h"
+#include "ash/root_window_controller.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/cursor_manager_test_api.h"
+#include "ash/test/test_launcher_delegate.h"
+#include "ash/wm/panel_layout_manager.h"
+#include "ash/wm/window_properties.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/base/hit_test.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace internal {
+
+class PanelWindowResizerTest : public test::AshTestBase {
+ public:
+ PanelWindowResizerTest() : window_(NULL) {}
+ virtual ~PanelWindowResizerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AshTestBase::SetUp();
+ window_.reset(CreatePanelWindow(gfx::Rect(0, 0, 201, 201)));
+
+ panel_layout_manager_ = static_cast<internal::PanelLayoutManager*>(
+ GetPanelContainer()->layout_manager());
+ launcher_top_ = panel_layout_manager_->launcher()->widget()->
+ GetWindowBoundsInScreen().y();
+ EXPECT_EQ(launcher_top_, window_->bounds().bottom());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ window_.reset();
+ AshTestBase::TearDown();
+ }
+
+ protected:
+ gfx::Point CalculateDragPoint(const PanelWindowResizer& resizer,
+ int delta_x,
+ int delta_y) const {
+ gfx::Point location = resizer.GetInitialLocationInParentForTest();
+ location.set_x(location.x() + delta_x);
+ location.set_y(location.y() + delta_y);
+ return location;
+ }
+
+ aura::Window* CreatePanelWindow(const gfx::Rect& bounds) {
+ aura::Window* window = CreateTestWindowInShellWithDelegateAndType(
+ NULL,
+ aura::client::WINDOW_TYPE_PANEL,
+ 0,
+ bounds);
+ test::TestLauncherDelegate* launcher_delegate =
+ test::TestLauncherDelegate::instance();
+ launcher_delegate->AddLauncherItem(window);
+ PanelLayoutManager* manager =
+ static_cast<PanelLayoutManager*>(GetPanelContainer()->layout_manager());
+ manager->Relayout();
+ return window;
+ }
+
+ aura::Window* GetPanelContainer() {
+ return Shell::GetContainer(
+ Shell::GetPrimaryRootWindow(),
+ internal::kShellWindowId_PanelContainer);
+ }
+
+ static PanelWindowResizer* CreatePanelWindowResizer(
+ aura::Window* window,
+ const gfx::Point& point_in_parent,
+ int window_component) {
+ return static_cast<PanelWindowResizer*>(CreateWindowResizer(
+ window, point_in_parent, window_component).release());
+ }
+
+ aura::test::TestWindowDelegate delegate_;
+ scoped_ptr<aura::Window> window_;
+ aura::Window* panel_container_;
+ internal::PanelLayoutManager* panel_layout_manager_;
+ int launcher_top_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PanelWindowResizerTest);
+};
+
+// Verifies a window can be dragged from the panel and detached and then
+// reattached.
+TEST_F(PanelWindowResizerTest, PanelDetachReattach) {
+ EXPECT_TRUE(window_->GetProperty(kPanelAttachedKey));
+ {
+ scoped_ptr<PanelWindowResizer> resizer(CreatePanelWindowResizer(
+ window_.get(), gfx::Point(), HTCAPTION));
+ ASSERT_TRUE(resizer.get());
+ gfx::Rect initial_bounds = window_->bounds();
+
+ // Drag the panel up slightly. The window should still be snapped to the
+ // launcher.
+ resizer->Drag(CalculateDragPoint(*resizer, 0, -5), 0);
+ EXPECT_EQ(initial_bounds.y(), window_->bounds().y());
+
+ // Drag further up and the window should now move to the cursor.
+ resizer->Drag(CalculateDragPoint(*resizer, 0, -100), 0);
+ EXPECT_EQ(initial_bounds.y() - 100, window_->bounds().y());
+
+ resizer->CompleteDrag(0);
+ }
+ // The panel should be detached.
+ EXPECT_FALSE(window_->GetProperty(kPanelAttachedKey));
+
+ {
+ scoped_ptr<PanelWindowResizer> resizer(CreatePanelWindowResizer(
+ window_.get(), gfx::Point(), HTCAPTION));
+ ASSERT_TRUE(resizer.get());
+ gfx::Rect initial_bounds = window_->bounds();
+
+ // Drag the panel down.
+ resizer->Drag(CalculateDragPoint(*resizer, 0, 95), 0);
+ // Release the mouse and the panel should be reattached.
+ resizer->CompleteDrag(0);
+ }
+ // The panel should be reattached and have snapped to the launcher.
+ EXPECT_TRUE(window_->GetProperty(kPanelAttachedKey));
+ EXPECT_EQ(launcher_top_, window_->bounds().bottom());
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/stacking_controller.cc b/ash/wm/stacking_controller.cc
index a9df31d..18b87fb 100644
--- a/ash/wm/stacking_controller.cc
+++ b/ash/wm/stacking_controller.cc
@@ -49,6 +49,10 @@ bool IsWindowModal(aura::Window* window) {
window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW;
}
+bool IsPanelAttached(aura::Window* window) {
+ return window->GetProperty(internal::kPanelAttachedKey);
+}
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -86,8 +90,11 @@ aura::Window* StackingController::GetDefaultParent(aura::Window* context,
return GetContainerById(
target_root, internal::kShellWindowId_UnparentedControlContainer);
case aura::client::WINDOW_TYPE_PANEL:
- return GetContainerById(target_root,
- internal::kShellWindowId_PanelContainer);
+ if (IsPanelAttached(window))
+ return GetContainerById(target_root,
+ internal::kShellWindowId_PanelContainer);
+ else
+ return GetAlwaysOnTopController(target_root)->GetContainer(window);
case aura::client::WINDOW_TYPE_MENU:
return GetContainerById(
target_root, internal::kShellWindowId_MenuContainer);
diff --git a/ash/wm/window_properties.cc b/ash/wm/window_properties.cc
index a52ab21..493cea7 100644
--- a/ash/wm/window_properties.cc
+++ b/ash/wm/window_properties.cc
@@ -23,6 +23,7 @@ DEFINE_OWNED_WINDOW_PROPERTY_KEY(ash::internal::AlwaysOnTopController,
DEFINE_WINDOW_PROPERTY_KEY(bool, kIgnoreSoloWindowFramePainterPolicy, false);
DEFINE_WINDOW_PROPERTY_KEY(bool, kIgnoredByShelfKey, false);
DEFINE_WINDOW_PROPERTY_KEY(bool, kImmersiveModeKey, false);
+DEFINE_WINDOW_PROPERTY_KEY(bool, kPanelAttachedKey, true);
DEFINE_WINDOW_PROPERTY_KEY(
ui::WindowShowState, kRestoreShowStateKey, ui::SHOW_STATE_DEFAULT);
DEFINE_WINDOW_PROPERTY_KEY(RootWindowController*,
diff --git a/ash/wm/window_properties.h b/ash/wm/window_properties.h
index 38d9cf9..41144c9 100644
--- a/ash/wm/window_properties.h
+++ b/ash/wm/window_properties.h
@@ -42,6 +42,9 @@ extern const aura::WindowProperty<bool>* const
// True if this is a browser window in immersive mode.
ASH_EXPORT extern const aura::WindowProperty<bool>* const kImmersiveModeKey;
+// True if this window is an attached panel.
+ASH_EXPORT extern const aura::WindowProperty<bool>* const kPanelAttachedKey;
+
// Used to remember the show state before the window was minimized.
extern const aura::WindowProperty<ui::WindowShowState>* const
kRestoreShowStateKey;
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
index 62ef772..748edf5 100644
--- a/ash/wm/workspace/workspace_window_resizer.cc
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -17,11 +17,13 @@
#include "ash/wm/cursor_manager.h"
#include "ash/wm/default_window_resizer.h"
#include "ash/wm/drag_window_resizer.h"
+#include "ash/wm/panel_window_resizer.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/phantom_window_controller.h"
#include "ash/wm/workspace/snap_sizer.h"
#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/client/window_types.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
@@ -41,7 +43,10 @@ scoped_ptr<WindowResizer> CreateWindowResizer(aura::Window* window,
return scoped_ptr<WindowResizer>();
WindowResizer* window_resizer = NULL;
- if (window->parent() &&
+ if (window->type() == aura::client::WINDOW_TYPE_PANEL) {
+ window_resizer = PanelWindowResizer::Create(
+ window, point_in_parent, window_component);
+ } else if (window->parent() &&
window->parent()->id() == internal::kShellWindowId_WorkspaceContainer) {
// Allow dragging maximized windows if it's not tracked by workspace. This
// is set by tab dragging code.