Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / tabs / tab_drag_controller.h
blobbb2412a30bb0001921f24211dc54de2eac67d9f2
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.
5 #ifndef CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
6 #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
8 #include <vector>
10 #include "base/memory/weak_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/timer/timer.h"
13 #include "chrome/browser/ui/host_desktop.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
15 #include "chrome/browser/ui/views/tabs/tab_strip_types.h"
16 #include "content/public/browser/notification_observer.h"
17 #include "content/public/browser/notification_registrar.h"
18 #include "ui/base/models/list_selection_model.h"
19 #include "ui/gfx/geometry/rect.h"
20 #include "ui/views/widget/widget_observer.h"
22 namespace gfx {
23 class Screen;
25 namespace ui {
26 class EventHandler;
27 class ListSelectionModel;
29 namespace views {
30 class View;
32 class Browser;
33 class Tab;
34 struct TabRendererData;
35 class TabStrip;
36 class TabStripModel;
38 // TabDragController is responsible for managing the tab dragging session. When
39 // the user presses the mouse on a tab a new TabDragController is created and
40 // Drag() is invoked as the mouse is dragged. If the mouse is dragged far enough
41 // TabDragController starts a drag session. The drag session is completed when
42 // EndDrag() is invoked (or the TabDragController is destroyed).
44 // While dragging within a tab strip TabDragController sets the bounds of the
45 // tabs (this is referred to as attached). When the user drags far enough such
46 // that the tabs should be moved out of the tab strip a new Browser is created
47 // and RunMoveLoop() is invoked on the Widget to drag the browser around. This
48 // is the default on aura.
49 class TabDragController : public content::NotificationObserver,
50 public views::WidgetObserver,
51 public TabStripModelObserver {
52 public:
53 // What should happen as the mouse is dragged within the tabstrip.
54 enum MoveBehavior {
55 // Only the set of visible tabs should change. This is only applicable when
56 // using touch layout.
57 MOVE_VISIBILE_TABS,
59 // Typical behavior where tabs are dragged around.
60 REORDER
63 // Indicates the event source that initiated the drag.
64 enum EventSource {
65 EVENT_SOURCE_MOUSE,
66 EVENT_SOURCE_TOUCH,
69 // Amount above or below the tabstrip the user has to drag before detaching.
70 static const int kTouchVerticalDetachMagnetism;
71 static const int kVerticalDetachMagnetism;
73 TabDragController();
74 ~TabDragController() override;
76 // Initializes TabDragController to drag the tabs in |tabs| originating from
77 // |source_tabstrip|. |source_tab| is the tab that initiated the drag and is
78 // contained in |tabs|. |mouse_offset| is the distance of the mouse pointer
79 // from the origin of the first tab in |tabs| and |source_tab_offset| the
80 // offset from |source_tab|. |source_tab_offset| is the horizontal offset of
81 // |mouse_offset| relative to |source_tab|. |initial_selection_model| is the
82 // selection model before the drag started and is only non-empty if
83 // |source_tab| was not initially selected.
84 void Init(TabStrip* source_tabstrip,
85 Tab* source_tab,
86 const std::vector<Tab*>& tabs,
87 const gfx::Point& mouse_offset,
88 int source_tab_offset,
89 const ui::ListSelectionModel& initial_selection_model,
90 MoveBehavior move_behavior,
91 EventSource event_source);
93 // Returns true if there is a drag underway and the drag is attached to
94 // |tab_strip|.
95 // NOTE: this returns false if the TabDragController is in the process of
96 // finishing the drag.
97 static bool IsAttachedTo(const TabStrip* tab_strip);
99 // Returns true if there is a drag underway.
100 static bool IsActive();
102 // Sets the move behavior. Has no effect if started_drag() is true.
103 void SetMoveBehavior(MoveBehavior behavior);
104 MoveBehavior move_behavior() const { return move_behavior_; }
106 EventSource event_source() const { return event_source_; }
108 // See description above fields for details on these.
109 bool active() const { return active_; }
110 const TabStrip* attached_tabstrip() const { return attached_tabstrip_; }
112 // Returns true if a drag started.
113 bool started_drag() const { return started_drag_; }
115 // Returns true if mutating the TabStripModel.
116 bool is_mutating() const { return is_mutating_; }
118 // Returns true if we've detached from a tabstrip and are running a nested
119 // move message loop.
120 bool is_dragging_window() const { return is_dragging_window_; }
122 // Invoked to drag to the new location, in screen coordinates.
123 void Drag(const gfx::Point& point_in_screen);
125 // Complete the current drag session.
126 void EndDrag(EndDragReason reason);
128 private:
129 // Used to indicate the direction the mouse has moved when attached.
130 static const int kMovedMouseLeft = 1 << 0;
131 static const int kMovedMouseRight = 1 << 1;
133 // Enumeration of the ways a drag session can end.
134 enum EndDragType {
135 // Drag session exited normally: the user released the mouse.
136 NORMAL,
138 // The drag session was canceled (alt-tab during drag, escape ...)
139 CANCELED,
141 // The tab (NavigationController) was destroyed during the drag.
142 TAB_DESTROYED
145 // Whether Detach() should release capture or not.
146 enum ReleaseCapture {
147 RELEASE_CAPTURE,
148 DONT_RELEASE_CAPTURE,
151 // Specifies what should happen when RunMoveLoop completes.
152 enum EndRunLoopBehavior {
153 // Indicates the drag should end.
154 END_RUN_LOOP_STOP_DRAGGING,
156 // Indicates the drag should continue.
157 END_RUN_LOOP_CONTINUE_DRAGGING
160 // Enumeration of the possible positions the detached tab may detach from.
161 enum DetachPosition {
162 DETACH_BEFORE,
163 DETACH_AFTER,
164 DETACH_ABOVE_OR_BELOW
167 // Specifies what should happen when a drag motion exits the tab strip region
168 // in an attempt to detach a tab.
169 enum DetachBehavior {
170 DETACHABLE,
171 NOT_DETACHABLE
174 // Indicates what should happen after invoking DragBrowserToNewTabStrip().
175 enum DragBrowserResultType {
176 // The caller should return immediately. This return value is used if a
177 // nested message loop was created or we're in a nested message loop and
178 // need to exit it.
179 DRAG_BROWSER_RESULT_STOP,
181 // The caller should continue.
182 DRAG_BROWSER_RESULT_CONTINUE,
185 // Stores the date associated with a single tab that is being dragged.
186 struct TabDragData {
187 TabDragData();
188 ~TabDragData();
190 // The WebContents being dragged.
191 content::WebContents* contents;
193 // This is the index of the tab in |source_tabstrip_| when the drag
194 // began. This is used to restore the previous state if the drag is aborted.
195 int source_model_index;
197 // If attached this is the tab in |attached_tabstrip_|.
198 Tab* attached_tab;
200 // Is the tab pinned?
201 bool pinned;
204 typedef std::vector<TabDragData> DragData;
206 // Sets |drag_data| from |tab|. This also registers for necessary
207 // notifications and resets the delegate of the WebContents.
208 void InitTabDragData(Tab* tab, TabDragData* drag_data);
210 // Overridden from content::NotificationObserver:
211 void Observe(int type,
212 const content::NotificationSource& source,
213 const content::NotificationDetails& details) override;
215 // Overriden from views::WidgetObserver:
216 void OnWidgetBoundsChanged(views::Widget* widget,
217 const gfx::Rect& new_bounds) override;
219 // Overriden from TabStripModelObserver:
220 void TabStripEmpty() override;
222 // Initialize the offset used to calculate the position to create windows
223 // in |GetWindowCreatePoint|. This should only be invoked from |Init|.
224 void InitWindowCreatePoint();
226 // Returns the point where a detached window should be created given the
227 // current mouse position |origin|.
228 gfx::Point GetWindowCreatePoint(const gfx::Point& origin) const;
230 void UpdateDockInfo(const gfx::Point& point_in_screen);
232 // Saves focus in the window that the drag initiated from. Focus will be
233 // restored appropriately if the drag ends within this same window.
234 void SaveFocus();
236 // Restore focus to the View that had focus before the drag was started, if
237 // the drag ends within the same Window as it began.
238 void RestoreFocus();
240 // Tests whether |point_in_screen| is past a minimum elasticity threshold
241 // required to start a drag.
242 bool CanStartDrag(const gfx::Point& point_in_screen) const;
244 // Invoked once a drag has started to determine the appropriate tabstrip to
245 // drag to (which may be the currently attached one).
246 void ContinueDragging(const gfx::Point& point_in_screen);
248 // Transitions dragging from |attached_tabstrip_| to |target_tabstrip|.
249 // |target_tabstrip| is NULL if the mouse is not over a valid tab strip. See
250 // DragBrowserResultType for details of the return type.
251 DragBrowserResultType DragBrowserToNewTabStrip(
252 TabStrip* target_tabstrip,
253 const gfx::Point& point_in_screen);
255 // Handles dragging for a touch tabstrip when the tabs are stacked. Doesn't
256 // actually reorder the tabs in anyway, just changes what's visible.
257 void DragActiveTabStacked(const gfx::Point& point_in_screen);
259 // Moves the active tab to the next/previous tab. Used when the next/previous
260 // tab is stacked.
261 void MoveAttachedToNextStackedIndex(const gfx::Point& point_in_screen);
262 void MoveAttachedToPreviousStackedIndex(const gfx::Point& point_in_screen);
264 // Handles dragging tabs while the tabs are attached.
265 void MoveAttached(const gfx::Point& point_in_screen);
267 // If necessary starts the |move_stacked_timer_|. The timer is started if
268 // close enough to an edge with stacked tabs.
269 void StartMoveStackedTimerIfNecessary(
270 const gfx::Point& point_in_screen,
271 int delay_ms);
273 // Returns the TabStrip for the specified window, or NULL if one doesn't exist
274 // or isn't compatible.
275 TabStrip* GetTabStripForWindow(gfx::NativeWindow window);
277 // Returns the compatible TabStrip to drag to at the specified point (screen
278 // coordinates), or NULL if there is none.
279 TabStrip* GetTargetTabStripForPoint(const gfx::Point& point_in_screen);
281 // Returns true if |tabstrip| contains the specified point in screen
282 // coordinates.
283 bool DoesTabStripContain(TabStrip* tabstrip,
284 const gfx::Point& point_in_screen) const;
286 // Returns the DetachPosition given the specified location in screen
287 // coordinates.
288 DetachPosition GetDetachPosition(const gfx::Point& point_in_screen);
290 // Attach the dragged Tab to the specified TabStrip.
291 void Attach(TabStrip* attached_tabstrip, const gfx::Point& point_in_screen);
293 // Detach the dragged Tab from the current TabStrip.
294 void Detach(ReleaseCapture release_capture);
296 // Detaches the tabs being dragged, creates a new Browser to contain them and
297 // runs a nested move loop.
298 void DetachIntoNewBrowserAndRunMoveLoop(const gfx::Point& point_in_screen);
300 // Runs a nested message loop that handles moving the current
301 // Browser. |drag_offset| is the offset from the window origin and is used in
302 // calculating the location of the window offset from the cursor while
303 // dragging.
304 void RunMoveLoop(const gfx::Vector2d& drag_offset);
306 // Determines the index to insert tabs at. |dragged_bounds| is the bounds of
307 // the tabs being dragged, |start| the index of the tab to start looking from.
308 // The search proceeds to the end of the strip.
309 int GetInsertionIndexFrom(const gfx::Rect& dragged_bounds, int start) const;
311 // Like GetInsertionIndexFrom(), but searches backwards from |start| to the
312 // beginning of the strip.
313 int GetInsertionIndexFromReversed(const gfx::Rect& dragged_bounds,
314 int start) const;
316 // Returns the index where the dragged WebContents should be inserted into
317 // |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
318 // coordinates relative to |attached_tabstrip_| and has had the mirroring
319 // transformation applied.
320 // NOTE: this is invoked from Attach() before the tabs have been inserted.
321 int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
323 // Returns true if |dragged_bounds| is close enough to the next stacked tab
324 // so that the active tab should be dragged there.
325 bool ShouldDragToNextStackedTab(const gfx::Rect& dragged_bounds,
326 int index) const;
328 // Returns true if |dragged_bounds| is close enough to the previous stacked
329 // tab so that the active tab should be dragged there.
330 bool ShouldDragToPreviousStackedTab(const gfx::Rect& dragged_bounds,
331 int index) const;
333 // Used by GetInsertionIndexForDraggedBounds() when the tabstrip is stacked.
334 int GetInsertionIndexForDraggedBoundsStacked(
335 const gfx::Rect& dragged_bounds) const;
337 // Retrieve the bounds of the DraggedTabView relative to the attached
338 // TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
339 // system.
340 gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
342 // Get the position of the dragged tab view relative to the attached tab
343 // strip with the mirroring transform applied.
344 gfx::Point GetAttachedDragPoint(const gfx::Point& point_in_screen);
346 // Finds the Tabs within the specified TabStrip that corresponds to the
347 // WebContents of the dragged tabs. Returns an empty vector if not attached.
348 std::vector<Tab*> GetTabsMatchingDraggedContents(TabStrip* tabstrip);
350 // Returns the bounds for the tabs based on the attached tab strip.
351 std::vector<gfx::Rect> CalculateBoundsForDraggedTabs();
353 // Does the work for EndDrag(). If we actually started a drag and |how_end| is
354 // not TAB_DESTROYED then one of EndDrag() or RevertDrag() is invoked.
355 void EndDragImpl(EndDragType how_end);
357 // Reverts a cancelled drag operation.
358 void RevertDrag();
360 // Reverts the tab at |drag_index| in |drag_data_|.
361 void RevertDragAt(size_t drag_index);
363 // Selects the dragged tabs in |model|. Does nothing if there are no longer
364 // any dragged contents (as happens when a WebContents is deleted out from
365 // under us).
366 void ResetSelection(TabStripModel* model);
368 // Restores |initial_selection_model_| to the |source_tabstrip_|.
369 void RestoreInitialSelection();
371 // Finishes a succesful drag operation.
372 void CompleteDrag();
374 // Maximizes the attached window.
375 void MaximizeAttachedWindow();
377 // Returns the bounds (in screen coordinates) of the specified View.
378 gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
380 // Hides the frame for the window that contains the TabStrip the current
381 // drag session was initiated from.
382 void HideFrame();
384 void BringWindowUnderPointToFront(const gfx::Point& point_in_screen);
386 // Convenience for getting the TabDragData corresponding to the tab the user
387 // started dragging.
388 TabDragData* source_tab_drag_data() {
389 return &(drag_data_[source_tab_index_]);
392 // Convenience for |source_tab_drag_data()->contents|.
393 content::WebContents* source_dragged_contents() {
394 return source_tab_drag_data()->contents;
397 // Returns the Widget of the currently attached TabStrip's BrowserView.
398 views::Widget* GetAttachedBrowserWidget();
400 // Returns true if the tabs were originality one after the other in
401 // |source_tabstrip_|.
402 bool AreTabsConsecutive();
404 // Calculates and returns new bounds for the dragged browser window.
405 // Takes into consideration current and restore bounds of |source| tab strip
406 // preventing the dragged size from being too small. Positions the new bounds
407 // such that the tab that was dragged remains under the |point_in_screen|.
408 // Offsets |drag_bounds| if necessary when dragging to the right from the
409 // source browser.
410 gfx::Rect CalculateDraggedBrowserBounds(TabStrip* source,
411 const gfx::Point& point_in_screen,
412 std::vector<gfx::Rect>* drag_bounds);
414 // Calculates scaled |drag_bounds| for dragged tabs and sets the tabs bounds.
415 // Layout of the tabstrip is performed and a new tabstrip width calculated.
416 // When |last_tabstrip_width| is larger than the new tabstrip width the tabs
417 // in attached tabstrip are scaled and the attached browser is positioned such
418 // that the tab that was dragged remains under the |point_in_screen|.
419 void AdjustBrowserAndTabBoundsForDrag(int last_tabstrip_width,
420 const gfx::Point& point_in_screen,
421 std::vector<gfx::Rect>* drag_bounds);
423 // Creates and returns a new Browser to handle the drag.
424 Browser* CreateBrowserForDrag(TabStrip* source,
425 const gfx::Point& point_in_screen,
426 gfx::Vector2d* drag_offset,
427 std::vector<gfx::Rect>* drag_bounds);
429 // Returns the TabStripModel for the specified tabstrip.
430 TabStripModel* GetModel(TabStrip* tabstrip) const;
432 // Returns the location of the cursor. This is either the location of the
433 // mouse or the location of the current touch point.
434 gfx::Point GetCursorScreenPoint();
436 // Returns the offset from the top left corner of the window to
437 // |point_in_screen|.
438 gfx::Vector2d GetWindowOffset(const gfx::Point& point_in_screen);
440 // Returns true if moving the mouse only changes the visible tabs.
441 bool move_only() const {
442 return (move_behavior_ == MOVE_VISIBILE_TABS) != 0;
445 // Returns the NativeWindow at the specified point. If |exclude_dragged_view|
446 // is true, then the dragged view is not considered.
447 gfx::NativeWindow GetLocalProcessWindow(const gfx::Point& screen_point,
448 bool exclude_dragged_view);
450 // Handles registering for notifications.
451 content::NotificationRegistrar registrar_;
453 EventSource event_source_;
455 // The TabStrip the drag originated from.
456 TabStrip* source_tabstrip_;
458 // The TabStrip the dragged Tab is currently attached to, or NULL if the
459 // dragged Tab is detached.
460 TabStrip* attached_tabstrip_;
462 // The screen that this drag is associated with. Cached, because other UI
463 // elements are NULLd at various points during the lifetime of this object.
464 gfx::Screen* screen_;
466 // The desktop type that this drag is associated with. Cached, because other
467 // UI elements are NULLd at various points during the lifetime of this
468 // object.
469 chrome::HostDesktopType host_desktop_type_;
471 // Whether capture can be released during the drag. When false, capture should
472 // not be released when transferring capture between widgets and when starting
473 // the move loop.
474 bool can_release_capture_;
476 // The position of the mouse (in screen coordinates) at the start of the drag
477 // operation. This is used to calculate minimum elasticity before a
478 // DraggedTabView is constructed.
479 gfx::Point start_point_in_screen_;
481 // This is the offset of the mouse from the top left of the first Tab where
482 // dragging began. This is used to ensure that the dragged view is always
483 // positioned at the correct location during the drag, and to ensure that the
484 // detached window is created at the right location.
485 gfx::Point mouse_offset_;
487 // Ratio of the x-coordinate of the |source_tab_offset| to the width of the
488 // tab.
489 float offset_to_width_ratio_;
491 // A hint to use when positioning new windows created by detaching Tabs. This
492 // is the distance of the mouse from the top left of the dragged tab as if it
493 // were the distance of the mouse from the top left of the first tab in the
494 // attached TabStrip from the top left of the window.
495 gfx::Point window_create_point_;
497 // Location of the first tab in the source tabstrip in screen coordinates.
498 // This is used to calculate |window_create_point_|.
499 gfx::Point first_source_tab_point_;
501 // Storage ID in ViewStorage where the last view that had focus in the window
502 // containing |source_tab_| is saved. This is saved so that focus can be
503 // restored properly when a drag begins and ends within this same window.
504 const int old_focused_view_id_;
506 // The horizontal position of the mouse cursor in screen coordinates at the
507 // time of the last re-order event.
508 int last_move_screen_loc_;
510 // Timer used to bring the window under the cursor to front. If the user
511 // stops moving the mouse for a brief time over a browser window, it is
512 // brought to front.
513 base::OneShotTimer<TabDragController> bring_to_front_timer_;
515 // Timer used to move the stacked tabs. See comment aboue
516 // StartMoveStackedTimerIfNecessary().
517 base::OneShotTimer<TabDragController> move_stacked_timer_;
519 // Did the mouse move enough that we started a drag?
520 bool started_drag_;
522 // Is the drag active?
523 bool active_;
525 DragData drag_data_;
527 // Index of the source tab in |drag_data_|.
528 size_t source_tab_index_;
530 // True until MoveAttached() is first invoked.
531 bool initial_move_;
533 // The selection model before the drag started. See comment above Init() for
534 // details.
535 ui::ListSelectionModel initial_selection_model_;
537 // The selection model of |attached_tabstrip_| before the tabs were attached.
538 ui::ListSelectionModel selection_model_before_attach_;
540 // Initial x-coordinates of the tabs when the drag started. Only used for
541 // touch mode.
542 std::vector<int> initial_tab_positions_;
544 // What should occur during ConinueDragging when a tab is attempted to be
545 // detached.
546 DetachBehavior detach_behavior_;
548 MoveBehavior move_behavior_;
550 // Updated as the mouse is moved when attached. Indicates whether the mouse
551 // has ever moved to the left or right. If the tabs are ever detached this
552 // is set to kMovedMouseRight | kMovedMouseLeft.
553 int mouse_move_direction_;
555 // Last location used in screen coordinates.
556 gfx::Point last_point_in_screen_;
558 // The following are needed when detaching into a browser
559 // (|detach_into_browser_| is true).
561 // See description above getter.
562 bool is_dragging_window_;
564 // True if |attached_tabstrip_| is in a browser specifically created for
565 // the drag.
566 bool is_dragging_new_browser_;
568 // True if |source_tabstrip_| was maximized before the drag.
569 bool was_source_maximized_;
571 // True if |source_tabstrip_| was in immersive fullscreen before the drag.
572 bool was_source_fullscreen_;
574 // True if the initial drag resulted in restoring the window (because it was
575 // maximized).
576 bool did_restore_window_;
578 EndRunLoopBehavior end_run_loop_behavior_;
580 // If true, we're waiting for a move loop to complete.
581 bool waiting_for_run_loop_to_exit_;
583 // The TabStrip to attach to after the move loop completes.
584 TabStrip* tab_strip_to_attach_to_after_exit_;
586 // Non-null for the duration of RunMoveLoop.
587 views::Widget* move_loop_widget_;
589 // See description above getter.
590 bool is_mutating_;
592 // |attach_x_| and |attach_index_| are set to the x-coordinate of the mouse
593 // (in terms of the tabstrip) and the insertion index at the time tabs are
594 // dragged into a new browser (attached). They are used to ensure we don't
595 // shift the tabs around in the wrong direction. The two are only valid if
596 // |attach_index_| is not -1.
597 // See comment around use for more details.
598 int attach_x_;
599 int attach_index_;
601 scoped_ptr<ui::EventHandler> escape_tracker_;
603 base::WeakPtrFactory<TabDragController> weak_factory_;
605 DISALLOW_COPY_AND_ASSIGN(TabDragController);
608 #endif // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_