Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2238)

Unified Diff: ash/wm/workspace/multi_window_resize_controller.cc

Issue 9609016: Initial cut at multi-window resize code. There's still some TODOs, but (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ash/wm/workspace/multi_window_resize_controller.cc
diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c60a81812baaf4858518b791f381cf7928f8a95f
--- /dev/null
+++ b/ash/wm/workspace/multi_window_resize_controller.cc
@@ -0,0 +1,376 @@
+// 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/multi_window_resize_controller.h"
+
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/root_window_event_filter.h"
+#include "ash/wm/workspace/workspace_event_filter.h"
+#include "ash/wm/workspace/workspace_window_resizer.h"
+#include "ui/aura/event_filter.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/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/view.h"
+#include "ui/views/widget/widget.h"
+#include "ui/views/widget/widget_delegate.h"
+
+using aura::Window;
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+const int kShowDelayMS = 100;
+
+// Padding from the bottom/right edge the resize widget is shown at.
+const int kResizeWidgetPadding = 40;
+
+bool ContainsX(Window* window, int x) {
+ return window->bounds().x() <= x && window->bounds().right() >= x;
+}
+
+bool ContainsY(Window* window, int y) {
+ return window->bounds().y() <= y && window->bounds().bottom() >= y;
+}
+
+} // namespace
+
+// View contained in the widget. Passes along mouse events to the
+// MultiWindowResizeController so that it can start/stop the resize loop.
+class MultiWindowResizeController::ResizeView : public views::View {
+ public:
+ explicit ResizeView(MultiWindowResizeController* controller,
+ Direction direction)
+ : controller_(controller),
+ direction_(direction) {
+ }
+
+ // views::View overrides:
+ virtual gfx::Size GetPreferredSize() OVERRIDE {
+ return gfx::Size(30, 30);
+ }
+ virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
+ // TODO: replace with real assets.
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(128, 0, 0, 0));
+ paint.setStyle(SkPaint::kFill_Style);
+ paint.setAntiAlias(true);
+ canvas->AsCanvasSkia()->GetSkCanvas()->drawCircle(
+ SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(15), paint);
+ }
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE {
+ gfx::Point location(event.location());
+ views::View::ConvertPointToScreen(this, &location);
+ controller_->StartResize(location);
+ return true;
+ }
+ virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE {
+ gfx::Point location(event.location());
+ views::View::ConvertPointToScreen(this, &location);
+ controller_->Resize(location);
+ return true;
+ }
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE {
+ controller_->CompleteResize();
+ }
+ virtual void OnMouseCaptureLost() OVERRIDE {
+ controller_->CancelResize();
+ }
+ virtual gfx::NativeCursor GetCursor(
+ const views::MouseEvent& event) OVERRIDE {
+ int component = (direction_ == LEFT_RIGHT) ? HTRIGHT : HTBOTTOM;
+ return RootWindowEventFilter::CursorForWindowComponent(component);
+ }
+
+ private:
+ MultiWindowResizeController* controller_;
+ const Direction direction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResizeView);
+};
+
+// MouseWatcherHost implementation for MultiWindowResizeController. Forwards
+// Contains() to MultiWindowResizeController.
+class MultiWindowResizeController::ResizeMouseWatcherHost :
+ public views::MouseWatcherHost {
+ public:
+ ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}
+
+ // MouseWatcherHost overrides:
+ virtual bool Contains(const gfx::Point& screen_point,
+ MouseEventType type) OVERRIDE {
+ return !host_->IsOverWindows(screen_point);
+ }
+
+ private:
+ MultiWindowResizeController* host_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
+};
+
+MultiWindowResizeController::ResizeWindows::ResizeWindows()
+ : window1(NULL),
+ window2(NULL),
+ direction(TOP_BOTTOM){
+}
+
+MultiWindowResizeController::ResizeWindows::~ResizeWindows() {
+}
+
+bool MultiWindowResizeController::ResizeWindows::Equals(
+ const ResizeWindows& other) const {
+ return window1 == other.window1 &&
+ window2 == other.window2 &&
+ direction == other.direction;
+}
+
+MultiWindowResizeController::MultiWindowResizeController()
+ : resize_widget_(NULL),
+ grid_size_(0) {
+}
+
+MultiWindowResizeController::~MultiWindowResizeController() {
+ Hide();
+}
+
+void MultiWindowResizeController::Show(Window* window,
+ int component,
+ const gfx::Point& point) {
+ ResizeWindows windows(DetermineWindows(window, component, point));
+ if (IsShowing()) {
+ if (windows_.Equals(windows))
+ return;
+ Hide();
+ // Fall through to see if there is another hot spot we should show at.
+ }
+
+ windows_ = windows;
+ if (!windows_.is_valid())
+ return;
+ // TODO: need to listen for windows to be closed/destroyed, maybe window
+ // moved too.
+ show_timer_.Start(FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kShowDelayMS),
+ this, &MultiWindowResizeController::ShowNow);
+}
+
+void MultiWindowResizeController::Hide() {
+ if (window_resizer_.get())
+ return; // Ignore hides while actively resizing.
+
+ show_timer_.Stop();
+ if (!resize_widget_)
+ return;
+ resize_widget_->Close();
+ resize_widget_ = NULL;
+ windows_ = ResizeWindows();
+}
+
+void MultiWindowResizeController::MouseMovedOutOfHost() {
+ Hide();
+}
+
+MultiWindowResizeController::ResizeWindows
+MultiWindowResizeController::DetermineWindows(
+ Window* window,
+ int window_component,
+ const gfx::Point& point) const {
+ ResizeWindows result;
+ gfx::Point point_in_parent(point);
+ Window::ConvertPointToWindow(window, window->parent(), &point_in_parent);
+ switch (window_component) {
+ case HTRIGHT:
+ result.direction = LEFT_RIGHT;
+ result.window1 = window;
+ result.window2 = FindWindowByEdge(
+ window, HTLEFT, window->bounds().right(), point_in_parent.y());
+ break;
+ case HTLEFT:
+ result.direction = LEFT_RIGHT;
+ result.window1 = FindWindowByEdge(
+ window, HTRIGHT, window->bounds().x(), point_in_parent.y());
+ result.window2 = window;
+ break;
+ case HTTOP:
+ result.direction = TOP_BOTTOM;
+ result.window1 = FindWindowByEdge(
+ window, HTBOTTOM, point_in_parent.x(), window->bounds().y());
+ result.window2 = window;
+ break;
+ case HTBOTTOM:
+ result.direction = TOP_BOTTOM;
+ result.window1 = window;
+ result.window2 = FindWindowByEdge(
+ window, HTTOP, point_in_parent.x(), window->bounds().bottom());
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+Window* MultiWindowResizeController::FindWindowByEdge(
+ Window* window_to_ignore,
+ int edge_want,
+ int x,
+ int y) const {
+ Window* parent = window_to_ignore->parent();
+ const Window::Windows& windows(parent->children());
+ for (Window::Windows::const_reverse_iterator i = windows.rbegin();
+ i != windows.rend(); ++i) {
+ Window* window = *i;
+ if (window == window_to_ignore || !window->IsVisible())
+ continue;
+ switch (edge_want) {
+ case HTLEFT:
+ if (ContainsY(window, y) && window->bounds().x() == x)
+ return window;
+ break;
+ case HTRIGHT:
+ if (ContainsY(window, y) && window->bounds().right() == x)
+ return window;
+ break;
+ case HTTOP:
+ if (ContainsX(window, x) && window->bounds().y() == y)
+ return window;
+ break;
+ case HTBOTTOM:
+ if (ContainsX(window, x) && window->bounds().bottom() == y)
+ return window;
+ break;
+ default:
+ NOTREACHED();
+ }
+ // Window doesn't contain the edge, but if window contains |point|
+ // it's obscuring any other window that could be at the location.
+ if (window->bounds().Contains(x, y))
+ return NULL;
+ }
+ return NULL;
+}
+
+void MultiWindowResizeController::ShowNow() {
+ DCHECK(!resize_widget_);
+ DCHECK(windows_.is_valid());
+ show_timer_.Stop();
+ resize_widget_ = new views::Widget;
+ views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
+ params.transparent = true;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.parent = Shell::GetInstance()->GetContainer(
+ ash::internal::kShellWindowId_AlwaysOnTopContainer);
+ params.can_activate = false;
+ ResizeView* view = new ResizeView(this, windows_.direction);
+ params.delegate = new views::WidgetDelegateView;
+ resize_widget_->set_focus_on_creation(false);
+ resize_widget_->Init(params);
+ resize_widget_->GetNativeWindow()->SetName("MultiWindowResizeController");
+ resize_widget_->SetContentsView(view);
+ show_bounds_ = CalculateResizeWidgetBounds();
+ resize_widget_->SetBounds(show_bounds_);
+ resize_widget_->Show();
+}
+
+bool MultiWindowResizeController::IsShowing() const {
+ return resize_widget_ || show_timer_.IsRunning();
+}
+
+void MultiWindowResizeController::StartResize(
+ const gfx::Point& screen_location) {
+ DCHECK(!window_resizer_.get());
+ DCHECK(windows_.is_valid());
+ gfx::Point parent_location(screen_location);
+ aura::Window::ConvertPointToWindow(
+ windows_.window1->GetRootWindow(), windows_.window1->parent(),
+ &parent_location);
+ std::vector<aura::Window*> windows;
+ windows.push_back(windows_.window2);
+ // TODO: search for other windows.
+ int component = windows_.direction == LEFT_RIGHT ? HTRIGHT : HTBOTTOM;
+ window_resizer_.reset(WorkspaceWindowResizer::Create(
+ windows_.window1, parent_location, component, grid_size_, windows));
+}
+
+void MultiWindowResizeController::Resize(const gfx::Point& screen_location) {
+ gfx::Point parent_location(screen_location);
+ aura::Window::ConvertPointToWindow(windows_.window1->GetRootWindow(),
+ windows_.window1->parent(),
+ &parent_location);
+ window_resizer_->Drag(parent_location);
+ gfx::Rect bounds = CalculateResizeWidgetBounds();
+ if (windows_.direction == LEFT_RIGHT)
+ bounds.set_y(show_bounds_.y());
+ else
+ bounds.set_x(show_bounds_.x());
+ resize_widget_->SetBounds(bounds);
+}
+
+void MultiWindowResizeController::CompleteResize() {
+ window_resizer_->CompleteDrag();
+ window_resizer_.reset();
+
+ // Mouse may still be over resizer, if not hide.
+ gfx::Point screen_loc = gfx::Screen::GetCursorScreenPoint();
+ if (!resize_widget_->GetWindowScreenBounds().Contains(screen_loc))
+ Hide();
+}
+
+void MultiWindowResizeController::CancelResize() {
+ window_resizer_->RevertDrag();
+ window_resizer_.reset();
+ Hide();
+}
+
+gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds() const {
+ gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
+ int x = 0, y = 0;
+ if (windows_.direction == LEFT_RIGHT) {
+ x = windows_.window1->bounds().right() - pref.width() / 2;
+ y = std::min(windows_.window1->bounds().bottom(),
+ windows_.window2->bounds().bottom()) - kResizeWidgetPadding;
+ y -= pref.height() / 2;
+ } else {
+ x = std::min(windows_.window1->bounds().right(),
+ windows_.window2->bounds().right()) - kResizeWidgetPadding;
+ x -= pref.width() / 2;
+ y = windows_.window1->bounds().bottom() - pref.height() / 2;
+ }
+ return gfx::Rect(x, y, pref.width(), pref.height());
+}
+
+bool MultiWindowResizeController::IsOverWindows(
+ const gfx::Point& screen_location) const {
+ if (window_resizer_.get())
+ return true; // Ignore hides while actively resizing.
+
+ if (resize_widget_->GetWindowScreenBounds().Contains(screen_location))
+ return true;
+
+ return IsOverWindow(windows_.window1, screen_location) ||
+ IsOverWindow(windows_.window2, screen_location);
+}
+
+bool MultiWindowResizeController::IsOverWindow(
+ aura::Window* window,
+ const gfx::Point& screen_location) const {
+ if (!window->GetScreenBounds().Contains(screen_location))
+ return false;
+
+ gfx::Point window_loc(screen_location);
+ aura::Window::ConvertPointToWindow(
+ window->GetRootWindow(), window->parent(), &window_loc);
+ int component = window->delegate()->GetNonClientComponent(window_loc);
+ // TODO: this needs to make sure no other window is obscuring window.
+ return windows_.Equals(DetermineWindows(window, component, window_loc));
+}
+
+} // namespace internal
+} // namespace ash

Powered by Google App Engine
This is Rietveld 408576698