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 #ifndef CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_ 6 #define CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_ 7 8 #include <gtk/gtk.h> 9 #include <vector> 10 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" 25 26 class BrowserWindowGtk; 27 class CustomDrawButton; 28 class DraggedTabControllerGtk; 29 class GtkThemeService; 30 31 namespace gfx { 32 class Image; 33 } 34 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 { 41 public: 42 class TabAnimation; 43 44 TabStripGtk(TabStripModel* model, BrowserWindowGtk* window); 45 virtual ~TabStripGtk(); 46 47 // Initialize and load the TabStrip into a container. 48 // TODO(tc): Pass in theme provider so we can properly theme the tabs. 49 void Init(); 50 51 void Show(); 52 void Hide(); 53 54 TabStripModel* model() const { return model_; } 55 56 BrowserWindowGtk* window() const { return window_; } 57 58 GtkWidget* widget() const { return tabstrip_.get(); } 59 60 // Returns true if there is an active drag session. 61 bool IsDragSessionActive() const { return drag_controller_.get() != NULL; } 62 63 // Returns true if a tab is being dragged into this tabstrip. 64 bool IsActiveDropTarget() const; 65 66 // Sets the bounds of the tabs. 67 void Layout(); 68 69 // Queues a draw for the tabstrip widget. 70 void SchedulePaint(); 71 72 // Sets the bounds of the tabstrip. 73 void SetBounds(const gfx::Rect& bounds); 74 75 // Returns the bounds of the tabstrip. 76 const gfx::Rect& bounds() const { return bounds_; } 77 78 // Updates loading animations for the TabStrip. 79 void UpdateLoadingAnimations(); 80 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); 84 85 // Returns true if Tabs in this TabStrip are currently changing size or 86 // position. 87 bool IsAnimating() const; 88 89 // Destroys the active drag controller. 90 void DestroyDragController(); 91 92 // Removes |dragged_tab| from this tabstrip, and deletes it. 93 void DestroyDraggedTab(TabGtk* dragged_tab); 94 95 // Retrieve the ideal bounds for the Tab at the specified index. 96 gfx::Rect GetIdealBounds(int index); 97 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); 102 103 // TabstripOriginProvider implementation ------------------------------------- 104 virtual gfx::Point GetTabStripOriginForWidget(GtkWidget* widget) OVERRIDE; 105 106 // ViewIDUtil::Delegate implementation --------------------------------------- 107 virtual GtkWidget* GetWidgetForViewID(ViewID id) OVERRIDE; 108 109 protected: 110 // TabStripModelObserver implementation: 111 virtual void TabInsertedAt(content::WebContents* contents, 112 int index, 113 bool foreground) OVERRIDE; 114 virtual void TabDetachedAt(content::WebContents* contents, 115 int index) OVERRIDE; 116 virtual void TabMoved(content::WebContents* contents, 117 int from_index, 118 int to_index) OVERRIDE; 119 virtual void ActiveTabChanged(content::WebContents* old_contents, 120 content::WebContents* new_contents, 121 int index, 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, 127 int index, 128 TabChangeType change_type) OVERRIDE; 129 virtual void TabReplacedAt(TabStripModel* tab_strip_model, 130 content::WebContents* old_contents, 131 content::WebContents* new_contents, 132 int index) OVERRIDE; 133 virtual void TabMiniStateChanged(content::WebContents* contents, 134 int index) OVERRIDE; 135 virtual void TabBlockedStateChanged(content::WebContents* contents, 136 int index) OVERRIDE; 137 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; 164 165 // MessageLoop::Observer implementation: 166 virtual void WillProcessEvent(GdkEvent* event) OVERRIDE; 167 virtual void DidProcessEvent(GdkEvent* event) OVERRIDE; 168 169 // Overridden from content::NotificationObserver: 170 virtual void Observe(int type, 171 const content::NotificationSource& source, 172 const content::NotificationDetails& details) OVERRIDE; 173 174 // Horizontal gap between mini-tabs and normal tabs. 175 static const int mini_to_non_mini_gap_; 176 177 private: 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; 187 188 struct TabData { 189 TabGtk* tab; 190 gfx::Rect ideal_bounds; 191 }; 192 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. 195 class DropInfo { 196 public: 197 DropInfo(int index, bool drop_before, bool point_down); 198 virtual ~DropInfo(); 199 200 // TODO(jhawkins): Factor out this code into a TransparentContainer class. 201 202 // expose-event handler that redraws the drop indicator. 203 CHROMEGTK_CALLBACK_1(DropInfo, gboolean, OnExposeEvent, GdkEventExpose*); 204 205 // Sets the color map of the container window to allow the window to be 206 // transparent. 207 void SetContainerColorMap(); 208 209 // Sets full transparency for the container window. This is used if 210 // compositing is available for the screen. 211 void SetContainerTransparency(); 212 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 215 // screen. 216 void SetContainerShapeMask(); 217 218 // Creates the container widget. 219 void CreateContainer(); 220 221 // Destroys the container widget. 222 void DestroyContainer(); 223 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. 228 int drop_index; 229 bool drop_before; 230 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. 234 bool point_down; 235 236 // Transparent container window used to render the drop indicator over the 237 // tabstrip and toolbar. 238 GtkWidget* container; 239 240 // The drop indicator image. 241 gfx::Image* drop_arrow; 242 243 private: 244 DISALLOW_COPY_AND_ASSIGN(DropInfo); 245 }; 246 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); 251 252 // expose-event handler that redraws the tabstrip 253 CHROMEGTK_CALLBACK_1(TabStripGtk, gboolean, OnExpose, GdkEventExpose*); 254 255 // size-allocate handler that gets the new bounds of the tabstrip. 256 CHROMEGTK_CALLBACK_1(TabStripGtk, void, OnSizeAllocate, GtkAllocation*); 257 258 // drag-motion handler that is signaled when the user performs a drag in the 259 // tabstrip bounds. 260 CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragMotion, GdkDragContext*, 261 gint, gint, guint); 262 263 // drag-drop handler that is notified when the user finishes a drag. 264 CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragDrop, GdkDragContext*, 265 gint, gint, guint); 266 267 // drag-leave handler that is signaled when the mouse leaves the tabstrip 268 // during a drag. 269 CHROMEGTK_CALLBACK_2(TabStripGtk, gboolean, OnDragLeave, GdkDragContext*, 270 guint); 271 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*, 275 guint, guint); 276 277 // Handles the clicked signal from the new tab button. 278 CHROMEGTK_CALLBACK_0(TabStripGtk, void, OnNewTabClicked); 279 280 // Sets the bounds of the tab and moves the tab widget to those bounds. 281 void SetTabBounds(TabGtk* tab, const gfx::Rect& bounds); 282 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, 287 int num_rects, 288 std::vector<int>* tabs_to_paint); 289 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); 293 294 // Initializes the new tab button. 295 CustomDrawButton* MakeNewTabButton(); 296 297 // Sets the theme specific background on the new tab button. 298 void SetNewTabButtonBackground(); 299 300 // Gets the number of Tabs in the collection. 301 int GetTabCount() const; 302 303 // Returns the number of mini-tabs. 304 int GetMiniTabCount() const; 305 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; 309 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 314 // accordingly. 315 // 316 // Use this instead of GetTabAt if the index comes from the model. 317 TabGtk* GetTabAtAdjustForAnimation(int index) const; 318 319 // Returns the exact (unrounded) current width of each tab. 320 void GetCurrentTabWidths(double* unselected_width, 321 double* selected_width) const; 322 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, 330 int mini_tab_count, 331 double* unselected_width, 332 double* selected_width) const; 333 334 // Returns the horizontal offset before the tab at |tab_index|. 335 int GetTabHOffset(int tab_index); 336 337 // Returns the x-coordinate tabs start from. 338 int tab_start_x() const; 339 340 // Perform an animated resize-relayout of the TabStrip immediately. 341 void ResizeLayoutTabs(); 342 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; 346 347 // Reset the Z-ordering of tabs. 348 void ReStack(); 349 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(); 354 355 // Calculates the available width for tabs, assuming a Tab is to be closed. 356 int GetAvailableWidthForTabs(TabGtk* last_tab) const; 357 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; 362 363 // Cleans up the tab from the TabStrip at the specified |index|. 364 void RemoveTabAt(int index); 365 366 // Called from the message loop observer when a mouse movement has occurred 367 // anywhere over our containing window. 368 void HandleGlobalMouseMoveEvent(); 369 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(); 375 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); 381 382 // -- Link Drag & Drop ------------------------------------------------------ 383 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 386 // it. 387 gfx::Rect GetDropBounds(int drop_index, bool drop_before, bool* is_beneath); 388 389 // Updates the location of the drop based on the event. 390 void UpdateDropIndex(GdkDragContext* context, gint x, gint y); 391 392 // Sets the location of the drop, repainting as necessary. 393 void SetDropIndex(int index, bool drop_before); 394 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 397 // successful. 398 bool CompleteDrop(const guchar* data, bool is_plain_text); 399 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); 403 404 // -- Animations ------------------------------------------------------------- 405 406 // Stops the current animation. 407 void StopAnimation(); 408 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); 412 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, 419 int to_index, 420 const gfx::Rect& start_bounds); 421 void StartResizeLayoutAnimation(); 422 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); 426 427 // The Tabs we contain, and their last generated "good" bounds. 428 std::vector<TabData> tab_data_; 429 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_; 436 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 443 // cursor. 444 int available_width_for_tabs_; 445 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_; 449 450 // The GtkFixed widget. 451 ui::OwnedWidgetGtk tabstrip_; 452 453 // The bounds of the tabstrip. 454 gfx::Rect bounds_; 455 456 // The amount to offset tab backgrounds when we are using an autogenerated 457 // tab background image. 458 int tab_vertical_offset_; 459 460 // Our model. 461 TabStripModel* model_; 462 463 // The BrowserWindowGtk containing this tab strip. 464 BrowserWindowGtk* window_; 465 466 // Theme resources. 467 GtkThemeService* theme_service_; 468 469 // The currently running animation. 470 scoped_ptr<TabAnimation> active_animation_; 471 472 // The New Tab button. 473 scoped_ptr<CustomDrawButton> newtab_button_; 474 475 // The bounds of the bitmap surface used to paint the New Tab button. 476 gfx::Rect newtab_surface_bounds_; 477 478 // Valid for the lifetime of a drag over us. 479 scoped_ptr<DropInfo> drop_info_; 480 481 // The controller for a drag initiated from a Tab. Valid for the lifetime of 482 // the drag session. 483 scoped_ptr<DraggedTabControllerGtk> drag_controller_; 484 485 // A factory that is used to construct a delayed callback to the 486 // ResizeLayoutTabsNow method. 487 base::WeakPtrFactory<TabStripGtk> weak_factory_; 488 489 // A different factory for calls to Layout(). 490 base::WeakPtrFactory<TabStripGtk> layout_factory_; 491 492 // True if the tabstrip has already been added as a MessageLoop observer. 493 bool added_as_message_loop_observer_; 494 495 // Helper for performing tab selection as a result of dragging over a tab. 496 HoverTabSelector hover_tab_selector_; 497 498 content::NotificationRegistrar registrar_; 499 500 DISALLOW_COPY_AND_ASSIGN(TabStripGtk); 501 }; 502 503 #endif // CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_ 504