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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/wm/workspace/multi_window_resize_controller.h"
6
7 #include "ash/shell.h"
8 #include "ash/shell_window_ids.h"
9 #include "ash/wm/root_window_event_filter.h"
10 #include "ash/wm/workspace/workspace_event_filter.h"
11 #include "ash/wm/workspace/workspace_window_resizer.h"
12 #include "ui/aura/event_filter.h"
13 #include "ui/aura/root_window.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/window_delegate.h"
16 #include "ui/base/hit_test.h"
17 #include "ui/gfx/canvas.h"
18 #include "ui/gfx/canvas_skia.h"
19 #include "ui/gfx/screen.h"
20 #include "ui/views/view.h"
21 #include "ui/views/widget/widget.h"
22 #include "ui/views/widget/widget_delegate.h"
23
24 using aura::Window;
25
26 namespace ash {
27 namespace internal {
28
29 namespace {
30
31 const int kShowDelayMS = 100;
32
33 // Padding from the bottom/right edge the resize widget is shown at.
34 const int kResizeWidgetPadding = 40;
35
36 bool ContainsX(Window* window, int x) {
37 return window->bounds().x() <= x && window->bounds().right() >= x;
38 }
39
40 bool ContainsY(Window* window, int y) {
41 return window->bounds().y() <= y && window->bounds().bottom() >= y;
42 }
43
44 } // namespace
45
46 // View contained in the widget. Passes along mouse events to the
47 // MultiWindowResizeController so that it can start/stop the resize loop.
48 class MultiWindowResizeController::ResizeView : public views::View {
49 public:
50 explicit ResizeView(MultiWindowResizeController* controller,
51 Direction direction)
52 : controller_(controller),
53 direction_(direction) {
54 }
55
56 // views::View overrides:
57 virtual gfx::Size GetPreferredSize() OVERRIDE {
58 return gfx::Size(30, 30);
59 }
60 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
61 // TODO: replace with real assets.
62 SkPaint paint;
63 paint.setColor(SkColorSetARGB(128, 0, 0, 0));
64 paint.setStyle(SkPaint::kFill_Style);
65 paint.setAntiAlias(true);
66 canvas->AsCanvasSkia()->GetSkCanvas()->drawCircle(
67 SkIntToScalar(15), SkIntToScalar(15), SkIntToScalar(15), paint);
68 }
69 virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE {
70 gfx::Point location(event.location());
71 views::View::ConvertPointToScreen(this, &location);
72 controller_->StartResize(location);
73 return true;
74 }
75 virtual bool OnMouseDragged(const views::MouseEvent& event) OVERRIDE {
76 gfx::Point location(event.location());
77 views::View::ConvertPointToScreen(this, &location);
78 controller_->Resize(location);
79 return true;
80 }
81 virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE {
82 controller_->CompleteResize();
83 }
84 virtual void OnMouseCaptureLost() OVERRIDE {
85 controller_->CancelResize();
86 }
87 virtual gfx::NativeCursor GetCursor(
88 const views::MouseEvent& event) OVERRIDE {
89 int component = (direction_ == LEFT_RIGHT) ? HTRIGHT : HTBOTTOM;
90 return RootWindowEventFilter::CursorForWindowComponent(component);
91 }
92
93 private:
94 MultiWindowResizeController* controller_;
95 const Direction direction_;
96
97 DISALLOW_COPY_AND_ASSIGN(ResizeView);
98 };
99
100 // MouseWatcherHost implementation for MultiWindowResizeController. Forwards
101 // Contains() to MultiWindowResizeController.
102 class MultiWindowResizeController::ResizeMouseWatcherHost :
103 public views::MouseWatcherHost {
104 public:
105 ResizeMouseWatcherHost(MultiWindowResizeController* host) : host_(host) {}
106
107 // MouseWatcherHost overrides:
108 virtual bool Contains(const gfx::Point& screen_point,
109 MouseEventType type) OVERRIDE {
110 return !host_->IsOverWindows(screen_point);
111 }
112
113 private:
114 MultiWindowResizeController* host_;
115
116 DISALLOW_COPY_AND_ASSIGN(ResizeMouseWatcherHost);
117 };
118
119 MultiWindowResizeController::ResizeWindows::ResizeWindows()
120 : window1(NULL),
121 window2(NULL),
122 direction(TOP_BOTTOM){
123 }
124
125 MultiWindowResizeController::ResizeWindows::~ResizeWindows() {
126 }
127
128 bool MultiWindowResizeController::ResizeWindows::Equals(
129 const ResizeWindows& other) const {
130 return window1 == other.window1 &&
131 window2 == other.window2 &&
132 direction == other.direction;
133 }
134
135 MultiWindowResizeController::MultiWindowResizeController()
136 : resize_widget_(NULL),
137 grid_size_(0) {
138 }
139
140 MultiWindowResizeController::~MultiWindowResizeController() {
141 Hide();
142 }
143
144 void MultiWindowResizeController::Show(Window* window,
145 int component,
146 const gfx::Point& point) {
147 ResizeWindows windows(DetermineWindows(window, component, point));
148 if (IsShowing()) {
149 if (windows_.Equals(windows))
150 return;
151 Hide();
152 // Fall through to see if there is another hot spot we should show at.
153 }
154
155 windows_ = windows;
156 if (!windows_.is_valid())
157 return;
158 // TODO: need to listen for windows to be closed/destroyed, maybe window
159 // moved too.
160 show_timer_.Start(FROM_HERE,
161 base::TimeDelta::FromMilliseconds(kShowDelayMS),
162 this, &MultiWindowResizeController::ShowNow);
163 }
164
165 void MultiWindowResizeController::Hide() {
166 if (window_resizer_.get())
167 return; // Ignore hides while actively resizing.
168
169 show_timer_.Stop();
170 if (!resize_widget_)
171 return;
172 resize_widget_->Close();
173 resize_widget_ = NULL;
174 windows_ = ResizeWindows();
175 }
176
177 void MultiWindowResizeController::MouseMovedOutOfHost() {
178 Hide();
179 }
180
181 MultiWindowResizeController::ResizeWindows
182 MultiWindowResizeController::DetermineWindows(
183 Window* window,
184 int window_component,
185 const gfx::Point& point) const {
186 ResizeWindows result;
187 gfx::Point point_in_parent(point);
188 Window::ConvertPointToWindow(window, window->parent(), &point_in_parent);
189 switch (window_component) {
190 case HTRIGHT:
191 result.direction = LEFT_RIGHT;
192 result.window1 = window;
193 result.window2 = FindWindowByEdge(
194 window, HTLEFT, window->bounds().right(), point_in_parent.y());
195 break;
196 case HTLEFT:
197 result.direction = LEFT_RIGHT;
198 result.window1 = FindWindowByEdge(
199 window, HTRIGHT, window->bounds().x(), point_in_parent.y());
200 result.window2 = window;
201 break;
202 case HTTOP:
203 result.direction = TOP_BOTTOM;
204 result.window1 = FindWindowByEdge(
205 window, HTBOTTOM, point_in_parent.x(), window->bounds().y());
206 result.window2 = window;
207 break;
208 case HTBOTTOM:
209 result.direction = TOP_BOTTOM;
210 result.window1 = window;
211 result.window2 = FindWindowByEdge(
212 window, HTTOP, point_in_parent.x(), window->bounds().bottom());
213 break;
214 default:
215 break;
216 }
217 return result;
218 }
219
220 Window* MultiWindowResizeController::FindWindowByEdge(
221 Window* window_to_ignore,
222 int edge_want,
223 int x,
224 int y) const {
225 Window* parent = window_to_ignore->parent();
226 const Window::Windows& windows(parent->children());
227 for (Window::Windows::const_reverse_iterator i = windows.rbegin();
228 i != windows.rend(); ++i) {
229 Window* window = *i;
230 if (window == window_to_ignore || !window->IsVisible())
231 continue;
232 switch (edge_want) {
233 case HTLEFT:
234 if (ContainsY(window, y) && window->bounds().x() == x)
235 return window;
236 break;
237 case HTRIGHT:
238 if (ContainsY(window, y) && window->bounds().right() == x)
239 return window;
240 break;
241 case HTTOP:
242 if (ContainsX(window, x) && window->bounds().y() == y)
243 return window;
244 break;
245 case HTBOTTOM:
246 if (ContainsX(window, x) && window->bounds().bottom() == y)
247 return window;
248 break;
249 default:
250 NOTREACHED();
251 }
252 // Window doesn't contain the edge, but if window contains |point|
253 // it's obscuring any other window that could be at the location.
254 if (window->bounds().Contains(x, y))
255 return NULL;
256 }
257 return NULL;
258 }
259
260 void MultiWindowResizeController::ShowNow() {
261 DCHECK(!resize_widget_);
262 DCHECK(windows_.is_valid());
263 show_timer_.Stop();
264 resize_widget_ = new views::Widget;
265 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
266 params.transparent = true;
267 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
268 params.parent = Shell::GetInstance()->GetContainer(
269 ash::internal::kShellWindowId_AlwaysOnTopContainer);
270 params.can_activate = false;
271 ResizeView* view = new ResizeView(this, windows_.direction);
272 params.delegate = new views::WidgetDelegateView;
273 resize_widget_->set_focus_on_creation(false);
274 resize_widget_->Init(params);
275 resize_widget_->GetNativeWindow()->SetName("MultiWindowResizeController");
276 resize_widget_->SetContentsView(view);
277 show_bounds_ = CalculateResizeWidgetBounds();
278 resize_widget_->SetBounds(show_bounds_);
279 resize_widget_->Show();
280 }
281
282 bool MultiWindowResizeController::IsShowing() const {
283 return resize_widget_ || show_timer_.IsRunning();
284 }
285
286 void MultiWindowResizeController::StartResize(
287 const gfx::Point& screen_location) {
288 DCHECK(!window_resizer_.get());
289 DCHECK(windows_.is_valid());
290 gfx::Point parent_location(screen_location);
291 aura::Window::ConvertPointToWindow(
292 windows_.window1->GetRootWindow(), windows_.window1->parent(),
293 &parent_location);
294 std::vector<aura::Window*> windows;
295 windows.push_back(windows_.window2);
296 // TODO: search for other windows.
297 int component = windows_.direction == LEFT_RIGHT ? HTRIGHT : HTBOTTOM;
298 window_resizer_.reset(WorkspaceWindowResizer::Create(
299 windows_.window1, parent_location, component, grid_size_, windows));
300 }
301
302 void MultiWindowResizeController::Resize(const gfx::Point& screen_location) {
303 gfx::Point parent_location(screen_location);
304 aura::Window::ConvertPointToWindow(windows_.window1->GetRootWindow(),
305 windows_.window1->parent(),
306 &parent_location);
307 window_resizer_->Drag(parent_location);
308 gfx::Rect bounds = CalculateResizeWidgetBounds();
309 if (windows_.direction == LEFT_RIGHT)
310 bounds.set_y(show_bounds_.y());
311 else
312 bounds.set_x(show_bounds_.x());
313 resize_widget_->SetBounds(bounds);
314 }
315
316 void MultiWindowResizeController::CompleteResize() {
317 window_resizer_->CompleteDrag();
318 window_resizer_.reset();
319
320 // Mouse may still be over resizer, if not hide.
321 gfx::Point screen_loc = gfx::Screen::GetCursorScreenPoint();
322 if (!resize_widget_->GetWindowScreenBounds().Contains(screen_loc))
323 Hide();
324 }
325
326 void MultiWindowResizeController::CancelResize() {
327 window_resizer_->RevertDrag();
328 window_resizer_.reset();
329 Hide();
330 }
331
332 gfx::Rect MultiWindowResizeController::CalculateResizeWidgetBounds() const {
333 gfx::Size pref = resize_widget_->GetContentsView()->GetPreferredSize();
334 int x = 0, y = 0;
335 if (windows_.direction == LEFT_RIGHT) {
336 x = windows_.window1->bounds().right() - pref.width() / 2;
337 y = std::min(windows_.window1->bounds().bottom(),
338 windows_.window2->bounds().bottom()) - kResizeWidgetPadding;
339 y -= pref.height() / 2;
340 } else {
341 x = std::min(windows_.window1->bounds().right(),
342 windows_.window2->bounds().right()) - kResizeWidgetPadding;
343 x -= pref.width() / 2;
344 y = windows_.window1->bounds().bottom() - pref.height() / 2;
345 }
346 return gfx::Rect(x, y, pref.width(), pref.height());
347 }
348
349 bool MultiWindowResizeController::IsOverWindows(
350 const gfx::Point& screen_location) const {
351 if (window_resizer_.get())
352 return true; // Ignore hides while actively resizing.
353
354 if (resize_widget_->GetWindowScreenBounds().Contains(screen_location))
355 return true;
356
357 return IsOverWindow(windows_.window1, screen_location) ||
358 IsOverWindow(windows_.window2, screen_location);
359 }
360
361 bool MultiWindowResizeController::IsOverWindow(
362 aura::Window* window,
363 const gfx::Point& screen_location) const {
364 if (!window->GetScreenBounds().Contains(screen_location))
365 return false;
366
367 gfx::Point window_loc(screen_location);
368 aura::Window::ConvertPointToWindow(
369 window->GetRootWindow(), window->parent(), &window_loc);
370 int component = window->delegate()->GetNonClientComponent(window_loc);
371 // TODO: this needs to make sure no other window is obscuring window.
372 return windows_.Equals(DetermineWindows(window, component, window_loc));
373 }
374
375 } // namespace internal
376 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698