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_GTK_TABS_TAB_STRIP_GTK_H_
6 #define CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
11 #include "base/basictypes.h"
12 #include "base/compiler_specific.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "chrome/browser/ui/gtk/tabs/tab_gtk.h"
16 #include "chrome/browser/ui/gtk/tabstrip_origin_provider.h"
17 #include "chrome/browser/ui/gtk/view_id_util.h"
18 #include "chrome/browser/ui/tabs/hover_tab_selector.h"
19 #include "chrome/browser/ui/tabs/tab_strip_model.h"
20 #include "content/public/browser/notification_observer.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "ui/base/gtk/gtk_signal.h"
23 #include "ui/base/gtk/owned_widget_gtk.h"
24 #include "ui/gfx/rect.h"
26 class BrowserWindowGtk
;
27 class CustomDrawButton
;
28 class DraggedTabControllerGtk
;
29 class GtkThemeService
;
35 class TabStripGtk
: public TabStripModelObserver
,
36 public TabGtk::TabDelegate
,
37 public base::MessageLoopForUI::Observer
,
38 public content::NotificationObserver
,
39 public TabstripOriginProvider
,
40 public ViewIDUtil::Delegate
{
44 TabStripGtk(TabStripModel
* model
, BrowserWindowGtk
* window
);
45 virtual ~TabStripGtk();
47 // Initialize and load the TabStrip into a container.
48 // TODO(tc): Pass in theme provider so we can properly theme the tabs.
54 TabStripModel
* model() const { return model_
; }
56 BrowserWindowGtk
* window() const { return window_
; }
58 GtkWidget
* widget() const { return tabstrip_
.get(); }
60 // Returns true if there is an active drag session.
61 bool IsDragSessionActive() const { return drag_controller_
.get() != NULL
; }
63 // Returns true if a tab is being dragged into this tabstrip.
64 bool IsActiveDropTarget() const;
66 // Sets the bounds of the tabs.
69 // Queues a draw for the tabstrip widget.
72 // Sets the bounds of the tabstrip.
73 void SetBounds(const gfx::Rect
& bounds
);
75 // Returns the bounds of the tabstrip.
76 const gfx::Rect
& bounds() const { return bounds_
; }
78 // Updates loading animations for the TabStrip.
79 void UpdateLoadingAnimations();
81 // Return true if this tab strip is compatible with the provided tab strip.
82 // Compatible tab strips can transfer tabs during drag and drop.
83 bool IsCompatibleWith(TabStripGtk
* other
);
85 // Returns true if Tabs in this TabStrip are currently changing size or
87 bool IsAnimating() const;
89 // Destroys the active drag controller.
90 void DestroyDragController();
92 // Removes |dragged_tab| from this tabstrip, and deletes it.
93 void DestroyDraggedTab(TabGtk
* dragged_tab
);
95 // Retrieve the ideal bounds for the Tab at the specified index.
96 gfx::Rect
GetIdealBounds(int index
);
98 // Sets the vertical offset that each tab will use to offset against the
99 // background image. Passed in from the titlebar and based on the size of the
100 // alignment that sits above the tabstrip.
101 void SetVerticalOffset(int offset
);
103 // TabstripOriginProvider implementation -------------------------------------
104 virtual gfx::Point
GetTabStripOriginForWidget(GtkWidget
* widget
) OVERRIDE
;
106 // ViewIDUtil::Delegate implementation ---------------------------------------
107 virtual GtkWidget
* GetWidgetForViewID(ViewID id
) OVERRIDE
;
110 // TabStripModelObserver implementation:
111 virtual void TabInsertedAt(content::WebContents
* contents
,
113 bool foreground
) OVERRIDE
;
114 virtual void TabDetachedAt(content::WebContents
* contents
,
116 virtual void TabMoved(content::WebContents
* contents
,
118 int to_index
) OVERRIDE
;
119 virtual void ActiveTabChanged(content::WebContents
* old_contents
,
120 content::WebContents
* new_contents
,
122 int reason
) OVERRIDE
;
123 virtual void TabSelectionChanged(
124 TabStripModel
* tab_strip_model
,
125 const ui::ListSelectionModel
& old_model
) OVERRIDE
;
126 virtual void TabChangedAt(content::WebContents
* contents
,
128 TabChangeType change_type
) OVERRIDE
;
129 virtual void TabReplacedAt(TabStripModel
* tab_strip_model
,
130 content::WebContents
* old_contents
,
131 content::WebContents
* new_contents
,
133 virtual void TabMiniStateChanged(content::WebContents
* contents
,
135 virtual void TabBlockedStateChanged(content::WebContents
* contents
,
138 // TabGtk::TabDelegate implementation:
139 virtual bool IsTabActive(const TabGtk
* tab
) const OVERRIDE
;
140 virtual bool IsTabSelected(const TabGtk
* tab
) const OVERRIDE
;
141 virtual bool IsTabPinned(const TabGtk
* tab
) const OVERRIDE
;
142 virtual bool IsTabDetached(const TabGtk
* tab
) const OVERRIDE
;
143 virtual void ActivateTab(TabGtk
* tab
) OVERRIDE
;
144 virtual void ToggleTabSelection(TabGtk
* tab
) OVERRIDE
;
145 virtual void ExtendTabSelection(TabGtk
* tab
) OVERRIDE
;
146 virtual void CloseTab(TabGtk
* tab
) OVERRIDE
;
147 virtual bool IsCommandEnabledForTab(
148 TabStripModel::ContextMenuCommand command_id
,
149 const TabGtk
* tab
) const OVERRIDE
;
150 virtual void ExecuteCommandForTab(
151 TabStripModel::ContextMenuCommand command_id
, TabGtk
* tab
) OVERRIDE
;
152 virtual void StartHighlightTabsForCommand(
153 TabStripModel::ContextMenuCommand command_id
, TabGtk
* tab
) OVERRIDE
;
154 virtual void StopHighlightTabsForCommand(
155 TabStripModel::ContextMenuCommand command_id
, TabGtk
* tab
) OVERRIDE
;
156 virtual void StopAllHighlighting() OVERRIDE
;
157 virtual void MaybeStartDrag(TabGtk
* tab
, const gfx::Point
& point
) OVERRIDE
;
158 virtual void ContinueDrag(GdkDragContext
* context
) OVERRIDE
;
159 virtual bool EndDrag(bool canceled
) OVERRIDE
;
160 virtual bool HasAvailableDragActions() const OVERRIDE
;
161 virtual GtkThemeService
* GetThemeProvider() OVERRIDE
;
162 virtual TabStripMenuController
* GetTabStripMenuControllerForTab(
163 TabGtk
* tab
) OVERRIDE
;
165 // MessageLoop::Observer implementation:
166 virtual void WillProcessEvent(GdkEvent
* event
) OVERRIDE
;
167 virtual void DidProcessEvent(GdkEvent
* event
) OVERRIDE
;
169 // Overridden from content::NotificationObserver:
170 virtual void Observe(int type
,
171 const content::NotificationSource
& source
,
172 const content::NotificationDetails
& details
) OVERRIDE
;
174 // Horizontal gap between mini-tabs and normal tabs.
175 static const int mini_to_non_mini_gap_
;
178 friend class BrowserWindowGtk
;
179 friend class DraggedTabControllerGtk
;
180 friend class InsertTabAnimation
;
181 friend class MiniMoveAnimation
;
182 friend class MiniTabAnimation
;
183 friend class MoveTabAnimation
;
184 friend class RemoveTabAnimation
;
185 friend class ResizeLayoutAnimation
;
186 friend class TabAnimation
;
190 gfx::Rect ideal_bounds
;
193 // Used during a drop session of a url. Tracks the position of the drop as
194 // well as a window used to highlight where the drop occurs.
197 DropInfo(int index
, bool drop_before
, bool point_down
);
200 // TODO(jhawkins): Factor out this code into a TransparentContainer class.
202 // expose-event handler that redraws the drop indicator.
203 CHROMEGTK_CALLBACK_1(DropInfo
, gboolean
, OnExposeEvent
, GdkEventExpose
*);
205 // Sets the color map of the container window to allow the window to be
207 void SetContainerColorMap();
209 // Sets full transparency for the container window. This is used if
210 // compositing is available for the screen.
211 void SetContainerTransparency();
213 // Sets the shape mask for the container window to emulate a transparent
214 // container window. This is used if compositing is not available for the
216 void SetContainerShapeMask();
218 // Creates the container widget.
219 void CreateContainer();
221 // Destroys the container widget.
222 void DestroyContainer();
224 // Index of the tab to drop on. If drop_before is true, the drop should
225 // occur between the tab at drop_index - 1 and drop_index.
226 // WARNING: if drop_before is true it is possible this will == tab_count,
227 // which indicates the drop should create a new tab at the end of the tabs.
231 // Direction the arrow should point in. If true, the arrow is displayed
232 // above the tab and points down. If false, the arrow is displayed beneath
233 // the tab and points up.
236 // Transparent container window used to render the drop indicator over the
237 // tabstrip and toolbar.
238 GtkWidget
* container
;
240 // The drop indicator image.
241 gfx::Image
* drop_arrow
;
244 DISALLOW_COPY_AND_ASSIGN(DropInfo
);
247 // Map signal handler that sets initial z-ordering. The widgets need to be
248 // realized before we can set the stacking. We use the "map" signal since the
249 // "realize" signal is called before the child widgets get realized.
250 CHROMEGTK_CALLBACK_0(TabStripGtk
, void, OnMap
);
252 // expose-event handler that redraws the tabstrip
253 CHROMEGTK_CALLBACK_1(TabStripGtk
, gboolean
, OnExpose
, GdkEventExpose
*);
255 // size-allocate handler that gets the new bounds of the tabstrip.
256 CHROMEGTK_CALLBACK_1(TabStripGtk
, void, OnSizeAllocate
, GtkAllocation
*);
258 // drag-motion handler that is signaled when the user performs a drag in the
260 CHROMEGTK_CALLBACK_4(TabStripGtk
, gboolean
, OnDragMotion
, GdkDragContext
*,
263 // drag-drop handler that is notified when the user finishes a drag.
264 CHROMEGTK_CALLBACK_4(TabStripGtk
, gboolean
, OnDragDrop
, GdkDragContext
*,
267 // drag-leave handler that is signaled when the mouse leaves the tabstrip
269 CHROMEGTK_CALLBACK_2(TabStripGtk
, gboolean
, OnDragLeave
, GdkDragContext
*,
272 // drag-data-received handler that receives the data associated with the drag.
273 CHROMEGTK_CALLBACK_6(TabStripGtk
, gboolean
, OnDragDataReceived
,
274 GdkDragContext
*, gint
, gint
, GtkSelectionData
*,
277 // Handles the clicked signal from the new tab button.
278 CHROMEGTK_CALLBACK_0(TabStripGtk
, void, OnNewTabClicked
);
280 // Sets the bounds of the tab and moves the tab widget to those bounds.
281 void SetTabBounds(TabGtk
* tab
, const gfx::Rect
& bounds
);
283 // Returns true if |rects| are all areas that match up with tab favicons.
284 // |rects| must be sorted from left to right. |tabs_to_paint| are the tab
285 // positions that match the rects.
286 bool CanPaintOnlyFavicons(const GdkRectangle
* rects
,
288 std::vector
<int>* tabs_to_paint
);
290 // Paints the tab favicon areas for tabs in |tabs_to_paint|.
291 void PaintOnlyFavicons(GdkEventExpose
* event
,
292 const std::vector
<int>& tabs_to_paint
);
294 // Initializes the new tab button.
295 CustomDrawButton
* MakeNewTabButton();
297 // Sets the theme specific background on the new tab button.
298 void SetNewTabButtonBackground();
300 // Gets the number of Tabs in the collection.
301 int GetTabCount() const;
303 // Returns the number of mini-tabs.
304 int GetMiniTabCount() const;
306 // Retrieves the Tab at the specified index. Take care in using this, you may
307 // need to use GetTabAtAdjustForAnimation.
308 TabGtk
* GetTabAt(int index
) const;
310 // Returns the tab at the specified index. If a remove animation is on going
311 // and the index is >= the index of the tab being removed, the index is
312 // incremented. While a remove operation is on going the indices of the model
313 // do not line up with the indices of the view. This method adjusts the index
316 // Use this instead of GetTabAt if the index comes from the model.
317 TabGtk
* GetTabAtAdjustForAnimation(int index
) const;
319 // Returns the exact (unrounded) current width of each tab.
320 void GetCurrentTabWidths(double* unselected_width
,
321 double* selected_width
) const;
323 // Returns the exact (unrounded) desired width of each tab, based on the
324 // desired strip width and number of tabs. If
325 // |width_of_tabs_for_mouse_close_| is nonnegative we use that value in
326 // calculating the desired strip width; otherwise we use the current width.
327 // |mini_tab_count| gives the number of mini-tabs, and |tab_count| the
328 // number of mini and non-mini-tabs.
329 void GetDesiredTabWidths(int tab_count
,
331 double* unselected_width
,
332 double* selected_width
) const;
334 // Returns the horizontal offset before the tab at |tab_index|.
335 int GetTabHOffset(int tab_index
);
337 // Returns the x-coordinate tabs start from.
338 int tab_start_x() const;
340 // Perform an animated resize-relayout of the TabStrip immediately.
341 void ResizeLayoutTabs();
343 // Returns whether or not the cursor is currently in the "tab strip zone"
344 // which is defined as the region above the TabStrip and a bit below it.
345 bool IsCursorInTabStripZone() const;
347 // Reset the Z-ordering of tabs.
350 // Ensure that the message loop observer used for event spying is added and
351 // removed appropriately so we can tell when to resize layout the tab strip.
352 void AddMessageLoopObserver();
353 void RemoveMessageLoopObserver();
355 // Calculates the available width for tabs, assuming a Tab is to be closed.
356 int GetAvailableWidthForTabs(TabGtk
* last_tab
) const;
358 // Finds the index of the WebContents corresponding to |tab| in our
359 // associated TabStripModel, or -1 if there is none (e.g. the specified |tab|
360 // is being animated closed).
361 int GetIndexOfTab(const TabGtk
* tab
) const;
363 // Cleans up the tab from the TabStrip at the specified |index|.
364 void RemoveTabAt(int index
);
366 // Called from the message loop observer when a mouse movement has occurred
367 // anywhere over our containing window.
368 void HandleGlobalMouseMoveEvent();
370 // Generates the ideal bounds of the TabStrip when all Tabs have finished
371 // animating to their desired position/bounds. This is used by the standard
372 // Layout method and other callers like the DraggedTabController that need
373 // stable representations of Tab positions.
374 void GenerateIdealBounds();
376 // Lays out the New Tab button, assuming the right edge of the last Tab on
377 // the TabStrip at |last_tab_right|. |unselected_width| is the width of
378 // unselected tabs at the moment this function is called. The value changes
379 // during animations, so we can't use current_unselected_width_.
380 void LayoutNewTabButton(double last_tab_right
, double unselected_width
);
382 // -- Link Drag & Drop ------------------------------------------------------
384 // Returns the bounds to render the drop at, in screen coordinates. Sets
385 // |is_beneath| to indicate whether the arrow is beneath the tab, or above
387 gfx::Rect
GetDropBounds(int drop_index
, bool drop_before
, bool* is_beneath
);
389 // Updates the location of the drop based on the event.
390 void UpdateDropIndex(GdkDragContext
* context
, gint x
, gint y
);
392 // Sets the location of the drop, repainting as necessary.
393 void SetDropIndex(int index
, bool drop_before
);
395 // Determines whether the data is acceptable by the tabstrip and opens a new
396 // tab with the data as URL if it is. Returns true if the drop was
398 bool CompleteDrop(const guchar
* data
, bool is_plain_text
);
400 // Returns the image to use for indicating a drop on a tab. If is_down is
401 // true, this returns an arrow pointing down.
402 static gfx::Image
* GetDropArrowImage(bool is_down
);
404 // -- Animations -------------------------------------------------------------
406 // Stops the current animation.
407 void StopAnimation();
409 // A generic Layout method for various classes of TabStrip animations,
410 // including Insert, Remove and Resize Layout cases.
411 void AnimationLayout(double unselected_width
);
413 // Starts various types of TabStrip animations.
414 void StartInsertTabAnimation(int index
);
415 void StartRemoveTabAnimation(int index
, content::WebContents
* contents
);
416 void StartMoveTabAnimation(int from_index
, int to_index
);
417 void StartMiniTabAnimation(int index
);
418 void StartMiniMoveTabAnimation(int from_index
,
420 const gfx::Rect
& start_bounds
);
421 void StartResizeLayoutAnimation();
423 // Notifies the TabStrip that the specified TabAnimation has completed.
424 // Optionally a full Layout will be performed, specified by |layout|.
425 void FinishAnimation(TabAnimation
* animation
, bool layout
);
427 // The Tabs we contain, and their last generated "good" bounds.
428 std::vector
<TabData
> tab_data_
;
430 // The current widths of various types of tabs. We save these so that, as
431 // users close tabs while we're holding them at the same size, we can lay out
432 // tabs exactly and eliminate the "pixel jitter" we'd get from just leaving
433 // them all at their existing, rounded widths.
434 double current_unselected_width_
;
435 double current_selected_width_
;
437 // If this value is nonnegative, it is used in GetDesiredTabWidths() to
438 // calculate how much space in the tab strip to use for tabs. Most of the
439 // time this will be -1, but while we're handling closing a tab via the mouse,
440 // we'll set this to the edge of the last tab before closing, so that if we
441 // are closing the last tab and need to resize immediately, we'll resize only
442 // back to this width, thus once again placing the last tab under the mouse
444 int available_width_for_tabs_
;
446 // True if a resize layout animation should be run a short delay after the
447 // mouse exits the TabStrip.
448 bool needs_resize_layout_
;
450 // The GtkFixed widget.
451 ui::OwnedWidgetGtk tabstrip_
;
453 // The bounds of the tabstrip.
456 // The amount to offset tab backgrounds when we are using an autogenerated
457 // tab background image.
458 int tab_vertical_offset_
;
461 TabStripModel
* model_
;
463 // The BrowserWindowGtk containing this tab strip.
464 BrowserWindowGtk
* window_
;
467 GtkThemeService
* theme_service_
;
469 // The currently running animation.
470 scoped_ptr
<TabAnimation
> active_animation_
;
472 // The New Tab button.
473 scoped_ptr
<CustomDrawButton
> newtab_button_
;
475 // The bounds of the bitmap surface used to paint the New Tab button.
476 gfx::Rect newtab_surface_bounds_
;
478 // Valid for the lifetime of a drag over us.
479 scoped_ptr
<DropInfo
> drop_info_
;
481 // The controller for a drag initiated from a Tab. Valid for the lifetime of
483 scoped_ptr
<DraggedTabControllerGtk
> drag_controller_
;
485 // True if the tabstrip has already been added as a MessageLoop observer.
486 bool added_as_message_loop_observer_
;
488 // Helper for performing tab selection as a result of dragging over a tab.
489 HoverTabSelector hover_tab_selector_
;
491 content::NotificationRegistrar registrar_
;
493 // A factory that is used to construct a delayed callback to the
494 // ResizeLayoutTabsNow method.
495 base::WeakPtrFactory
<TabStripGtk
> weak_factory_
;
497 // A different factory for calls to Layout().
498 base::WeakPtrFactory
<TabStripGtk
> layout_factory_
;
500 DISALLOW_COPY_AND_ASSIGN(TabStripGtk
);
503 #endif // CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_