1 // Copyright (c) 2011 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 #pragma once 8 9 #include <gtk/gtk.h> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/message_loop.h" 14 #include "base/task.h" 15 #include "chrome/browser/tabs/tab_strip_model.h" 16 #include "chrome/browser/ui/gtk/owned_widget_gtk.h" 17 #include "chrome/browser/ui/gtk/tabs/tab_gtk.h" 18 #include "chrome/browser/ui/gtk/tabstrip_origin_provider.h" 19 #include "chrome/browser/ui/gtk/view_id_util.h" 20 #include "content/common/notification_observer.h" 21 #include "ui/base/gtk/gtk_signal.h" 22 #include "ui/gfx/rect.h" 23 24 class BrowserWindowGtk; 25 class CustomDrawButton; 26 class DraggedTabControllerGtk; 27 class GtkThemeService; 28 29 class TabStripGtk : public TabStripModelObserver, 30 public TabGtk::TabDelegate, 31 public MessageLoopForUI::Observer, 32 public NotificationObserver, 33 public TabstripOriginProvider, 34 public ViewIDUtil::Delegate { 35 public: 36 class TabAnimation; 37 38 TabStripGtk(TabStripModel* model, BrowserWindowGtk* window); 39 virtual ~TabStripGtk(); 40 41 // Initialize and load the TabStrip into a container. 42 // TODO(tc): Pass in theme provider so we can properly theme the tabs. 43 void Init(); 44 45 void Show(); 46 void Hide(); 47 model()48 TabStripModel* model() const { return model_; } 49 window()50 BrowserWindowGtk* window() const { return window_; } 51 widget()52 GtkWidget* widget() const { return tabstrip_.get(); } 53 54 // Returns true if there is an active drag session. IsDragSessionActive()55 bool IsDragSessionActive() const { return drag_controller_.get() != NULL; } 56 57 // Returns true if a tab is being dragged into this tabstrip. 58 bool IsActiveDropTarget() const; 59 60 // Sets the bounds of the tabs. 61 void Layout(); 62 63 // Queues a draw for the tabstrip widget. 64 void SchedulePaint(); 65 66 // Sets the bounds of the tabstrip. 67 void SetBounds(const gfx::Rect& bounds); 68 69 // Returns the bounds of the tabstrip. bounds()70 const gfx::Rect& bounds() const { return bounds_; } 71 72 // Updates loading animations for the TabStrip. 73 void UpdateLoadingAnimations(); 74 75 // Return true if this tab strip is compatible with the provided tab strip. 76 // Compatible tab strips can transfer tabs during drag and drop. 77 bool IsCompatibleWith(TabStripGtk* other); 78 79 // Returns true if Tabs in this TabStrip are currently changing size or 80 // position. 81 bool IsAnimating() const; 82 83 // Destroys the active drag controller. 84 void DestroyDragController(); 85 86 // Removes the drag source tab from this tabstrip, and deletes it. 87 void DestroyDraggedSourceTab(TabGtk* tab); 88 89 // Retrieve the ideal bounds for the Tab at the specified index. 90 gfx::Rect GetIdealBounds(int index); 91 92 // Sets the vertical offset that each tab will use to offset against the 93 // background image. Passed in from the titlebar and based on the size of the 94 // alignment that sits above the tabstrip. 95 void SetVerticalOffset(int offset); 96 97 // TabstripOriginProvider implementation ------------------------------------- 98 virtual gfx::Point GetTabStripOriginForWidget(GtkWidget* widget); 99 100 // ViewIDUtil::Delegate implementation --------------------------------------- 101 virtual GtkWidget* GetWidgetForViewID(ViewID id); 102 103 protected: 104 // TabStripModelObserver implementation: 105 virtual void TabInsertedAt(TabContentsWrapper* contents, 106 int index, 107 bool foreground); 108 virtual void TabDetachedAt(TabContentsWrapper* contents, int index); 109 virtual void TabSelectedAt(TabContentsWrapper* old_contents, 110 TabContentsWrapper* contents, 111 int index, 112 bool user_gesture); 113 virtual void TabMoved(TabContentsWrapper* contents, 114 int from_index, 115 int to_index); 116 virtual void TabChangedAt(TabContentsWrapper* contents, int index, 117 TabChangeType change_type); 118 virtual void TabReplacedAt(TabStripModel* tab_strip_model, 119 TabContentsWrapper* old_contents, 120 TabContentsWrapper* new_contents, 121 int index); 122 virtual void TabMiniStateChanged(TabContentsWrapper* contents, int index); 123 virtual void TabBlockedStateChanged(TabContentsWrapper* contents, 124 int index); 125 126 // TabGtk::TabDelegate implementation: 127 virtual bool IsTabSelected(const TabGtk* tab) const; 128 virtual bool IsTabPinned(const TabGtk* tab) const; 129 virtual bool IsTabDetached(const TabGtk* tab) const; 130 virtual void SelectTab(TabGtk* tab); 131 virtual void CloseTab(TabGtk* tab); 132 virtual bool IsCommandEnabledForTab( 133 TabStripModel::ContextMenuCommand command_id, const TabGtk* tab) const; 134 virtual void ExecuteCommandForTab( 135 TabStripModel::ContextMenuCommand command_id, TabGtk* tab); 136 virtual void StartHighlightTabsForCommand( 137 TabStripModel::ContextMenuCommand command_id, TabGtk* tab); 138 virtual void StopHighlightTabsForCommand( 139 TabStripModel::ContextMenuCommand command_id, TabGtk* tab); 140 virtual void StopAllHighlighting(); 141 virtual void MaybeStartDrag(TabGtk* tab, const gfx::Point& point); 142 virtual void ContinueDrag(GdkDragContext* context); 143 virtual bool EndDrag(bool canceled); 144 virtual bool HasAvailableDragActions() const; 145 virtual ui::ThemeProvider* GetThemeProvider(); 146 147 // MessageLoop::Observer implementation: 148 virtual void WillProcessEvent(GdkEvent* event); 149 virtual void DidProcessEvent(GdkEvent* event); 150 151 // Overridden from NotificationObserver: 152 virtual void Observe(NotificationType type, 153 const NotificationSource& source, 154 const NotificationDetails& details); 155 156 // Horizontal gap between mini-tabs and normal tabs. 157 static const int mini_to_non_mini_gap_; 158 159 private: 160 friend class BrowserWindowGtk; 161 friend class DraggedTabControllerGtk; 162 friend class InsertTabAnimation; 163 friend class MiniMoveAnimation; 164 friend class MiniTabAnimation; 165 friend class MoveTabAnimation; 166 friend class RemoveTabAnimation; 167 friend class ResizeLayoutAnimation; 168 friend class TabAnimation; 169 170 struct TabData { 171 TabGtk* tab; 172 gfx::Rect ideal_bounds; 173 }; 174 175 // Used during a drop session of a url. Tracks the position of the drop as 176 // well as a window used to highlight where the drop occurs. 177 class DropInfo { 178 public: 179 DropInfo(int index, bool drop_before, bool point_down); 180 virtual ~DropInfo(); 181 182 // TODO(jhawkins): Factor out this code into a TransparentContainer class. 183 184 // expose-event handler that redraws the drop indicator. 185 CHROMEGTK_CALLBACK_1(DropInfo, gboolean, OnExposeEvent, GdkEventExpose*); 186 187 // Sets the color map of the container window to allow the window to be 188 // transparent. 189 void SetContainerColorMap(); 190 191 // Sets full transparency for the container window. This is used if 192 // compositing is available for the screen. 193 void SetContainerTransparency(); 194 195 // Sets the shape mask for the container window to emulate a transparent 196 // container window. This is used if compositing is not available for the 197 // screen. 198 void SetContainerShapeMask(); 199 200 // Creates the container widget. 201 void CreateContainer(); 202 203 // Destroys the container widget. 204 void DestroyContainer(); 205 206 // Index of the tab to drop on. If drop_before is true, the drop should 207 // occur between the tab at drop_index - 1 and drop_index. 208 // WARNING: if drop_before is true it is possible this will == tab_count, 209 // which indicates the drop should create a new tab at the end of the tabs. 210 int drop_index; 211 bool drop_before; 212 213 // Direction the arrow should point in. If true, the arrow is displayed 214 // above the tab and points down. If false, the arrow is displayed beneath 215 // the tab and points up. 216 bool point_down; 217 218 // Transparent container window used to render the drop indicator over the 219 // tabstrip and toolbar. 220 GtkWidget* container; 221 222 // The drop indicator image. 223 GdkPixbuf* drop_arrow; 224 225 private: 226 DISALLOW_COPY_AND_ASSIGN(DropInfo); 227 }; 228 229 // expose-event handler that redraws the tabstrip 230 CHROMEGTK_CALLBACK_1(TabStripGtk, gboolean, OnExpose, GdkEventExpose*); 231 232 // size-allocate handler that gets the new bounds of the tabstrip. 233 CHROMEGTK_CALLBACK_1(TabStripGtk, void, OnSizeAllocate, GtkAllocation*); 234 235 // drag-motion handler that is signaled when the user performs a drag in the 236 // tabstrip bounds. 237 CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragMotion, GdkDragContext*, 238 gint, gint, guint); 239 240 // drag-drop handler that is notified when the user finishes a drag. 241 CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragDrop, GdkDragContext*, 242 gint, gint, guint); 243 244 // drag-leave handler that is signaled when the mouse leaves the tabstrip 245 // during a drag. 246 CHROMEGTK_CALLBACK_2(TabStripGtk, gboolean, OnDragLeave, GdkDragContext*, 247 guint); 248 249 // drag-data-received handler that receives the data associated with the drag. 250 CHROMEGTK_CALLBACK_6(TabStripGtk, gboolean, OnDragDataReceived, 251 GdkDragContext*, gint, gint, GtkSelectionData*, 252 guint, guint); 253 254 // Handles the clicked signal from the new tab button. 255 CHROMEGTK_CALLBACK_0(TabStripGtk, void, OnNewTabClicked); 256 257 // Sets the bounds of the tab and moves the tab widget to those bounds. 258 void SetTabBounds(TabGtk* tab, const gfx::Rect& bounds); 259 260 // Returns true if |rects| are all areas that match up with tab favicons. 261 // |rects| must be sorted from left to right. |tabs_to_paint| are the tab 262 // positions that match the rects. 263 bool CanPaintOnlyFavicons(const GdkRectangle* rects, 264 int num_rects, 265 std::vector<int>* tabs_to_paint); 266 267 // Paints the tab favicon areas for tabs in |tabs_to_paint|. 268 void PaintOnlyFavicons(GdkEventExpose* event, 269 const std::vector<int>& tabs_to_paint); 270 271 // Initializes the new tab button. 272 CustomDrawButton* MakeNewTabButton(); 273 274 // Gets the number of Tabs in the collection. 275 int GetTabCount() const; 276 277 // Returns the number of mini-tabs. 278 int GetMiniTabCount() const; 279 280 // Retrieves the Tab at the specified index. Take care in using this, you may 281 // need to use GetTabAtAdjustForAnimation. 282 TabGtk* GetTabAt(int index) const; 283 284 // Returns the tab at the specified index. If a remove animation is on going 285 // and the index is >= the index of the tab being removed, the index is 286 // incremented. While a remove operation is on going the indices of the model 287 // do not line up with the indices of the view. This method adjusts the index 288 // accordingly. 289 // 290 // Use this instead of GetTabAt if the index comes from the model. 291 TabGtk* GetTabAtAdjustForAnimation(int index) const; 292 293 // Returns the exact (unrounded) current width of each tab. 294 void GetCurrentTabWidths(double* unselected_width, 295 double* selected_width) const; 296 297 // Returns the exact (unrounded) desired width of each tab, based on the 298 // desired strip width and number of tabs. If 299 // |width_of_tabs_for_mouse_close_| is nonnegative we use that value in 300 // calculating the desired strip width; otherwise we use the current width. 301 // |mini_tab_count| gives the number of mini-tabs, and |tab_count| the 302 // number of mini and non-mini-tabs. 303 void GetDesiredTabWidths(int tab_count, 304 int mini_tab_count, 305 double* unselected_width, 306 double* selected_width) const; 307 308 // Returns the horizontal offset before the tab at |tab_index|. 309 int GetTabHOffset(int tab_index); 310 311 // Returns the x-coordinate tabs start from. 312 int tab_start_x() const; 313 314 // Perform an animated resize-relayout of the TabStrip immediately. The 315 // value returned indicates whether a resize actually took place. 316 bool ResizeLayoutTabs(); 317 318 // Returns whether or not the cursor is currently in the "tab strip zone" 319 // which is defined as the region above the TabStrip and a bit below it. 320 bool IsCursorInTabStripZone() const; 321 322 // Ensure that the message loop observer used for event spying is added and 323 // removed appropriately so we can tell when to resize layout the tab strip. 324 void AddMessageLoopObserver(); 325 void RemoveMessageLoopObserver(); 326 327 // Calculates the available width for tabs, assuming a Tab is to be closed. 328 int GetAvailableWidthForTabs(TabGtk* last_tab) const; 329 330 // Finds the index of the TabContents corresponding to |tab| in our 331 // associated TabStripModel, or -1 if there is none (e.g. the specified |tab| 332 // is being animated closed). 333 int GetIndexOfTab(const TabGtk* tab) const; 334 335 // Cleans up the tab from the TabStrip at the specified |index|. 336 void RemoveTabAt(int index); 337 338 // Called from the message loop observer when a mouse movement has occurred 339 // anywhere over our containing window. 340 void HandleGlobalMouseMoveEvent(); 341 342 // Generates the ideal bounds of the TabStrip when all Tabs have finished 343 // animating to their desired position/bounds. This is used by the standard 344 // Layout method and other callers like the DraggedTabController that need 345 // stable representations of Tab positions. 346 void GenerateIdealBounds(); 347 348 // Lays out the New Tab button, assuming the right edge of the last Tab on 349 // the TabStrip at |last_tab_right|. |unselected_width| is the width of 350 // unselected tabs at the moment this function is called. The value changes 351 // during animations, so we can't use current_unselected_width_. 352 void LayoutNewTabButton(double last_tab_right, double unselected_width); 353 354 // -- Link Drag & Drop ------------------------------------------------------ 355 356 // Returns the bounds to render the drop at, in screen coordinates. Sets 357 // |is_beneath| to indicate whether the arrow is beneath the tab, or above 358 // it. 359 gfx::Rect GetDropBounds(int drop_index, bool drop_before, bool* is_beneath); 360 361 // Updates the location of the drop based on the event. 362 void UpdateDropIndex(GdkDragContext* context, gint x, gint y); 363 364 // Sets the location of the drop, repainting as necessary. 365 void SetDropIndex(int index, bool drop_before); 366 367 // Determines whether the data is acceptable by the tabstrip and opens a new 368 // tab with the data as URL if it is. Returns true if the drop was 369 // successful. 370 bool CompleteDrop(guchar* data, bool is_plain_text); 371 372 // Returns the image to use for indicating a drop on a tab. If is_down is 373 // true, this returns an arrow pointing down. 374 static GdkPixbuf* GetDropArrowImage(bool is_down); 375 376 // -- Animations ------------------------------------------------------------- 377 378 // Stops the current animation. 379 void StopAnimation(); 380 381 // A generic Layout method for various classes of TabStrip animations, 382 // including Insert, Remove and Resize Layout cases. 383 void AnimationLayout(double unselected_width); 384 385 // Starts various types of TabStrip animations. 386 void StartInsertTabAnimation(int index); 387 void StartRemoveTabAnimation(int index, TabContents* contents); 388 void StartMoveTabAnimation(int from_index, int to_index); 389 void StartMiniTabAnimation(int index); 390 void StartMiniMoveTabAnimation(int from_index, 391 int to_index, 392 const gfx::Rect& start_bounds); 393 void StartResizeLayoutAnimation(); 394 395 // Notifies the TabStrip that the specified TabAnimation has completed. 396 // Optionally a full Layout will be performed, specified by |layout|. 397 void FinishAnimation(TabAnimation* animation, bool layout); 398 399 NotificationRegistrar registrar_; 400 401 // The Tabs we contain, and their last generated "good" bounds. 402 std::vector<TabData> tab_data_; 403 404 // The current widths of various types of tabs. We save these so that, as 405 // users close tabs while we're holding them at the same size, we can lay out 406 // tabs exactly and eliminate the "pixel jitter" we'd get from just leaving 407 // them all at their existing, rounded widths. 408 double current_unselected_width_; 409 double current_selected_width_; 410 411 // If this value is nonnegative, it is used in GetDesiredTabWidths() to 412 // calculate how much space in the tab strip to use for tabs. Most of the 413 // time this will be -1, but while we're handling closing a tab via the mouse, 414 // we'll set this to the edge of the last tab before closing, so that if we 415 // are closing the last tab and need to resize immediately, we'll resize only 416 // back to this width, thus once again placing the last tab under the mouse 417 // cursor. 418 int available_width_for_tabs_; 419 420 // True if a resize layout animation should be run a short delay after the 421 // mouse exits the TabStrip. 422 bool needs_resize_layout_; 423 424 // The GtkFixed widget. 425 OwnedWidgetGtk tabstrip_; 426 427 // The bounds of the tabstrip. 428 gfx::Rect bounds_; 429 430 // The amount to offset tab backgrounds when we are using an autogenerated 431 // tab background image. 432 int tab_vertical_offset_; 433 434 // Our model. 435 TabStripModel* model_; 436 437 // The BrowserWindowGtk containing this tab strip. 438 BrowserWindowGtk* window_; 439 440 // Theme resources. 441 GtkThemeService* theme_service_; 442 443 // The currently running animation. 444 scoped_ptr<TabAnimation> active_animation_; 445 446 // The New Tab button. 447 scoped_ptr<CustomDrawButton> newtab_button_; 448 449 // Valid for the lifetime of a drag over us. 450 scoped_ptr<DropInfo> drop_info_; 451 452 // The controller for a drag initiated from a Tab. Valid for the lifetime of 453 // the drag session. 454 scoped_ptr<DraggedTabControllerGtk> drag_controller_; 455 456 // A factory that is used to construct a delayed callback to the 457 // ResizeLayoutTabsNow method. 458 ScopedRunnableMethodFactory<TabStripGtk> resize_layout_factory_; 459 460 // True if the tabstrip has already been added as a MessageLoop observer. 461 bool added_as_message_loop_observer_; 462 463 DISALLOW_COPY_AND_ASSIGN(TabStripGtk); 464 }; 465 466 #endif // CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_ 467