summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-05 20:27:00 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-05 20:27:00 +0000
commitc12fac94defb54d0a16ca2e1a27fc4023a1acba7 (patch)
treeec653aa7163c77d08d6333a5e83d0a9c7dce12c4
parent6cf2af2efbde465c5ce6c90bd141056ed78ba335 (diff)
downloadchromium_src-c12fac94defb54d0a16ca2e1a27fc4023a1acba7.zip
chromium_src-c12fac94defb54d0a16ca2e1a27fc4023a1acba7.tar.gz
chromium_src-c12fac94defb54d0a16ca2e1a27fc4023a1acba7.tar.bz2
Implement Linux tabs as widgets. This moves tab input handling into the correct object. This change also adds an initial DraggedTabControllerGtk.
Review URL: http://codereview.chromium.org/99371 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15330 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/gtk/browser_window_gtk.cc2
-rw-r--r--chrome/browser/gtk/custom_button.h10
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc368
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h177
-rw-r--r--chrome/browser/gtk/tabs/tab_gtk.cc204
-rw-r--r--chrome/browser/gtk/tabs/tab_gtk.h77
-rw-r--r--chrome/browser/gtk/tabs/tab_renderer_gtk.cc113
-rw-r--r--chrome/browser/gtk/tabs/tab_renderer_gtk.h37
-rw-r--r--chrome/browser/gtk/tabs/tab_strip_gtk.cc602
-rw-r--r--chrome/browser/gtk/tabs/tab_strip_gtk.h132
-rw-r--r--chrome/chrome.gyp2
11 files changed, 980 insertions, 744 deletions
diff --git a/chrome/browser/gtk/browser_window_gtk.cc b/chrome/browser/gtk/browser_window_gtk.cc
index f8887a0..61bc7d8 100644
--- a/chrome/browser/gtk/browser_window_gtk.cc
+++ b/chrome/browser/gtk/browser_window_gtk.cc
@@ -227,7 +227,7 @@ BrowserWindowGtk::BrowserWindowGtk(Browser* browser)
window_vbox_ = gtk_vbox_new(FALSE, 0);
tabstrip_.reset(new TabStripGtk(browser_->tabstrip_model()));
- tabstrip_->Init();
+ tabstrip_->Init(bounds_.width());
tabstrip_->AddTabStripToBox(window_vbox_);
// This vbox surrounds the "content": toolbar+page.
diff --git a/chrome/browser/gtk/custom_button.h b/chrome/browser/gtk/custom_button.h
index aea7717..f5e21ce 100644
--- a/chrome/browser/gtk/custom_button.h
+++ b/chrome/browser/gtk/custom_button.h
@@ -9,6 +9,7 @@
#include <string>
+#include "base/gfx/rect.h"
#include "base/scoped_ptr.h"
#include "chrome/common/owned_widget_gtk.h"
@@ -55,6 +56,15 @@ class CustomDrawButton {
GtkWidget* widget() const { return widget_.get(); }
+ gfx::Rect bounds() const {
+ return gfx::Rect(widget_.get()->allocation.x,
+ widget_.get()->allocation.y,
+ widget_.get()->allocation.width,
+ widget_.get()->allocation.height);
+ }
+
+ int width() const { return widget_.get()->allocation.width; }
+
// This is a convenience function for creating a widget that closes
// a bar (find bar, download shelf, info bars). The button will be packed in
// |hbox|.
diff --git a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc
new file mode 100644
index 0000000..3650b27
--- /dev/null
+++ b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc
@@ -0,0 +1,368 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h"
+
+#include "chrome/browser/gtk/tabs/tab_strip_gtk.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/notification_service.h"
+
+namespace {
+
+// Used to determine how far a tab must obscure another tab in order to swap
+// their indexes.
+const int kHorizontalMoveThreshold = 16; // pixels
+
+} // namespace
+
+DraggedTabControllerGtk::DraggedTabControllerGtk(TabGtk* source_tab,
+ TabStripGtk* source_tabstrip)
+ : dragged_contents_(NULL),
+ original_delegate_(NULL),
+ source_tab_(source_tab),
+ source_tabstrip_(source_tabstrip),
+ source_model_index_(source_tabstrip->GetIndexOfTab(source_tab)),
+ attached_tabstrip_(source_tabstrip),
+ last_move_screen_x_(0) {
+ SetDraggedContents(
+ source_tabstrip_->model()->GetTabContentsAt(source_model_index_));
+}
+
+DraggedTabControllerGtk::~DraggedTabControllerGtk() {
+}
+
+void DraggedTabControllerGtk::CaptureDragInfo(const gfx::Point& mouse_offset) {
+ start_screen_point_ = GetCursorScreenPoint();
+ mouse_offset_ = mouse_offset;
+ snap_bounds_ = source_tab_->bounds();
+}
+
+void DraggedTabControllerGtk::Drag() {
+ ContinueDragging();
+}
+
+bool DraggedTabControllerGtk::EndDrag(bool canceled) {
+ return EndDragImpl(canceled ? CANCELED : NORMAL);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DraggedTabControllerGtk, TabContentsDelegate implementation:
+
+void DraggedTabControllerGtk::OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ if (original_delegate_) {
+ if (disposition == CURRENT_TAB)
+ disposition = NEW_WINDOW;
+
+ original_delegate_->OpenURLFromTab(source, url, referrer,
+ disposition, transition);
+ }
+}
+
+void DraggedTabControllerGtk::NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags) {
+}
+
+void DraggedTabControllerGtk::AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ DCHECK(disposition != CURRENT_TAB);
+
+ // Theoretically could be called while dragging if the page tries to
+ // spawn a window. Route this message back to the browser in most cases.
+ if (original_delegate_) {
+ original_delegate_->AddNewContents(source, new_contents, disposition,
+ initial_pos, user_gesture);
+ }
+}
+
+void DraggedTabControllerGtk::ActivateContents(TabContents* contents) {
+ // Ignored.
+}
+
+void DraggedTabControllerGtk::LoadingStateChanged(TabContents* source) {
+}
+
+void DraggedTabControllerGtk::CloseContents(TabContents* source) {
+ // Theoretically could be called by a window. Should be ignored
+ // because window.close() is ignored (usually, even though this
+ // method gets called.)
+}
+
+void DraggedTabControllerGtk::MoveContents(TabContents* source,
+ const gfx::Rect& pos) {
+ // Theoretically could be called by a web page trying to move its
+ // own window. Should be ignored since we're moving the window...
+}
+
+bool DraggedTabControllerGtk::IsPopup(TabContents* source) {
+ return false;
+}
+
+void DraggedTabControllerGtk::ToolbarSizeChanged(TabContents* source,
+ bool finished) {
+ // Dragged tabs don't care about this.
+}
+
+void DraggedTabControllerGtk::URLStarredChanged(TabContents* source,
+ bool starred) {
+ // Ignored.
+}
+
+void DraggedTabControllerGtk::UpdateTargetURL(TabContents* source,
+ const GURL& url) {
+ // Ignored.
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DraggedTabControllerGtk, NotificationObserver implementation:
+
+void DraggedTabControllerGtk::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED);
+ DCHECK(Source<TabContents>(source).ptr() == dragged_contents_);
+ EndDragImpl(TAB_DESTROYED);
+}
+
+void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) {
+ if (dragged_contents_) {
+ registrar_.Remove(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(dragged_contents_));
+ if (original_delegate_)
+ dragged_contents_->set_delegate(original_delegate_);
+ }
+ original_delegate_ = NULL;
+ dragged_contents_ = new_contents;
+ if (dragged_contents_) {
+ registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(dragged_contents_));
+
+ // We need to be the delegate so we receive messages about stuff,
+ // otherwise our dragged_contents() may be replaced and subsequently
+ // collected/destroyed while the drag is in process, leading to
+ // nasty crashes.
+ original_delegate_ = dragged_contents_->delegate();
+ dragged_contents_->set_delegate(this);
+ }
+}
+
+void DraggedTabControllerGtk::ContinueDragging() {
+ // TODO(jhawkins): We don't handle the situation where the last tab is dragged
+ // out of a window, so we'll just go with the way Windows handles dragging for
+ // now.
+ gfx::Point screen_point = GetCursorScreenPoint();
+ MoveTab(screen_point);
+}
+
+void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) {
+ gfx::Point dragged_point = GetDraggedPoint(screen_point);
+
+ if (attached_tabstrip_) {
+ // Determine the horizontal move threshold. This is dependent on the width
+ // of tabs. The smaller the tabs compared to the standard size, the smaller
+ // the threshold.
+ double unselected, selected;
+ attached_tabstrip_->GetCurrentTabWidths(&unselected, &selected);
+ double ratio = unselected / TabGtk::GetStandardSize().width();
+ int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
+
+ // Update the model, moving the TabContents from one index to another. Do
+ // this only if we have moved a minimum distance since the last reorder (to
+ // prevent jitter).
+ if (abs(screen_point.x() - last_move_screen_x_) > threshold) {
+ TabStripModel* attached_model = attached_tabstrip_->model();
+ int from_index =
+ attached_model->GetIndexOfTabContents(dragged_contents_);
+ gfx::Rect bounds = source_tab_->bounds();
+ int to_index = GetInsertionIndexForDraggedBounds(bounds);
+ to_index = NormalizeIndexToAttachedTabStrip(to_index);
+ if (from_index != to_index) {
+ last_move_screen_x_ = screen_point.x();
+ snap_bounds_ = attached_tabstrip_->GetTabAt(to_index)->bounds();
+ attached_model->MoveTabContentsAt(from_index, to_index, true);
+ }
+ }
+ }
+
+ // Move the tab. There are no changes to the model if we're detached.
+ gfx::Rect bounds = source_tab_->bounds();
+ bounds.set_x(dragged_point.x());
+ source_tab_->SetBounds(bounds);
+ gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()),
+ source_tab_->widget(), bounds.x(), bounds.y());
+}
+
+TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint(
+ const gfx::Point& screen_point) {
+ // TODO(jhawkins): Actually get the correct tabstrip under |screen_point|.
+ return source_tabstrip_;
+}
+
+int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds(
+ const gfx::Rect& dragged_bounds) const {
+ int right_tab_x = 0;
+
+ // TODO(jhawkins): Handle RTL layout.
+
+ // Divides each tab into two halves to see if the dragged tab has crossed
+ // the halfway boundary necessary to move past the next tab.
+ for (int i = 0; i < attached_tabstrip_->GetTabCount(); i++) {
+ gfx::Rect ideal_bounds = attached_tabstrip_->GetIdealBounds(i);
+
+ gfx::Rect left_half = ideal_bounds;
+ left_half.set_width(left_half.width() / 2);
+
+ gfx::Rect right_half = ideal_bounds;
+ right_half.set_width(ideal_bounds.width() - left_half.width());
+ right_half.set_x(left_half.right());
+
+ right_tab_x = right_half.right();
+
+ if (dragged_bounds.x() >= right_half.x() &&
+ dragged_bounds.x() < right_half.right()) {
+ return i + 1;
+ } else if (dragged_bounds.x() >= left_half.x() &&
+ dragged_bounds.x() < left_half.right()) {
+ return i;
+ }
+ }
+
+ if (dragged_bounds.right() > right_tab_x)
+ return attached_tabstrip_->model()->count();
+
+ return TabStripModel::kNoTab;
+}
+
+gfx::Point DraggedTabControllerGtk::GetDraggedPoint(const gfx::Point& point) {
+ int x = point.x() - mouse_offset_.x();
+ int y = point.y() - mouse_offset_.y();
+
+ // Snap the dragged tab to the tab strip.
+ if (x < 0)
+ x = 0;
+
+ // Make sure the tab can't be dragged off the right side of the tab strip.
+ int max_x = attached_tabstrip_->bounds_.right() - source_tab_->width();
+ if (x > max_x)
+ x = max_x;
+
+ return gfx::Point(x, y);
+}
+
+int DraggedTabControllerGtk::NormalizeIndexToAttachedTabStrip(int index) const {
+ if (index >= attached_tabstrip_->model_->count())
+ return attached_tabstrip_->model_->count() - 1;
+ if (index == TabStripModel::kNoTab)
+ return 0;
+ return index;
+}
+
+TabGtk* DraggedTabControllerGtk::GetTabMatchingDraggedContents(
+ TabStripGtk* tabstrip) const {
+ int index = tabstrip->model()->GetIndexOfTabContents(dragged_contents_);
+ return index == TabStripModel::kNoTab ? NULL : tabstrip->GetTabAt(index);
+}
+
+bool DraggedTabControllerGtk::EndDragImpl(EndDragType type) {
+ // WARNING: this may be invoked multiple times. In particular, if deletion
+ // occurs after a delay (as it does when the tab is released in the original
+ // tab strip) and the navigation controller/tab contents is deleted before
+ // the animation finishes, this is invoked twice. The second time through
+ // type == TAB_DESTROYED.
+
+ bool destroy_now = true;
+ if (type != TAB_DESTROYED) {
+ if (type == CANCELED) {
+ RevertDrag();
+ } else {
+ destroy_now = CompleteDrag();
+ }
+
+ if (dragged_contents_ && dragged_contents_->delegate() == this)
+ dragged_contents_->set_delegate(original_delegate_);
+ } else {
+ // If we get here it means the NavigationController is going down. Don't
+ // attempt to do any cleanup other than resetting the delegate (if we're
+ // still the delegate).
+ if (dragged_contents_ && dragged_contents_->delegate() == this)
+ dragged_contents_->set_delegate(NULL);
+ dragged_contents_ = NULL;
+ }
+
+ // The delegate of the dragged contents should have been reset. Unset the
+ // original delegate so that we don't attempt to reset the delegate when
+ // deleted.
+ DCHECK(!dragged_contents_ || dragged_contents_->delegate() != this);
+ original_delegate_ = NULL;
+
+ // If we're not destroyed now, we'll be destroyed asynchronously later.
+ if (destroy_now)
+ source_tabstrip_->DestroyDragController();
+
+ return destroy_now;
+}
+
+void DraggedTabControllerGtk::RevertDrag() {
+ // We save this here because code below will modify |attached_tabstrip_|.
+ if (attached_tabstrip_) {
+ int index = attached_tabstrip_->model()->GetIndexOfTabContents(
+ dragged_contents_);
+ if (attached_tabstrip_ != source_tabstrip_) {
+ // The Tab was inserted into another TabStrip. We need to put it back
+ // into the original one.
+ attached_tabstrip_->model()->DetachTabContentsAt(index);
+ // TODO(beng): (Cleanup) seems like we should use Attach() for this
+ // somehow.
+ attached_tabstrip_ = source_tabstrip_;
+ source_tabstrip_->model()->InsertTabContentsAt(source_model_index_,
+ dragged_contents_, true, false);
+ } else {
+ // The Tab was moved within the TabStrip where the drag was initiated.
+ // Move it back to the starting location.
+ source_tabstrip_->model()->MoveTabContentsAt(index, source_model_index_,
+ true);
+ }
+ } else {
+ // TODO(beng): (Cleanup) seems like we should use Attach() for this
+ // somehow.
+ attached_tabstrip_ = source_tabstrip_;
+ // The Tab was detached from the TabStrip where the drag began, and has not
+ // been attached to any other TabStrip. We need to put it back into the
+ // source TabStrip.
+ source_tabstrip_->model()->InsertTabContentsAt(source_model_index_,
+ dragged_contents_, true, false);
+ }
+}
+
+bool DraggedTabControllerGtk::CompleteDrag() {
+ // We don't need to do anything other than make the Tab visible again,
+ // since the dragged tab is going away.
+ gfx::Rect bounds = source_tab_->bounds();
+ bounds.set_x(snap_bounds_.x());
+ source_tab_->SetBounds(bounds);
+ gtk_fixed_move(GTK_FIXED(source_tabstrip_->tabstrip_.get()),
+ source_tab_->widget(), bounds.x(), bounds.y());
+
+ return false;
+}
+
+gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const {
+ // Get default display and screen.
+ GdkDisplay* display = gdk_display_get_default();
+
+ // Get cursor position.
+ int x, y;
+ gdk_display_get_pointer(display, NULL, &x, &y, NULL);
+
+ return gfx::Point(x, y);
+}
diff --git a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h
new file mode 100644
index 0000000..5381b38
--- /dev/null
+++ b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
+#define CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
+
+#include "base/timer.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+#include "chrome/common/notification_registrar.h"
+
+class TabGtk;
+class TabStripGtk;
+
+class DraggedTabControllerGtk : public NotificationObserver,
+ public TabContentsDelegate {
+ public:
+ DraggedTabControllerGtk(TabGtk* source_tab, TabStripGtk* source_tabstrip);
+ virtual ~DraggedTabControllerGtk();
+
+ // Capture information needed to be used during a drag session for this
+ // controller's associated source Tab and TabStrip. |mouse_offset| is the
+ // distance of the mouse pointer from the Tab's origin.
+ void CaptureDragInfo(const gfx::Point& mouse_offset);
+
+ // Responds to drag events subsequent to StartDrag. If the mouse moves a
+ // sufficient distance before the mouse is released, a drag session is
+ // initiated.
+ void Drag();
+
+ // Complete the current drag session. If the drag session was canceled
+ // because the user pressed Escape or something interrupted it, |canceled|
+ // is true so the helper can revert the state to the world before the drag
+ // begun. Returns whether the tab has been destroyed.
+ bool EndDrag(bool canceled);
+
+ private:
+ // Enumeration of the ways a drag session can end.
+ enum EndDragType {
+ // Drag session exited normally: the user released the mouse.
+ NORMAL,
+
+ // The drag session was canceled (alt-tab during drag, escape ...)
+ CANCELED,
+
+ // The tab (NavigationController) was destroyed during the drag.
+ TAB_DESTROYED
+ };
+
+ // Overridden from TabContentsDelegate:
+ virtual void OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition);
+ virtual void NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags);
+ virtual void AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture);
+ virtual void ActivateContents(TabContents* contents);
+ virtual void LoadingStateChanged(TabContents* source);
+ virtual void CloseContents(TabContents* source);
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
+ virtual bool IsPopup(TabContents* source);
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating);
+ virtual void URLStarredChanged(TabContents* source, bool starred);
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url);
+
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Sets the TabContents being dragged with the specified |new_contents|.
+ void SetDraggedContents(TabContents* new_contents);
+
+ // Move the DraggedTabView according to the current mouse screen position,
+ // potentially updating the source and other TabStrips.
+ void ContinueDragging();
+
+ // Handles moving the Tab within a TabStrip as well as updating the View.
+ void MoveTab(const gfx::Point& screen_point);
+
+ // Returns the compatible TabStrip that is under the specified point (screen
+ // coordinates), or NULL if there is none.
+ TabStripGtk* GetTabStripForPoint(const gfx::Point& screen_point);
+
+ // Returns the index where the dragged TabContents should be inserted into
+ // the attached TabStripModel given the DraggedTabView's bounds
+ // |dragged_bounds| in coordinates relative to the attached TabStrip.
+ int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
+
+ // Get the position of the dragged tab relative to the attached tab strip.
+ gfx::Point GetDraggedPoint(const gfx::Point& point);
+
+ // Finds the Tab within the specified TabStrip that corresponds to the
+ // dragged TabContents.
+ TabGtk* GetTabMatchingDraggedContents(TabStripGtk* tabstrip) const;
+
+ // Does the work for EndDrag. Returns whether the tab has been destroyed.
+ bool EndDragImpl(EndDragType how_end);
+
+ // If the drag was aborted for some reason, this function is called to un-do
+ // the changes made during the drag operation.
+ void RevertDrag();
+
+ // Finishes the drag operation. Returns true if the drag controller should
+ // be destroyed immediately, false otherwise.
+ bool CompleteDrag();
+
+ // Utility for getting the mouse position in screen coordinates.
+ gfx::Point GetCursorScreenPoint() const;
+
+ // Utility to convert the specified TabStripModel index to something valid
+ // for the attached TabStrip.
+ int NormalizeIndexToAttachedTabStrip(int index) const;
+
+ void BringWindowUnderMouseToFront();
+
+ // Handles registering for notifications.
+ NotificationRegistrar registrar_;
+
+ // The TabContents being dragged.
+ TabContents* dragged_contents_;
+
+ // The original TabContentsDelegate of |dragged_contents_|, before it was
+ // detached from the browser window. We store this so that we can forward
+ // certain delegate notifications back to it if we can't handle them locally.
+ TabContentsDelegate* original_delegate_;
+
+ // The tab that initiated the drag session.
+ TabGtk* source_tab_;
+
+ // The tab strip |source_tab_| originated from.
+ TabStripGtk* source_tabstrip_;
+
+ // This is the index of the |source_tab_| in |source_tabstrip_| when the drag
+ // began. This is used to restore the previous state if the drag is aborted.
+ int source_model_index_;
+
+ // The TabStrip the dragged Tab is currently attached to, or NULL if the
+ // dragged Tab is detached.
+ TabStripGtk* attached_tabstrip_;
+
+ // The position of the mouse (in screen coordinates) at the start of the drag
+ // operation. This is used to calculate minimum elasticity before a
+ // DraggedTabView is constructed.
+ gfx::Point start_screen_point_;
+
+ // This is the offset of the mouse from the top left of the Tab where
+ // dragging begun. This is used to ensure that the dragged view is always
+ // positioned at the correct location during the drag, and to ensure that the
+ // detached window is created at the right location.
+ gfx::Point mouse_offset_;
+
+ // The horizontal position of the mouse cursor in screen coordinates at the
+ // time of the last re-order event.
+ int last_move_screen_x_;
+
+ // The last good tab bounds of the dragged tab. This is the position the tab
+ // will be snapped back to when the drag is released.
+ // TODO(jhawkins): We should not be moving the tab itself, but rather a
+ // stand-in renderer.
+ gfx::Rect snap_bounds_;
+
+ // Timer used to bring the window under the cursor to front. If the user
+ // stops moving the mouse for a brief time over a browser window, it is
+ // brought to front.
+ base::OneShotTimer<DraggedTabControllerGtk> bring_to_front_timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(DraggedTabControllerGtk);
+};
+
+#endif // CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
diff --git a/chrome/browser/gtk/tabs/tab_gtk.cc b/chrome/browser/gtk/tabs/tab_gtk.cc
index 9966d0f..6db2595 100644
--- a/chrome/browser/gtk/tabs/tab_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_gtk.cc
@@ -5,15 +5,21 @@
#include "chrome/browser/gtk/tabs/tab_gtk.h"
#include "app/resource_bundle.h"
+#include "chrome/browser/gtk/custom_button.h"
#include "chrome/browser/gtk/menu_gtk.h"
#include "chrome/common/gfx/path.h"
#include "chrome/common/l10n_util.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
-static const SkScalar kTabCapWidth = 15;
-static const SkScalar kTabTopCurveWidth = 4;
-static const SkScalar kTabBottomCurveWidth = 3;
+namespace {
+
+// The targets available for drag n' drop.
+GtkTargetEntry target_table[] = {
+ { const_cast<char*>("application/x-chrome-tab"), GTK_TARGET_SAME_APP, 0 }
+};
+
+} // namespace
class TabGtk::ContextMenuController : public MenuGtk::Delegate {
public:
@@ -95,17 +101,38 @@ TabGtk::TabGtk(TabDelegate* delegate)
: TabRendererGtk(),
delegate_(delegate),
closing_(false) {
- ResourceBundle& rb = ResourceBundle::GetSharedInstance();
- SkBitmap* bitmap = rb.GetBitmapNamed(IDR_TAB_CLOSE);
-
- close_button_.reset(new TabButtonGtk(this));
- close_button_.get()->SetImage(TabButtonGtk::BS_NORMAL, bitmap);
- close_button_.get()->SetImage(TabButtonGtk::BS_HOT,
- rb.GetBitmapNamed(IDR_TAB_CLOSE_H));
- close_button_.get()->SetImage(TabButtonGtk::BS_PUSHED,
- rb.GetBitmapNamed(IDR_TAB_CLOSE_P));
- close_button_.get()->set_bounds(
- gfx::Rect(0, 0, bitmap->width(), bitmap->height()));
+ event_box_.Own(gtk_event_box_new());
+ gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE);
+ gtk_drag_source_set(event_box_.get(), GDK_BUTTON1_MASK,
+ target_table, G_N_ELEMENTS(target_table),
+ GDK_ACTION_MOVE);
+ gtk_drag_dest_set(event_box_.get(), GTK_DEST_DEFAULT_DROP,
+ target_table, G_N_ELEMENTS(target_table),
+ GDK_ACTION_MOVE);
+ gtk_drag_dest_set_track_motion(event_box_.get(), true);
+ g_signal_connect(G_OBJECT(event_box_.get()), "button-press-event",
+ G_CALLBACK(OnMousePress), this);
+ g_signal_connect(G_OBJECT(event_box_.get()), "button-release-event",
+ G_CALLBACK(OnMouseRelease), this);
+ g_signal_connect(G_OBJECT(event_box_.get()), "enter-notify-event",
+ G_CALLBACK(OnEnterNotify), this);
+ g_signal_connect(G_OBJECT(event_box_.get()), "leave-notify-event",
+ G_CALLBACK(OnLeaveNotify), this);
+ g_signal_connect_after(G_OBJECT(event_box_.get()), "drag-begin",
+ G_CALLBACK(&OnDragBegin), this);
+ g_signal_connect_after(G_OBJECT(event_box_.get()), "drag-end",
+ G_CALLBACK(&OnDragEnd), this);
+ g_signal_connect_after(G_OBJECT(event_box_.get()), "drag-failed",
+ G_CALLBACK(&OnDragFailed), this);
+ g_signal_connect_after(G_OBJECT(event_box_.get()), "drag-motion",
+ G_CALLBACK(&OnDragMotion), this);
+ gtk_widget_add_events(event_box_.get(),
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+ gtk_container_add(GTK_CONTAINER(event_box_.get()), TabRendererGtk::widget());
+ gtk_widget_show_all(event_box_.get());
+
+ close_button_.reset(MakeCloseButton());
}
TabGtk::~TabGtk() {
@@ -116,47 +143,84 @@ TabGtk::~TabGtk() {
// Invoke this so that we hide the highlight.
ContextMenuClosed();
}
+
+ event_box_.Destroy();
}
-bool TabGtk::IsPointInBounds(const gfx::Point& point) {
- GdkRegion* region = MakeRegionForTab();
- bool in_bounds = (gdk_region_point_in(region, point.x(), point.y()) == TRUE);
- gdk_region_destroy(region);
- return in_bounds;
+// static
+gboolean TabGtk::OnMousePress(GtkWidget* widget, GdkEventButton* event,
+ TabGtk* tab) {
+ if (event->button == 1) {
+ // Store whether or not we were selected just now... we only want to be
+ // able to drag foreground tabs, so we don't start dragging the tab if
+ // it was in the background.
+ bool just_selected = !tab->IsSelected();
+ if (just_selected) {
+ tab->delegate_->SelectTab(tab);
+ }
+ }
+
+ return TRUE;
}
-bool TabGtk::OnMotionNotify(GdkEventMotion* event) {
- gfx::Point point(event->x, event->y);
- bool paint = false;
+// static
+gboolean TabGtk::OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
+ TabGtk* tab) {
+ if (event->button == 2) {
+ tab->delegate_->CloseTab(tab);
+ } else if (event->button == 3) {
+ tab->ShowContextMenu();
+ }
- if (!(event->state & GDK_BUTTON1_MASK))
- paint = set_hovering(IsPointInBounds(point));
+ return TRUE;
+}
- paint |= close_button_.get()->OnMotionNotify(event);
- return paint;
+// static
+gboolean TabGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
+ TabGtk* tab) {
+ tab->OnMouseEntered();
+ return TRUE;
}
-bool TabGtk::OnMousePress(const gfx::Point& point) {
- if (close_button_.get()->IsPointInBounds(point))
- return close_button_.get()->OnMousePress();
+// static
+gboolean TabGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
+ TabGtk* tab) {
+ tab->OnMouseExited();
+ return TRUE;
+}
- return false;
+// static
+void TabGtk::OnDragBegin(GtkWidget* widget, GdkDragContext* context,
+ TabGtk* tab) {
+ int x, y;
+ gdk_window_get_pointer(tab->event_box_.get()->window, &x, &y, NULL);
+ tab->delegate_->MaybeStartDrag(tab, gfx::Point(x, y));
}
-void TabGtk::OnMouseRelease(GdkEventButton* event) {
- close_button_.get()->OnMouseRelease();
+// static
+void TabGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context,
+ TabGtk* tab) {
+ // Notify the drag helper that we're done with any potential drag operations.
+ // Clean up the drag helper, which is re-created on the next mouse press.
+ tab->delegate_->EndDrag(false);
+}
- if (event->button == 2) {
- delegate_->CloseTab(this);
- } else if (event->button == 3) {
- ShowContextMenu();
- }
+// static
+gboolean TabGtk::OnDragMotion(GtkWidget* widget,
+ GdkDragContext* context,
+ guint x, guint y,
+ guint time,
+ TabGtk* tab) {
+ tab->delegate_->ContinueDrag(context);
+ return TRUE;
}
-bool TabGtk::OnLeaveNotify() {
- bool paint = set_hovering(false);
- paint |= close_button_.get()->OnLeaveNotify();
- return paint;
+// static
+gboolean TabGtk::OnDragFailed(GtkWidget* widget, GdkDragContext* context,
+ GtkDragResult result,
+ TabGtk* tab) {
+ tab->delegate_->EndDrag(true);
+ return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
@@ -167,50 +231,23 @@ bool TabGtk::IsSelected() const {
}
void TabGtk::CloseButtonResized(const gfx::Rect& bounds) {
- close_button_.get()->set_bounds(bounds);
-}
-
-void TabGtk::Paint(ChromeCanvasPaint* canvas) {
- TabRendererGtk::Paint(canvas);
- close_button_.get()->Paint(canvas);
+ gtk_fixed_move(GTK_FIXED(TabRendererGtk::widget()),
+ close_button_.get()->widget(), bounds.x(), bounds.y());
}
-////////////////////////////////////////////////////////////////////////////////
-// TabGtk, TabButtonGtk::Delegate implementation:
+void TabGtk::Paint(GdkEventExpose* event) {
+ TabRendererGtk::Paint(event);
-GdkRegion* TabGtk::MakeRegionForButton(const TabButtonGtk* button) const {
- // Use the close button bounds for hit-testing.
- return NULL;
-}
-
-void TabGtk::OnButtonActivate(const TabButtonGtk* button) {
- delegate_->CloseTab(this);
+ gtk_container_propagate_expose(GTK_CONTAINER(TabRendererGtk::widget()),
+ close_button_.get()->widget(), event);
}
///////////////////////////////////////////////////////////////////////////////
// TabGtk, private:
-GdkRegion* TabGtk::MakeRegionForTab()const {
- int w = width();
- int h = height();
- static const int kNumRegionPoints = 9;
-
- GdkPoint polygon[kNumRegionPoints] = {
- { 0, h },
- { kTabBottomCurveWidth, h - kTabBottomCurveWidth },
- { kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth },
- { kTabCapWidth, 0 },
- { w - kTabCapWidth, 0 },
- { w - kTabCapWidth - kTabTopCurveWidth, kTabTopCurveWidth },
- { w - kTabBottomCurveWidth, h - kTabBottomCurveWidth },
- { w, h },
- { 0, h },
- };
-
- GdkRegion* region = gdk_region_polygon(polygon, kNumRegionPoints,
- GDK_WINDING_RULE);
- gdk_region_offset(region, x(), y());
- return region;
+// static
+void TabGtk::OnCloseButtonClicked(GtkWidget* widget, TabGtk* tab) {
+ tab->delegate_->CloseTab(tab);
}
void TabGtk::ShowContextMenu() {
@@ -224,3 +261,16 @@ void TabGtk::ContextMenuClosed() {
delegate()->StopAllHighlighting();
menu_controller_.reset();
}
+
+CustomDrawButton* TabGtk::MakeCloseButton() {
+ CustomDrawButton* button = new CustomDrawButton(IDR_TAB_CLOSE,
+ IDR_TAB_CLOSE_P, IDR_TAB_CLOSE_H, IDR_TAB_CLOSE);
+
+ g_signal_connect(G_OBJECT(button->widget()), "clicked",
+ G_CALLBACK(OnCloseButtonClicked), this);
+ GTK_WIDGET_UNSET_FLAGS(button->widget(), GTK_CAN_FOCUS);
+ gtk_fixed_put(GTK_FIXED(TabRendererGtk::widget()), button->widget(), 0, 0);
+ gtk_widget_show(button->widget());
+
+ return button;
+}
diff --git a/chrome/browser/gtk/tabs/tab_gtk.h b/chrome/browser/gtk/tabs/tab_gtk.h
index 3892844..2999564 100644
--- a/chrome/browser/gtk/tabs/tab_gtk.h
+++ b/chrome/browser/gtk/tabs/tab_gtk.h
@@ -6,7 +6,6 @@
#define CHROME_BROWSER_GTK_TABS_TAB_GTK_H_
#include "base/basictypes.h"
-#include "chrome/browser/gtk/tabs/tab_button_gtk.h"
#include "chrome/browser/gtk/tabs/tab_renderer_gtk.h"
#include "chrome/browser/tabs/tab_strip_model.h"
@@ -14,8 +13,9 @@ namespace gfx {
class Path;
}
-class TabGtk : public TabRendererGtk,
- public TabButtonGtk::Delegate {
+class CustomDrawButton;
+
+class TabGtk : public TabRendererGtk {
public:
// An interface implemented by an object that can help this Tab complete
// various actions. The index parameter is the index of this Tab in the
@@ -48,6 +48,12 @@ class TabGtk : public TabRendererGtk,
TabStripModel::ContextMenuCommand command_id, TabGtk* tab) = 0;
virtual void StopAllHighlighting() = 0;
+ // Potentially starts a drag for the specified Tab.
+ virtual void MaybeStartDrag(TabGtk* tab, const gfx::Point& point) = 0;
+
+ // Continues dragging a Tab.
+ virtual void ContinueDrag(GdkDragContext* context) = 0;
+
// Ends dragging a Tab. |canceled| is true if the drag was aborted in a way
// other than the user releasing the mouse. Returns whether the tab has been
// destroyed.
@@ -66,47 +72,56 @@ class TabGtk : public TabRendererGtk,
// Access the delegate.
TabDelegate* delegate() const { return delegate_; }
+ GtkWidget* widget() const { return event_box_.get(); }
+
// Used to set/check whether this Tab is being animated closed.
void set_closing(bool closing) { closing_ = closing; }
bool closing() const { return closing_; }
- // Checks whether |point| is inside the bounds of the tab.
- bool IsPointInBounds(const gfx::Point& point);
-
// TabRendererGtk overrides:
virtual bool IsSelected() const;
virtual void CloseButtonResized(const gfx::Rect& bounds);
- virtual void Paint(ChromeCanvasPaint* canvas);
+ virtual void Paint(GdkEventExpose* event);
+
+ // button-press-event handler that handles mouse clicks.
+ static gboolean OnMousePress(GtkWidget* widget, GdkEventButton* event,
+ TabGtk* tab);
- // Sent by the tabstrip when the mouse moves within this tab. Mouse state is
- // in |event|. Returns true if the tabstrip needs to be redrawn as a result
- // of the motion.
- bool OnMotionNotify(GdkEventMotion* event);
+ // button-release-event handler that handles mouse click releases.
+ static gboolean OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
+ TabGtk* tab);
- // Sent by the tabstrip when the mouse clicks within this tab. Returns true
- // if the tabstrip needs to be redrawn as a result of the click.
- bool OnMousePress(const gfx::Point& point);
+ // enter-notify-event handler that signals when the mouse enters the tab.
+ static gboolean OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
+ TabGtk* tab);
- // Sent by the tabstrip when the mouse click is released.
- void OnMouseRelease(GdkEventButton* event);
+ // leave-notify-event handler that signals when the mouse enters the tab.
+ static gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
+ TabGtk* tab);
- // Sent by the tabstrip when the mouse leaves this tab. Returns true
- // if the tabstrip needs to be redrawn as a result of the movement.
- bool OnLeaveNotify();
+ // drag-begin handler that signals when a drag action begins.
+ static void OnDragBegin(GtkWidget* widget, GdkDragContext* context,
+ TabGtk* tab);
- protected:
- // TabButtonGtk::Delegate implementation:
- virtual GdkRegion* MakeRegionForButton(const TabButtonGtk* button) const;
- virtual void OnButtonActivate(const TabButtonGtk* button);
+ // drag-end handler that signals when a drag action ends.
+ static void OnDragEnd(GtkWidget* widget, GdkDragContext* context,
+ TabGtk* tab);
+
+ // drag-motion handler that handles drag movements in the tabstrip.
+ static gboolean OnDragMotion(GtkWidget* widget, GdkDragContext* context,
+ guint x, guint y, guint time,
+ TabGtk* tab);
+
+ // drag-failed handler that is emitted when the drag fails.
+ static gboolean OnDragFailed(GtkWidget* widget, GdkDragContext* context,
+ GtkDragResult result, TabGtk* tab);
private:
class ContextMenuController;
-
friend class ContextMenuController;
- // Creates a clickable region of the tab's visual representation. Used for
- // hit-testing. Caller is responsible for destroying the region.
- GdkRegion* MakeRegionForTab() const;
+ // Handles the clicked signal for the close button.
+ static void OnCloseButtonClicked(GtkWidget* widget, TabGtk* tab);
// Shows the context menu.
void ShowContextMenu();
@@ -114,6 +129,8 @@ class TabGtk : public TabRendererGtk,
// Invoked when the context menu closes.
void ContextMenuClosed();
+ CustomDrawButton* MakeCloseButton();
+
// An instance of a delegate object that can perform various actions based on
// user gestures.
TabDelegate* delegate_;
@@ -124,7 +141,11 @@ class TabGtk : public TabRendererGtk,
// The context menu controller.
scoped_ptr<ContextMenuController> menu_controller_;
- scoped_ptr<TabButtonGtk> close_button_;
+ // The close button.
+ scoped_ptr<CustomDrawButton> close_button_;
+
+ // The windowless widget used to collect input events for the tab.
+ OwnedWidgetGtk event_box_;
DISALLOW_COPY_AND_ASSIGN(TabGtk);
};
diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
index 1ce7aaf..a9a9438 100644
--- a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
@@ -26,14 +26,21 @@ const int kFavIconSize = 16;
const int kSelectedTitleColor = SK_ColorBLACK;
const int kUnselectedTitleColor = SkColorSetRGB(64, 64, 64);
+// How long the hover state takes.
+const int kHoverDurationMs = 90;
+
+// How opaque to make the hover state (out of 1).
+const double kHoverOpacity = 0.33;
+
+const SkScalar kTabCapWidth = 15;
+const SkScalar kTabTopCurveWidth = 4;
+const SkScalar kTabBottomCurveWidth = 3;
+
// The vertical and horizontal offset used to position the close button
// in the tab. TODO(jhawkins): Ask pkasting what the Fuzz is about.
const int kCloseButtonVertFuzz = 0;
const int kCloseButtonHorzFuzz = 5;
-// How opaque to make the hover state (out of 1).
-const double kHoverOpacity = 0.33;
-
TabRendererGtk::LoadingAnimation::Data loading_animation_data;
// Loads the loading animation images and data.
@@ -116,6 +123,11 @@ void TabRendererGtk::LoadingAnimation::ValidateLoadingAnimation(
}
}
+bool TabRendererGtk::IsVisible() {
+ // TODO(jhawkins): Implement this and SetVisible.
+ return true;
+}
+
////////////////////////////////////////////////////////////////////////////////
// TabRendererGtk, public:
@@ -125,12 +137,21 @@ TabRendererGtk::TabRendererGtk()
showing_close_button_(false),
fav_icon_hiding_offset_(0),
should_display_crashed_favicon_(false),
- hovering_(false),
loading_animation_(&loading_animation_data) {
InitResources();
+
+ tab_.Own(gtk_fixed_new());
+ gtk_widget_set_app_paintable(tab_.get(), TRUE);
+ g_signal_connect(G_OBJECT(tab_.get()), "expose-event",
+ G_CALLBACK(OnExpose), this);
+ gtk_widget_show(tab_.get());
+
+ hover_animation_.reset(new SlideAnimation(this));
+ hover_animation_->SetSlideDuration(kHoverDurationMs);
}
TabRendererGtk::~TabRendererGtk() {
+ tab_.Destroy();
}
void TabRendererGtk::UpdateData(TabContents* contents, bool loading_only) {
@@ -236,6 +257,7 @@ void TabRendererGtk::LoadTabImages() {
}
void TabRendererGtk::SetBounds(const gfx::Rect& bounds) {
+ gtk_widget_set_size_request(tab_.get(), bounds.width(), bounds.height());
bounds_ = bounds;
Layout();
}
@@ -247,10 +269,29 @@ std::wstring TabRendererGtk::GetTitle() const {
return data_.title;
}
+///////////////////////////////////////////////////////////////////////////////
+// TabRendererGtk, AnimationDelegate implementation:
+
+void TabRendererGtk::AnimationProgressed(const Animation* animation) {
+ gtk_widget_queue_draw(tab_.get());
+}
+
+void TabRendererGtk::AnimationCanceled(const Animation* animation) {
+ AnimationEnded(animation);
+}
+
+void TabRendererGtk::AnimationEnded(const Animation* animation) {
+ gtk_widget_queue_draw(tab_.get());
+}
+
////////////////////////////////////////////////////////////////////////////////
// TabRendererGtk, private:
-void TabRendererGtk::Paint(ChromeCanvasPaint* canvas) {
+void TabRendererGtk::Paint(GdkEventExpose* event) {
+ ChromeCanvasPaint canvas(event);
+ if (canvas.isEmpty())
+ return;
+
// Don't paint if we're narrower than we can render correctly. (This should
// only happen during animations).
if (width() < GetMinimumUnselectedSize().width())
@@ -265,20 +306,20 @@ void TabRendererGtk::Paint(ChromeCanvasPaint* canvas) {
show_close_button != showing_close_button_)
Layout();
- PaintTabBackground(canvas);
+ PaintTabBackground(&canvas);
if (show_icon) {
if (loading_animation_.animation_state() != ANIMATION_NONE) {
- PaintLoadingAnimation(canvas);
+ PaintLoadingAnimation(&canvas);
} else if (!data_.favicon.isNull()) {
- canvas->DrawBitmapInt(data_.favicon, favicon_bounds_.x(),
- favicon_bounds_.y() + fav_icon_hiding_offset_);
+ canvas.DrawBitmapInt(data_.favicon, favicon_bounds_.x(),
+ favicon_bounds_.y() + fav_icon_hiding_offset_);
}
}
if (show_download_icon) {
- canvas->DrawBitmapInt(*download_icon_,
- download_icon_bounds_.x(), download_icon_bounds_.y());
+ canvas.DrawBitmapInt(*download_icon_,
+ download_icon_bounds_.x(), download_icon_bounds_.y());
}
// Paint the Title.
@@ -295,15 +336,9 @@ void TabRendererGtk::Paint(ChromeCanvasPaint* canvas) {
SkColor title_color = IsSelected() ? kSelectedTitleColor
: kUnselectedTitleColor;
- canvas->DrawStringInt(title, *title_font_, title_color, title_bounds_.x(),
- title_bounds_.y(), title_bounds_.width(),
- title_bounds_.height());
-}
-
-bool TabRendererGtk::set_hovering(bool hovering) {
- bool paint = (hovering_ != hovering);
- hovering_ = hovering;
- return paint;
+ canvas.DrawStringInt(title, *title_font_, title_color, title_bounds_.x(),
+ title_bounds_.y(), title_bounds_.width(),
+ title_bounds_.height());
}
void TabRendererGtk::Layout() {
@@ -340,10 +375,9 @@ void TabRendererGtk::Layout() {
int close_button_top =
kTopPadding + kCloseButtonVertFuzz +
(content_height - close_button_height_) / 2;
- close_button_bounds_.SetRect(bounds_.x() +
- local_bounds.width() + kCloseButtonHorzFuzz,
- bounds_.y() + close_button_top,
- close_button_width_, close_button_height_);
+ close_button_bounds_.SetRect(local_bounds.width() + kCloseButtonHorzFuzz,
+ close_button_top, close_button_width_,
+ close_button_height_);
} else {
close_button_bounds_.SetRect(0, 0, 0, 0);
}
@@ -364,7 +398,7 @@ void TabRendererGtk::Layout() {
int title_width;
if (close_button_bounds_.width() && close_button_bounds_.height()) {
- title_width = std::max(close_button_bounds_.x() -
+ title_width = std::max(bounds_.x() + close_button_bounds_.x() -
kTitleCloseButtonSpacing - title_left, 0);
} else {
title_width = std::max(local_bounds.width() - title_left, 0);
@@ -384,9 +418,10 @@ void TabRendererGtk::PaintTabBackground(ChromeCanvasPaint* canvas) {
PaintActiveTabBackground(canvas);
} else {
// Draw our hover state.
- // TODO(jhawkins): Hover animations.
- if (hovering_) {
- PaintHoverTabBackground(canvas, kHoverOpacity);
+ Animation* animation = hover_animation_.get();
+ if (animation->GetCurrentValue() > 0) {
+ PaintHoverTabBackground(canvas,
+ animation->GetCurrentValue() * kHoverOpacity);
} else {
PaintInactiveTabBackground(canvas);
}
@@ -403,8 +438,8 @@ void TabRendererGtk::PaintInactiveTabBackground(ChromeCanvasPaint* canvas) {
width() - tab_inactive_.l_width - tab_inactive_.r_width,
height());
canvas->DrawBitmapInt(*image.image_r,
- bounds_.x() + width() - tab_inactive_.r_width,
- bounds_.y());
+ bounds_.x() + width() - tab_inactive_.r_width,
+ bounds_.y());
}
void TabRendererGtk::PaintHoverTabBackground(ChromeCanvasPaint* canvas,
@@ -453,7 +488,6 @@ void TabRendererGtk::PaintLoadingAnimation(ChromeCanvasPaint* canvas) {
// dst_x = x() + width() - kLeftPadding - image_size;
int dst_x = x() + kLeftPadding;
-
canvas->DrawBitmapInt(*frames, image_offset, 0, image_size,
image_size, dst_x, dst_y, image_size, image_size,
false);
@@ -482,6 +516,23 @@ bool TabRendererGtk::ShouldShowCloseBox() const {
}
// static
+gboolean TabRendererGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event,
+ TabRendererGtk* tab) {
+ tab->Paint(event);
+ return TRUE;
+}
+
+void TabRendererGtk::OnMouseEntered() {
+ hover_animation_->SetTweenType(SlideAnimation::EASE_OUT);
+ hover_animation_->Show();
+}
+
+void TabRendererGtk::OnMouseExited() {
+ hover_animation_->SetTweenType(SlideAnimation::EASE_IN);
+ hover_animation_->Hide();
+}
+
+// static
void TabRendererGtk::InitResources() {
if (initialized_)
return;
diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.h b/chrome/browser/gtk/tabs/tab_renderer_gtk.h
index 3b95189..81f2688 100644
--- a/chrome/browser/gtk/tabs/tab_renderer_gtk.h
+++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.h
@@ -7,10 +7,13 @@
#include <gtk/gtk.h>
+#include "app/animation.h"
+#include "app/slide_animation.h"
#include "base/basictypes.h"
#include "base/gfx/rect.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/gfx/chrome_font.h"
+#include "chrome/common/owned_widget_gtk.h"
#include "skia/include/SkBitmap.h"
namespace gfx {
@@ -19,7 +22,7 @@ class Size;
class TabContents;
-class TabRendererGtk {
+class TabRendererGtk : public AnimationDelegate {
public:
// Possible animation states.
enum AnimationState {
@@ -84,12 +87,14 @@ class TabRendererGtk {
virtual void CloseButtonResized(const gfx::Rect& bounds);
// Paints the tab into |canvas|.
- virtual void Paint(ChromeCanvasPaint* canvas);
+ virtual void Paint(GdkEventExpose* event);
// Advance the loading animation to the next frame, or hide the animation if
// the tab isn't loading.
void ValidateLoadingAnimation(AnimationState animation_state);
+ bool IsVisible();
+
// Returns the minimum possible size of a single unselected Tab.
static gfx::Size GetMinimumUnselectedSize();
// Returns the minimum possible size of a selected Tab. Selected tabs must
@@ -114,16 +119,22 @@ class TabRendererGtk {
// Sets the bounds of the tab.
void SetBounds(const gfx::Rect& bounds);
+ GtkWidget* widget() const { return tab_.get(); }
+
protected:
const gfx::Rect& title_bounds() const { return title_bounds_; }
const gfx::Rect& close_button_bounds() const { return close_button_bounds_; }
- // Sets the hovering status of the tab. Returns true if a repaint is needed.
- bool set_hovering(bool hovering);
-
// Returns the title of the Tab.
std::wstring GetTitle() const;
+ // Called by TabGtk to notify the renderer that the tab is being hovered.
+ void OnMouseEntered();
+
+ // Called by TabGtk to notify the renderer that the tab is no longer being
+ // hovered.
+ void OnMouseExited();
+
private:
// Model data. We store this here so that we don't need to ask the underlying
// model, which is tricky since instances of this object can outlive the
@@ -147,6 +158,11 @@ class TabRendererGtk {
int r_width;
};
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationCanceled(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+
// Generates the bounds for the interior items of the tab.
void Layout();
@@ -170,6 +186,10 @@ class TabRendererGtk {
// Returns whether the Tab should display a close button.
bool ShouldShowCloseBox() const;
+ // expose-event handler that redraws the tab.
+ static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* e,
+ TabRendererGtk* tab);
+
// TODO(jhawkins): Move to TabResources.
static void InitResources();
static bool initialized_;
@@ -197,6 +217,9 @@ class TabRendererGtk {
static int close_button_width_;
static int close_button_height_;
+ // The GtkDrawingArea we draw the tab on.
+ OwnedWidgetGtk tab_;
+
// Whether we're showing the icon. It is cached so that we can detect when it
// changes and layout appropriately.
bool showing_icon_;
@@ -217,8 +240,8 @@ class TabRendererGtk {
// The bounds of this Tab.
gfx::Rect bounds_;
- // Set when the mouse is hovering over this tab and the tab is not selected.
- bool hovering_;
+ // Hover animation.
+ scoped_ptr<SlideAnimation> hover_animation_;
// Contains the loading animation state.
LoadingAnimation loading_animation_;
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc
index 75242fb..6631764 100644
--- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc
@@ -9,6 +9,8 @@
#include "base/gfx/gtk_util.h"
#include "base/gfx/point.h"
#include "chrome/browser/browser.h"
+#include "chrome/browser/gtk/custom_button.h"
+#include "chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h"
#include "chrome/browser/gtk/tabs/tab_button_gtk.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/gfx/chrome_canvas.h"
@@ -34,19 +36,10 @@ const int kTabHOffset = -16;
SkBitmap* background = NULL;
-// The targets available for drag n' drop.
-GtkTargetEntry target_table[] = {
- { const_cast<char*>("application/x-tabstrip-tab"), GTK_TARGET_SAME_APP, 0 }
-};
-
inline int Round(double x) {
return static_cast<int>(x + 0.5);
}
-bool IsButtonPressed(guint state) {
- return (state & GDK_BUTTON1_MASK);
-}
-
// widget->allocation is not guaranteed to be set. After window creation,
// we pick up the normal bounds by connecting to the configure-event signal.
gfx::Rect GetInitialWidgetBounds(GtkWidget* widget) {
@@ -349,7 +342,7 @@ class MoveTabAnimation : public TabStripGtk::TabAnimation {
double new_x = start_tab_a_bounds_.x() + delta;
gfx::Rect bounds(Round(new_x), tab_a_->y(), tab_a_->width(),
tab_a_->height());
- tab_a_->SetBounds(bounds);
+ tabstrip_->SetTabBounds(tab_a_, bounds);
// Position Tab B
distance = start_tab_a_bounds_.x() - start_tab_b_bounds_.x();
@@ -357,7 +350,7 @@ class MoveTabAnimation : public TabStripGtk::TabAnimation {
new_x = start_tab_b_bounds_.x() + delta;
bounds = gfx::Rect(Round(new_x), tab_b_->y(), tab_b_->width(),
tab_b_->height());
- tab_b_->SetBounds(bounds);
+ tabstrip_->SetTabBounds(tab_b_, bounds);
}
protected:
@@ -432,50 +425,6 @@ class ResizeLayoutAnimation : public TabStripGtk::TabAnimation {
};
////////////////////////////////////////////////////////////////////////////////
-
-// Handles the movement of a Tab to it's snapped tab index.
-class SnapTabAnimation : public TabStripGtk::TabAnimation {
- public:
- SnapTabAnimation(TabStripGtk* tabstrip, const gfx::Rect& bounds)
- : TabAnimation(tabstrip, SNAP),
- tabstrip_(tabstrip) {
- tab_ = tabstrip->GetTabAt(tabstrip->hover_index_);
- animation_start_bounds_ = tab_->bounds();
- animation_end_bounds_ = bounds;
- }
- virtual ~SnapTabAnimation() {}
-
- // Overridden from AnimationDelegate:
- virtual void AnimationProgressed(const Animation* animation) {
- int delta_x = (animation_end_bounds_.x() - animation_start_bounds_.x());
- int x = animation_start_bounds_.x() +
- static_cast<int>(delta_x * animation->GetCurrentValue());
- int y = animation_end_bounds_.y();
- gfx::Rect rect = tab_->bounds();
- rect.set_x(x);
- rect.set_y(y);
- tab_->SetBounds(rect);
-
- gtk_widget_queue_draw(tabstrip_->tabstrip_.get());
- }
-
- protected:
- // Overridden from TabStrip::TabAnimation:
- virtual int GetDuration() const { return kReorderAnimationDurationMs; }
-
- private:
- TabStripGtk* tabstrip_;
- // The tab being snapped.
- TabGtk* tab_;
-
- // The start and end bounds of the animation sequence.
- gfx::Rect animation_start_bounds_;
- gfx::Rect animation_end_bounds_;
-
- DISALLOW_COPY_AND_ASSIGN(SnapTabAnimation);
-};
-
-////////////////////////////////////////////////////////////////////////////////
// TabStripGtk, public:
TabStripGtk::TabStripGtk(TabStripModel* model)
@@ -483,11 +432,7 @@ TabStripGtk::TabStripGtk(TabStripModel* model)
current_selected_width_(TabGtk::GetStandardSize().width()),
available_width_for_tabs_(-1),
resize_layout_scheduled_(false),
- model_(model),
- hover_index_(0),
- mouse_offset_(-1, -1),
- last_move_x_(0),
- is_dragging_(false) {
+ model_(model) {
}
TabStripGtk::~TabStripGtk() {
@@ -505,7 +450,7 @@ TabStripGtk::~TabStripGtk() {
tab_data_.clear();
}
-void TabStripGtk::Init() {
+void TabStripGtk::Init(int width) {
ResourceBundle &rb = ResourceBundle::GetSharedInstance();
model_->AddObserver(this);
@@ -514,55 +459,21 @@ void TabStripGtk::Init() {
background = rb.GetBitmapNamed(IDR_WINDOW_TOP_CENTER);
}
- tabstrip_.Own(gtk_drawing_area_new());
- gtk_widget_set_size_request(tabstrip_.get(), -1,
+ tabstrip_.Own(gtk_fixed_new());
+ gtk_fixed_set_has_window(GTK_FIXED(tabstrip_.get()), TRUE);
+ gtk_widget_set_size_request(tabstrip_.get(), width,
TabGtk::GetMinimumUnselectedSize().height());
gtk_widget_set_app_paintable(tabstrip_.get(), TRUE);
- // ChromeCanvasPaint already effectively double buffers.
- gtk_widget_set_double_buffered(tabstrip_.get(), FALSE);
- gtk_drag_source_set(tabstrip_.get(), GDK_BUTTON1_MASK,
- target_table, G_N_ELEMENTS(target_table),
- GDK_ACTION_MOVE);
- gtk_drag_dest_set(tabstrip_.get(), GTK_DEST_DEFAULT_DROP,
- target_table, G_N_ELEMENTS(target_table),
- GDK_ACTION_MOVE);
- gtk_drag_dest_set_track_motion(tabstrip_.get(), true);
g_signal_connect(G_OBJECT(tabstrip_.get()), "expose-event",
G_CALLBACK(OnExpose), this);
- g_signal_connect(G_OBJECT(tabstrip_.get()), "configure-event",
- G_CALLBACK(OnConfigure), this);
- g_signal_connect(G_OBJECT(tabstrip_.get()), "motion-notify-event",
- G_CALLBACK(OnMotionNotify), this);
- g_signal_connect(G_OBJECT(tabstrip_.get()), "button-press-event",
- G_CALLBACK(OnMousePress), this);
- g_signal_connect(G_OBJECT(tabstrip_.get()), "button-release-event",
- G_CALLBACK(OnMouseRelease), this);
- g_signal_connect(G_OBJECT(tabstrip_.get()), "leave-notify-event",
- G_CALLBACK(OnLeaveNotify), this);
- g_signal_connect_after(G_OBJECT(tabstrip_.get()), "drag-begin",
- G_CALLBACK(&OnDragBegin), this);
- g_signal_connect_after(G_OBJECT(tabstrip_.get()), "drag-end",
- G_CALLBACK(&OnDragEnd), this);
- g_signal_connect_after(G_OBJECT(tabstrip_.get()), "drag-failed",
- G_CALLBACK(&OnDragFailed), this);
- g_signal_connect_after(G_OBJECT(tabstrip_.get()), "drag-motion",
- G_CALLBACK(&OnDragMotion), this);
- gtk_widget_add_events(tabstrip_.get(),
- GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |GDK_LEAVE_NOTIFY_MASK);
+ g_signal_connect(G_OBJECT(tabstrip_.get()), "size-allocate",
+ G_CALLBACK(OnSizeAllocate), this);
+
+ newtab_button_.reset(MakeNewTabButton());
+
gtk_widget_show_all(tabstrip_.get());
bounds_ = GetInitialWidgetBounds(tabstrip_.get());
-
- SkBitmap* bitmap = rb.GetBitmapNamed(IDR_NEWTAB_BUTTON);
- newtab_button_.reset(new TabButtonGtk(this));
- newtab_button_.get()->SetImage(TabButtonGtk::BS_NORMAL, bitmap);
- newtab_button_.get()->SetImage(TabButtonGtk::BS_HOT,
- rb.GetBitmapNamed(IDR_NEWTAB_BUTTON_H));
- newtab_button_.get()->SetImage(TabButtonGtk::BS_PUSHED,
- rb.GetBitmapNamed(IDR_NEWTAB_BUTTON_P));
- newtab_button_.get()->set_bounds(
- gfx::Rect(0, 0, bitmap->width(), bitmap->height()));
}
void TabStripGtk::AddTabStripToBox(GtkWidget* box) {
@@ -589,7 +500,8 @@ void TabStripGtk::Layout() {
int tab_right = 0;
for (int i = 0; i < tab_count; ++i) {
const gfx::Rect& bounds = tab_data_.at(i).ideal_bounds;
- GetTabAt(i)->SetBounds(bounds);
+ TabGtk* tab = GetTabAt(i);
+ SetTabBounds(tab, bounds);
tab_right = bounds.right() + kTabHOffset;
}
@@ -597,6 +509,10 @@ void TabStripGtk::Layout() {
gtk_widget_queue_draw(tabstrip_.get());
}
+void TabStripGtk::SetBounds(const gfx::Rect& bounds) {
+ bounds_ = bounds;
+}
+
void TabStripGtk::UpdateLoadingAnimations() {
for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
TabGtk* current_tab = GetTabAt(i);
@@ -621,6 +537,11 @@ bool TabStripGtk::IsAnimating() const {
return active_animation_.get() != NULL;
}
+void TabStripGtk::DestroyDragController() {
+ if (IsDragSessionActive())
+ drag_controller_.reset(NULL);
+}
+
gfx::Rect TabStripGtk::GetIdealBounds(int index) {
DCHECK(index >= 0 && index < GetTabCount());
return tab_data_.at(index).ideal_bounds;
@@ -651,6 +572,8 @@ void TabStripGtk::TabInsertedAt(TabContents* contents,
tab->UpdateData(contents, false);
}
+ gtk_fixed_put(GTK_FIXED(tabstrip_.get()), tab->widget(), 0, 0);
+
// Don't animate the first tab; it looks weird.
if (GetTabCount() > 1) {
StartInsertTabAnimation(index);
@@ -788,44 +711,30 @@ void TabStripGtk::StopAllHighlighting() {
// TODO(jhawkins): Hook up animations.
}
-bool TabStripGtk::EndDrag(bool canceled) {
- // TODO(jhawkins): Tab dragging.
- return true;
-}
+void TabStripGtk::MaybeStartDrag(TabGtk* tab, const gfx::Point& point) {
+ // Don't accidentally start any drag operations during animations if the
+ // mouse is down.
+ if (IsAnimating() || tab->closing() || !HasAvailableDragActions())
+ return;
-bool TabStripGtk::HasAvailableDragActions() const {
- return model_->delegate()->GetDragActions() != 0;
+ drag_controller_.reset(new DraggedTabControllerGtk(tab, this));
+ drag_controller_->CaptureDragInfo(point);
}
-////////////////////////////////////////////////////////////////////////////////
-// TabStripGtk, TabButtonGtk::Delegate implementation:
-
-GdkRegion* TabStripGtk::MakeRegionForButton(const TabButtonGtk* button) const {
- const int w = button->width();
- const int kNumRegionPoints = 8;
-
- // These values are defined by the shape of the new tab bitmap. Should that
- // bitmap ever change, these values will need to be updated. They're so
- // custom it's not really worth defining constants for.
- GdkPoint polygon[kNumRegionPoints] = {
- { 0, 1 },
- { w - 7, 1 },
- { w - 4, 4 },
- { w, 16 },
- { w - 1, 17 },
- { 7, 17 },
- { 4, 13 },
- { 0, 1 },
- };
+void TabStripGtk::ContinueDrag(GdkDragContext* context) {
+ // We can get called even if |MaybeStartDrag| wasn't called in the event of
+ // a TabStrip animation when the mouse button is down. In this case we should
+ // _not_ continue the drag because it can lead to weird bugs.
+ if (drag_controller_.get())
+ drag_controller_->Drag();
+}
- GdkRegion* region = gdk_region_polygon(polygon, kNumRegionPoints,
- GDK_WINDING_RULE);
- gdk_region_offset(region, button->x(), button->y());
- return region;
+bool TabStripGtk::EndDrag(bool canceled) {
+ return drag_controller_.get() ? drag_controller_->EndDrag(canceled) : false;
}
-void TabStripGtk::OnButtonActivate(const TabButtonGtk* button) {
- model_->delegate()->AddBlankTab(true);
+bool TabStripGtk::HasAvailableDragActions() const {
+ return model_->delegate()->GetDragActions() != 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -899,16 +808,12 @@ void TabStripGtk::LayoutNewTabButton(double last_tab_right,
// We're shrinking tabs, so we need to anchor the New Tab button to the
// right edge of the TabStrip's bounds, rather than the right edge of the
// right-most Tab, otherwise it'll bounce when animating.
- newtab_button_.get()->set_bounds(
- gfx::Rect(bounds_.width() - newtab_button_.get()->bounds().width(),
- kNewTabButtonVOffset,
- newtab_button_.get()->bounds().width(),
- newtab_button_.get()->bounds().height()));
+ gtk_fixed_move(GTK_FIXED(tabstrip_.get()), newtab_button_.get()->widget(),
+ bounds_.width() - newtab_button_.get()->width(), kNewTabButtonVOffset);
} else {
- newtab_button_.get()->set_bounds(
- gfx::Rect(Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset,
- kNewTabButtonVOffset, newtab_button_.get()->bounds().width(),
- newtab_button_.get()->bounds().height()));
+ gtk_fixed_move(GTK_FIXED(tabstrip_.get()), newtab_button_.get()->widget(),
+ Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset,
+ kNewTabButtonVOffset);
}
}
@@ -932,7 +837,7 @@ void TabStripGtk::GetDesiredTabWidths(int tab_count,
if (available_width_for_tabs_ < 0) {
available_width = bounds_.width();
available_width -=
- (kNewTabButtonHOffset + newtab_button_.get()->bounds().width());
+ (kNewTabButtonHOffset + newtab_button_.get()->width());
} else {
// Interesting corner case: if |available_width_for_tabs_| > the result
// of the calculation in the conditional arm above, the strip is in
@@ -1008,7 +913,7 @@ void TabStripGtk::AnimationLayout(double unselected_width) {
TabGtk* tab = GetTabAt(i);
gfx::Rect bounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
tab_height);
- tab->SetBounds(bounds);
+ SetTabBounds(tab, bounds);
tab_x = end_of_tab + kTabHOffset;
}
LayoutNewTabButton(tab_x, unselected_width);
@@ -1053,13 +958,6 @@ void TabStripGtk::StartResizeLayoutAnimation() {
active_animation_->Start();
}
-void TabStripGtk::StartSnapTabAnimation(const gfx::Rect& bounds) {
- if (active_animation_.get())
- active_animation_->Stop();
- active_animation_.reset(new SnapTabAnimation(this, bounds));
- active_animation_->Start();
-}
-
bool TabStripGtk::CanUpdateDisplay() {
// Don't bother laying out/painting when we're closing all tabs.
if (model_->closing_all()) {
@@ -1081,16 +979,27 @@ void TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation,
// static
gboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event,
TabStripGtk* tabstrip) {
- ChromeCanvasPaint canvas(event);
- if (canvas.isEmpty())
+
+ if (gdk_region_empty(event->region))
return TRUE;
- canvas.TileImageInt(*background, 0, 0, tabstrip->bounds_.width(),
- tabstrip->bounds_.height());
+ // TODO(jhawkins): Ideally we'd like to only draw what's needed in the damage
+ // rect, but the tab widgets overlap each other, and painting on one widget
+ // will cause an expose-event to be sent to the widgets underneath. The
+ // underlying widget does not need to be redrawn as we control the order of
+ // expose-events. Currently we hack it to redraw the entire tabstrip. We
+ // could change the damage rect to just contain the tabs + the new tab button.
+ event->area.x = 0;
+ event->area.y = 0;
+ event->area.width = tabstrip->bounds_.width();
+ event->area.height = tabstrip->bounds_.height();
+ gdk_region_union_with_rect(event->region, &event->area);
- // Paint the New Tab button. This is painted first because a dragged tab
- // should be at the bottom of the z-order.
- tabstrip->newtab_button_.get()->Paint(&canvas);
+ tabstrip->PaintBackground(event);
+
+ // Paint the New Tab button.
+ gtk_container_propagate_expose(GTK_CONTAINER(tabstrip->tabstrip_.get()),
+ tabstrip->newtab_button_.get()->widget(), event);
// Paint the tabs in reverse order, so they stack to the left.
TabGtk* selected_tab = NULL;
@@ -1101,29 +1010,39 @@ gboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event,
// the model will be different to this object, e.g. when a Tab is being
// removed after its TabContents has been destroyed.
if (!tab->IsSelected()) {
- tab->Paint(&canvas);
+ gtk_container_propagate_expose(GTK_CONTAINER(tabstrip->tabstrip_.get()),
+ tab->widget(), event);
} else {
selected_tab = tab;
}
}
// Paint the selected tab last, so it overlaps all the others.
- if (selected_tab)
- selected_tab->Paint(&canvas);
+ if (selected_tab) {
+ gtk_container_propagate_expose(GTK_CONTAINER(tabstrip->tabstrip_.get()),
+ selected_tab->widget(), event);
+ }
return TRUE;
}
// static
-gboolean TabStripGtk::OnConfigure(GtkWidget* widget, GdkEventConfigure* event,
- TabStripGtk* tabstrip) {
- gfx::Rect bounds = gfx::Rect(event->x, event->y, event->width, event->height);
+void TabStripGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation,
+ TabStripGtk* tabstrip) {
+ gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ // Nothing to do if the bounds are the same. If we don't catch this, we'll
+ // get an infinite loop of size-allocate signals.
+ if (tabstrip->bounds_ == bounds)
+ return;
+
tabstrip->SetBounds(bounds);
// No tabs, nothing to layout. This happens when a browser window is created
// and shown before tabs are added (as in a popup window).
if (tabstrip->GetTabCount() == 0)
- return TRUE;
+ return;
// Do a regular layout on the first configure-event so we don't animate
// the first tab.
@@ -1134,347 +1053,32 @@ gboolean TabStripGtk::OnConfigure(GtkWidget* widget, GdkEventConfigure* event,
tabstrip->Layout();
else
tabstrip->ResizeLayoutTabs();
-
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event,
- TabStripGtk* tabstrip) {
- // The dragging code handles moving the tab while the tab is being dragged.
- if (tabstrip->is_dragging_)
- return TRUE;
-
- int old_hover_index = tabstrip->hover_index_;
- gfx::Point point(event->x, event->y);
-
- int index;
- TabAnimation* animation = tabstrip->active_animation_.get();
- if (animation && animation->type() == TabAnimation::SNAP) {
- index = tabstrip->FindTabHoverIndexIterative(point);
- } else {
- index = tabstrip->FindTabHoverIndexFast(point);
- }
-
- // Hovering does not take place outside of the currently highlighted tab if
- // the button is pressed.
- if (IsButtonPressed(event->state) && index != tabstrip->hover_index_)
- return TRUE;
-
- tabstrip->hover_index_ = index;
-
- bool paint = false;
- if (old_hover_index != -1 && old_hover_index != index &&
- old_hover_index < tabstrip->GetTabCount()) {
- // Notify the previously highlighted tab that the mouse has left.
- paint = tabstrip->GetTabAt(old_hover_index)->OnLeaveNotify();
- }
-
- if (tabstrip->hover_index_ == -1) {
- // If the hover index is out of bounds, try the new tab button.
- paint |= tabstrip->newtab_button_.get()->OnMotionNotify(event);
- } else {
- // Notify the currently highlighted tab where the mouse is.
- paint |= tabstrip->GetTabAt(tabstrip->hover_index_)->OnMotionNotify(event);
- }
-
- if (paint)
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
-
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnMousePress(GtkWidget* widget, GdkEventButton* event,
- TabStripGtk* tabstrip) {
- gfx::Point point(event->x, event->y);
-
- // Nothing happens on mouse press for middle and right click.
- if (event->button != 1)
- return TRUE;
-
- // The hover index is stale if we're in the middle of an animation and the
- // mouse is pressed without any movement.
- if (tabstrip->active_animation_.get())
- tabstrip->hover_index_ = tabstrip->FindTabHoverIndexIterative(point);
-
- if (tabstrip->hover_index_ == -1) {
- if (tabstrip->newtab_button_.get()->IsPointInBounds(point) &&
- tabstrip->newtab_button_.get()->OnMousePress())
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
-
- return TRUE;
- }
-
- TabGtk* tab = tabstrip->GetTabAt(tabstrip->hover_index_);
-
- // If a previous tab is closing, the hover index does not match the model
- // index.
- tabstrip->hover_index_ = tabstrip->GetIndexOfTab(tab);
-
- if (tab->OnMousePress(point)) {
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
- } else if (tabstrip->hover_index_ != tabstrip->model()->selected_index() &&
- !tab->closing()) {
- tabstrip->model()->SelectTabContentsAt(tabstrip->hover_index_, true);
- }
-
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
- TabStripGtk* tabstrip) {
- gfx::Point point(event->x, event->y);
- if (tabstrip->hover_index_ != -1) {
- tabstrip->GetTabAt(tabstrip->hover_index_)->OnMouseRelease(event);
- } else {
- tabstrip->newtab_button_.get()->OnMouseRelease();
- }
-
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
- TabStripGtk* tabstrip) {
- if (tabstrip->is_dragging_) {
- TabGtk* tab = tabstrip->GetTabAt(tabstrip->hover_index_);
- tabstrip->MoveTab(tab, gfx::Point(event->x, event->y));
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
- }
-
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
- TabStripGtk* tabstrip) {
- // No leave notification if the mouse button is pressed.
- if (IsButtonPressed(event->state))
- return TRUE;
-
- // gtk does not set the button pressed state when a drag is occurring, so
- // bail out if we're dragging. Otherwise, the SnapTabAnimation will have
- // the wrong tab index (-1) when initiating the snap.
- if (tabstrip->is_dragging_)
- return TRUE;
-
- // A leave-notify-event is generated on mouse click, which sets the mode to
- // GDK_CROSSING_GRAB. Ignore this event because it doesn't meant the mouse
- // has left the tabstrip.
- if (event->mode == GDK_CROSSING_GRAB)
- return TRUE;
-
- // There is a race between the remove tab animation and the hover index
- // handling in OnMotionNotify. As the mouse leaves the tabstrip,
- // OnMotionNotify figures out the hover index, the remove tab animation moves
- // a frame, and the hover index becomes stale as OnLeaveNotify is called.
- // The check against GetTabCount avoids this scenario.
- if (tabstrip->hover_index_ != -1 &&
- tabstrip->hover_index_ < tabstrip->GetTabCount()) {
- tabstrip->GetTabAt(tabstrip->hover_index_)->OnLeaveNotify();
- tabstrip->hover_index_ = -1;
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
- }
-
- return TRUE;
-}
-
-// static
-void TabStripGtk::OnDragBegin(GtkWidget* widget, GdkDragContext* context,
- TabStripGtk* tabstrip) {
- // No dragging should happen if the tab is closing.
- if (tabstrip->hover_index_ == -1 ||
- tabstrip->GetTabAt(tabstrip->hover_index_)->closing()) {
- gdk_drop_finish(context, FALSE, 0);
- return;
- }
-
- // If we're in the middle of a snap animation, stop the animation. We only
- // set the snap bounds if the tab is snapped into a proper index, which is not
- // the case if it's being snapped.
- if (tabstrip->active_animation_.get()) {
- tabstrip->active_animation_->Stop();
- } else {
- tabstrip->snap_bounds_ =
- tabstrip->GetTabAt(tabstrip->hover_index_)->bounds();
- }
-
- tabstrip->is_dragging_ = true;
}
// static
-void TabStripGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context,
- TabStripGtk* tabstrip) {
- if (tabstrip->is_dragging_) {
- tabstrip->StartSnapTabAnimation(tabstrip->snap_bounds_);
- tabstrip->mouse_offset_ = gfx::Point(-1, -1);
- tabstrip->is_dragging_ = false;
- }
+void TabStripGtk::OnNewTabClicked(GtkWidget* widget, TabStripGtk* tabstrip) {
+ tabstrip->model_->delegate()->AddBlankTab(true);
}
-// static
-gboolean TabStripGtk::OnDragFailed(GtkWidget* widget, GdkDragContext* context,
- GtkDragResult result,
- TabStripGtk* tabstrip) {
- tabstrip->mouse_offset_ = gfx::Point(-1, -1);
- tabstrip->is_dragging_ = false;
- return TRUE;
-}
-
-// static
-gboolean TabStripGtk::OnDragMotion(GtkWidget* widget,
- GdkDragContext* drag_context,
- guint x, guint y,
- guint time,
- TabStripGtk* tabstrip) {
- // gtk sends drag-motion signals even after the drag has failed, but before
- // the drag-end signal is emitted.
- if (!tabstrip->is_dragging_)
- return TRUE;
-
- TabGtk* tab = tabstrip->GetTabAt(tabstrip->hover_index_);
- gfx::Rect bounds = tab->bounds();
-
- // For whatever reason, gtk does not give us the coordinates of the mouse in
- // the drag-begin event, so set them here once.
- if (tabstrip->mouse_offset_ == gfx::Point(-1, -1))
- tabstrip->mouse_offset_.SetPoint(x - bounds.x(), y - bounds.y());
-
- tabstrip->MoveTab(tab, gfx::Point(x, y));
- gtk_widget_queue_draw(tabstrip->tabstrip_.get());
-
- return TRUE;
-}
-
-int TabStripGtk::FindTabHoverIndexIterative(const gfx::Point& point) {
- for (int i = 0; i < GetTabCount(); i++) {
- if (GetTabAt(i)->IsPointInBounds(point))
- return i;
- }
-
- return -1;
-}
-
-int TabStripGtk::FindTabHoverIndexFast(const gfx::Point& point) {
- // Get a rough estimate for which tab the mouse is over.
- int index = point.x() / (current_unselected_width_ + kTabHOffset);
-
- // Tab hovering calcuation.
- // Using the rough estimate tab index, we check the tab bounds in a smart
- // order to reduce the number of tabs we need to check. If the tab at the
- // estimated index is selected, check it first as it covers both tabs below
- // it. Otherwise, check the tab to the left, then the estimated tab, and
- // finally the tab to the right (tabs stack to the left.)
-
- int tab_count = GetTabCount();
- if (index == tab_count &&
- GetTabAt(index - 1)->IsPointInBounds(point)) {
- index--;
- } else if (index >= tab_count) {
- index = -1;
- } else if (index > 0 &&
- GetTabAt(index - 1)->IsPointInBounds(point)) {
- index--;
- } else if (index < tab_count - 1 &&
- GetTabAt(index + 1)->IsPointInBounds(point)) {
- index++;
- }
-
- return index;
+void TabStripGtk::PaintBackground(GdkEventExpose* event) {
+ ChromeCanvasPaint canvas(event);
+ canvas.TileImageInt(*background, 0, 0, bounds_.width(), bounds_.height());
}
-void TabStripGtk::MoveTab(TabGtk* tab, const gfx::Point& point) {
- // Determine the horizontal move threshold. This is dependent on the width
- // of tabs. The smaller the tabs compared to the standard size, the smaller
- // the threshold.
- double unselected, selected;
- GetCurrentTabWidths(&unselected, &selected);
-
- double ratio = unselected / TabGtk::GetStandardSize().width();
- int threshold = static_cast<int>(ratio * kHorizontalMoveThreshold);
-
- // Update the model, moving the TabContents from one index to another. Do
- // this only if we have moved a minimum distance since the last reorder (to
- // prevent jitter).
- if (abs(point.x() - last_move_x_) > threshold) {
- TabStripModel* attached_model = model();
- int from_index = hover_index_;
- gfx::Rect bounds = GetTabAt(hover_index_)->bounds();
-
- int to_index = GetInsertionIndexForDraggedBounds(bounds);
- to_index = NormalizeIndexToAttachedTabStrip(to_index);
- if (from_index != to_index) {
- last_move_x_ = point.x();
- hover_index_ = to_index;
- snap_bounds_ = GetTabAt(to_index)->bounds();
- attached_model->MoveTabContentsAt(from_index, to_index, true);
- }
- }
-
- // Move the tab.
- gfx::Point dragged_point = GetDraggedPoint(tab, point);
- gfx::Rect bounds = tab->bounds();
- bounds.set_x(dragged_point.x());
+void TabStripGtk::SetTabBounds(TabGtk* tab, const gfx::Rect& bounds) {
tab->SetBounds(bounds);
+ gtk_fixed_move(GTK_FIXED(tabstrip_.get()), tab->widget(),
+ bounds.x(), bounds.y());
}
-gfx::Point TabStripGtk::GetDraggedPoint(TabGtk* tab, const gfx::Point& point) {
- int x = point.x() - mouse_offset_.x();
- int y = point.y() - mouse_offset_.y();
-
- // Snap the dragged tab to the tab strip.
- if (x < 0)
- x = 0;
-
- // Make sure the tab can't be dragged off the right side of the tab strip.
- int max_x = bounds_.right() - tab->width();
- if (x > max_x)
- x = max_x;
-
- return gfx::Point(x, y);
-}
-
-int TabStripGtk::GetInsertionIndexForDraggedBounds(
- const gfx::Rect& dragged_bounds) {
- int right_tab_x = 0;
+CustomDrawButton* TabStripGtk::MakeNewTabButton() {
+ CustomDrawButton* button = new CustomDrawButton(IDR_NEWTAB_BUTTON,
+ IDR_NEWTAB_BUTTON_P, IDR_NEWTAB_BUTTON_H, 0);
- // TODO(jhawkins): Handle RTL layout.
-
- // Divides each tab into two halves to see if the dragged tab has crossed
- // the halfway boundary necessary to move past the next tab.
- for (int i = 0; i < GetTabCount(); i++) {
- gfx::Rect ideal_bounds = GetIdealBounds(i);
-
- gfx::Rect left_half = ideal_bounds;
- left_half.set_width(left_half.width() / 2);
-
- gfx::Rect right_half = ideal_bounds;
- right_half.set_width(ideal_bounds.width() - left_half.width());
- right_half.set_x(left_half.right());
-
- right_tab_x = right_half.right();
-
- if (dragged_bounds.x() >= right_half.x() &&
- dragged_bounds.x() < right_half.right()) {
- return i + 1;
- } else if (dragged_bounds.x() >= left_half.x() &&
- dragged_bounds.x() < left_half.right()) {
- return i;
- }
- }
-
- if (dragged_bounds.right() > right_tab_x)
- return model()->count();
-
- return TabStripModel::kNoTab;
-}
+ g_signal_connect(G_OBJECT(button->widget()), "clicked",
+ G_CALLBACK(OnNewTabClicked), this);
+ GTK_WIDGET_UNSET_FLAGS(button->widget(), GTK_CAN_FOCUS);
+ gtk_fixed_put(GTK_FIXED(tabstrip_.get()), button->widget(), 0, 0);
-int TabStripGtk::NormalizeIndexToAttachedTabStrip(int index) {
- if (index >= model_->count())
- return model_->count() - 1;
- if (index == TabStripModel::kNoTab)
- return 0;
- return index;
+ return button;
}
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.h b/chrome/browser/gtk/tabs/tab_strip_gtk.h
index 1d4a118..0e0739c 100644
--- a/chrome/browser/gtk/tabs/tab_strip_gtk.h
+++ b/chrome/browser/gtk/tabs/tab_strip_gtk.h
@@ -10,14 +10,15 @@
#include "base/basictypes.h"
#include "base/gfx/rect.h"
-#include "chrome/browser/gtk/tabs/tab_button_gtk.h"
#include "chrome/browser/gtk/tabs/tab_gtk.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/common/owned_widget_gtk.h"
+class CustomDrawButton;
+class DraggedTabControllerGtk;
+
class TabStripGtk : public TabStripModelObserver,
- public TabGtk::TabDelegate,
- public TabButtonGtk::Delegate {
+ public TabGtk::TabDelegate {
public:
class TabAnimation;
@@ -25,7 +26,7 @@ class TabStripGtk : public TabStripModelObserver,
virtual ~TabStripGtk();
// Initialize and load the TabStrip into a container.
- void Init();
+ void Init(int width);
void AddTabStripToBox(GtkWidget* box);
void Show();
@@ -33,11 +34,14 @@ class TabStripGtk : public TabStripModelObserver,
TabStripModel* model() const { return model_; }
+ // Returns true if there is an active drag session.
+ bool IsDragSessionActive() const { return drag_controller_.get() != NULL; }
+
// Sets the bounds of the tabs.
void Layout();
// Sets the bounds of the tabstrip.
- void SetBounds(const gfx::Rect& bounds) { bounds_ = bounds; }
+ void SetBounds(const gfx::Rect& bounds);
// Updates loading animations for the TabStrip.
void UpdateLoadingAnimations();
@@ -46,6 +50,9 @@ class TabStripGtk : public TabStripModelObserver,
// position.
bool IsAnimating() const;
+ // Destroys the active drag controller.
+ void DestroyDragController();
+
// Retrieve the ideal bounds for the Tab at the specified index.
gfx::Rect GetIdealBounds(int index);
@@ -76,18 +83,16 @@ class TabStripGtk : public TabStripModelObserver,
virtual void StopHighlightTabsForCommand(
TabStripModel::ContextMenuCommand command_id, TabGtk* tab);
virtual void StopAllHighlighting();
+ virtual void MaybeStartDrag(TabGtk* tab, const gfx::Point& point);
+ virtual void ContinueDrag(GdkDragContext* context);
virtual bool EndDrag(bool canceled);
virtual bool HasAvailableDragActions() const;
- // TabButtonGtk::Delegate implementation:
- virtual GdkRegion* MakeRegionForButton(const TabButtonGtk* button) const;
- virtual void OnButtonActivate(const TabButtonGtk* button);
-
private:
+ friend class DraggedTabControllerGtk;
friend class InsertTabAnimation;
friend class RemoveTabAnimation;
friend class MoveTabAnimation;
- friend class SnapTabAnimation;
friend class ResizeLayoutAnimation;
friend class TabAnimation;
@@ -100,76 +105,21 @@ class TabStripGtk : public TabStripModelObserver,
static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* e,
TabStripGtk* tabstrip);
- // configure-event handler that gets the new bounds of the tabstrip.
- static gboolean OnConfigure(GtkWidget* widget, GdkEventConfigure* event,
- TabStripGtk* tabstrip);
-
- // motion-notify-event handler that handles mouse movement in the tabstrip.
- static gboolean OnMotionNotify(GtkWidget* widget, GdkEventMotion* event,
- TabStripGtk* tabstrip);
-
- // button-press-event handler that handles mouse clicks.
- static gboolean OnMousePress(GtkWidget* widget, GdkEventButton* event,
- TabStripGtk* tabstrip);
-
- // button-release-event handler that handles mouse click releases.
- static gboolean OnMouseRelease(GtkWidget* widget, GdkEventButton* event,
- TabStripGtk* tabstrip);
-
- // enter-notify-event handler that signals when the mouse enters the tabstrip.
- static gboolean OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event,
- TabStripGtk* tabstrip);
-
- // leave-notify-event handler that signals when the mouse leaves the tabstrip.
- static gboolean OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
- TabStripGtk* tabstrip);
-
- // drag-begin handler that signals when a drag action begins.
- static void OnDragBegin(GtkWidget* widget, GdkDragContext* context,
- TabStripGtk* tabstrip);
-
- // drag-end handler that signals when a drag action ends.
- static void OnDragEnd(GtkWidget* widget, GdkDragContext* context,
- TabStripGtk* tabstrip);
+ // size-allocate handler that gets the new bounds of the tabstrip.
+ static void OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation,
+ TabStripGtk* tabstrip);
- // drag-motion handler that handles drag movements in the tabstrip.
- static gboolean OnDragMotion(GtkWidget* widget, GdkDragContext* context,
- guint x, guint y, guint time,
- TabStripGtk* tabstrip);
+ // Handles the clicked signal from the new tab button.
+ static void OnNewTabClicked(GtkWidget* widget, TabStripGtk* tabstrip);
- // drag-failed handler that is emitted when the drag fails.
- static gboolean OnDragFailed(GtkWidget* widget, GdkDragContext* context,
- GtkDragResult result, TabStripGtk* tabstrip);
+ // Renders the tabstrip background.
+ void PaintBackground(GdkEventExpose* event);
- // Finds the tab that is under |point| by iterating through all of the tabs
- // and checking if |point| is in their bounds. This method is only used when
- // the state of all the tabs cannot be calculated, as during a SnapTab
- // animation. Runs in O(n) time.
- int FindTabHoverIndexIterative(const gfx::Point& point);
+ // Sets the bounds of the tab and moves the tab widget to those bounds.
+ void SetTabBounds(TabGtk* tab, const gfx::Rect& bounds);
- // Finds the tab that is under |point| by estimating the tab index and
- // checking if |point| is in the bounds of the surrounding tabs. This method
- // is optimal and is used in most cases. Runs in O(1) time.
- int FindTabHoverIndexFast(const gfx::Point& point);
-
- // -- Drag & Drop ------------------------------------------------------------
- //
- // TODO(jhawkins): These functions belong in DraggedTabControllerGtk.
-
- // Returns the index where the dragged TabContents should be inserted into
- // the attached TabStripModel given the DraggedTabView's bounds
- // |dragged_bounds| in coordinates relative to the attached TabStrip.
- int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds);
-
- // Utility to convert the specified TabStripModel index to something valid
- // for the attached TabStrip.
- int NormalizeIndexToAttachedTabStrip(int index);
-
- // Handles moving the Tab within a TabStrip.
- void MoveTab(TabGtk* tab, const gfx::Point& point);
-
- // Get the position of the dragged tab relative to the attached tab strip.
- gfx::Point GetDraggedPoint(TabGtk* tab, const gfx::Point& point);
+ // Initializes the new tab button.
+ CustomDrawButton* MakeNewTabButton();
// Gets the number of Tabs in the collection.
int GetTabCount() const;
@@ -226,7 +176,6 @@ class TabStripGtk : public TabStripModelObserver,
void StartRemoveTabAnimation(int index, TabContents* contents);
void StartResizeLayoutAnimation();
void StartMoveTabAnimation(int from_index, int to_index);
- void StartSnapTabAnimation(const gfx::Rect& bounds);
// Returns true if detach or select changes in the model should be reflected
// in the TabStrip. This returns false if we're closing all tabs in the
@@ -262,7 +211,7 @@ class TabStripGtk : public TabStripModelObserver,
// TODO(beng): (Cleanup) this would be better named "needs_resize_layout_".
bool resize_layout_scheduled_;
- // The drawing area widget.
+ // The GtkFixed widget.
OwnedWidgetGtk tabstrip_;
// The bounds of the tabstrip.
@@ -271,34 +220,15 @@ class TabStripGtk : public TabStripModelObserver,
// Our model.
TabStripModel* model_;
- // The index of the tab the mouse is currently over. -1 if not over a tab.
- int hover_index_;
-
// The currently running animation.
scoped_ptr<TabAnimation> active_animation_;
// The New Tab button.
- scoped_ptr<TabButtonGtk> newtab_button_;
-
- // ===========================================================================
- // TODO(jhawkins): This belongs in DraggedTabControllerGtk.
-
- // This is the offset of the mouse from the top left of the Tab where
- // dragging begun. This is used to ensure that the dragged view is always
- // positioned at the correct location during the drag, and to ensure that the
- // detached window is created at the right location.
- gfx::Point mouse_offset_;
-
- // The horizontal position of the mouse cursor at the time of the last
- // re-order event.
- int last_move_x_;
-
- // The last good tab bounds of the dragged tab. This is the position the tab
- // will be snapped back to when the drag is released.
- gfx::Rect snap_bounds_;
+ scoped_ptr<CustomDrawButton> newtab_button_;
- // When a tab is being dragged, certain gtk events should be ignored.
- bool is_dragging_;
+ // The controller for a drag initiated from a Tab. Valid for the lifetime of
+ // the drag session.
+ scoped_ptr<DraggedTabControllerGtk> drag_controller_;
DISALLOW_COPY_AND_ASSIGN(TabStripGtk);
};
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 5fe5813..37308c3 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -880,6 +880,8 @@
'browser/gtk/status_bubble_gtk.h',
'browser/gtk/tab_contents_container_gtk.cc',
'browser/gtk/tab_contents_container_gtk.h',
+ 'browser/gtk/tabs/dragged_tab_controller_gtk.cc',
+ 'browser/gtk/tabs/dragged_tab_controller_gtk.h',
'browser/gtk/tabs/tab_button_gtk.cc',
'browser/gtk/tabs/tab_button_gtk.h',
'browser/gtk/tabs/tab_gtk.cc',