summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-18 21:08:48 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-18 21:08:48 +0000
commit43b7725b6686228f86ef1306703e6dade09dcc27 (patch)
tree638b550d39cc864af5c470f0259c7fe99671435c /chrome/browser
parent606d7469d6da2cc63a7188b75059bd6e5fe1f82f (diff)
downloadchromium_src-43b7725b6686228f86ef1306703e6dade09dcc27.zip
chromium_src-43b7725b6686228f86ef1306703e6dade09dcc27.tar.gz
chromium_src-43b7725b6686228f86ef1306703e6dade09dcc27.tar.bz2
Implement DraggedTabGtk, the object that handles rendering either a dragged tab or tab contents during a tab drag.
Review URL: http://codereview.chromium.org/113532 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16310 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc249
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h54
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_gtk.cc173
-rw-r--r--chrome/browser/gtk/tabs/dragged_tab_gtk.h108
-rw-r--r--chrome/browser/gtk/tabs/tab_gtk.cc52
-rw-r--r--chrome/browser/gtk/tabs/tab_gtk.h9
-rw-r--r--chrome/browser/gtk/tabs/tab_renderer_gtk.cc21
-rw-r--r--chrome/browser/gtk/tabs/tab_renderer_gtk.h12
-rw-r--r--chrome/browser/gtk/tabs/tab_strip_gtk.cc1
9 files changed, 620 insertions, 59 deletions
diff --git a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc
index 3650b27..1b64b78 100644
--- a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc
+++ b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.cc
@@ -4,9 +4,11 @@
#include "chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h"
+#include "chrome/browser/gtk/tabs/dragged_tab_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/gtk_util.h"
#include "chrome/common/notification_service.h"
namespace {
@@ -25,22 +27,41 @@ DraggedTabControllerGtk::DraggedTabControllerGtk(TabGtk* source_tab,
source_tabstrip_(source_tabstrip),
source_model_index_(source_tabstrip->GetIndexOfTab(source_tab)),
attached_tabstrip_(source_tabstrip),
+ in_destructor_(false),
last_move_screen_x_(0) {
SetDraggedContents(
source_tabstrip_->model()->GetTabContentsAt(source_model_index_));
}
DraggedTabControllerGtk::~DraggedTabControllerGtk() {
+ in_destructor_ = true;
+ // Need to delete the dragged tab here manually _before_ we reset the dragged
+ // contents to NULL, otherwise if the view is animating to its destination
+ // bounds, it won't be able to clean up properly since its cleanup routine
+ // uses GetIndexForDraggedContents, which will be invalid.
+ dragged_tab_.reset();
+ SetDraggedContents(NULL);
}
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();
+ if (!source_tab_)
+ return;
+
+ // Before we get to dragging anywhere, ensure that we consider ourselves
+ // attached to the source tabstrip.
+ if (source_tab_->IsVisible()) {
+ Attach(source_tabstrip_, gfx::Point());
+ }
+
+ if (!source_tab_->IsVisible()) {
+ // TODO(jhawkins): Save focus.
+ ContinueDragging();
+ }
}
bool DraggedTabControllerGtk::EndDrag(bool canceled) {
@@ -66,6 +87,8 @@ void DraggedTabControllerGtk::OpenURLFromTab(TabContents* source,
void DraggedTabControllerGtk::NavigationStateChanged(const TabContents* source,
unsigned changed_flags) {
+ if (dragged_tab_.get())
+ dragged_tab_->Update();
}
void DraggedTabControllerGtk::AddNewContents(TabContents* source,
@@ -88,6 +111,10 @@ void DraggedTabControllerGtk::ActivateContents(TabContents* contents) {
}
void DraggedTabControllerGtk::LoadingStateChanged(TabContents* source) {
+ // TODO(jhawkins): It would be nice to respond to this message by changing the
+ // screen shot in the dragged tab.
+ if (dragged_tab_.get())
+ dragged_tab_->Update();
}
void DraggedTabControllerGtk::CloseContents(TabContents* source) {
@@ -132,6 +159,10 @@ void DraggedTabControllerGtk::Observe(NotificationType type,
EndDragImpl(TAB_DESTROYED);
}
+void DraggedTabControllerGtk::InitWindowCreatePoint() {
+ window_create_point_.SetPoint(mouse_offset_.x(), mouse_offset_.y());
+}
+
void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) {
if (dragged_contents_) {
registrar_.Remove(this,
@@ -157,6 +188,8 @@ void DraggedTabControllerGtk::SetDraggedContents(TabContents* new_contents) {
}
void DraggedTabControllerGtk::ContinueDragging() {
+ EnsureDraggedTab();
+
// 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.
@@ -165,7 +198,7 @@ void DraggedTabControllerGtk::ContinueDragging() {
}
void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) {
- gfx::Point dragged_point = GetDraggedPoint(screen_point);
+ gfx::Point dragged_tab_point = GetDraggedTabPoint(screen_point);
if (attached_tabstrip_) {
// Determine the horizontal move threshold. This is dependent on the width
@@ -183,23 +216,18 @@ void DraggedTabControllerGtk::MoveTab(const gfx::Point& screen_point) {
TabStripModel* attached_model = attached_tabstrip_->model();
int from_index =
attached_model->GetIndexOfTabContents(dragged_contents_);
- gfx::Rect bounds = source_tab_->bounds();
+ gfx::Rect bounds = GetDraggedTabTabStripBounds(dragged_tab_point);
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());
+ // Move the dragged tab. There are no changes to the model if we're detached.
+ dragged_tab_->MoveTo(dragged_tab_point);
}
TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint(
@@ -208,6 +236,91 @@ TabStripGtk* DraggedTabControllerGtk::GetTabStripForPoint(
return source_tabstrip_;
}
+void DraggedTabControllerGtk::Attach(TabStripGtk* attached_tabstrip,
+ const gfx::Point& screen_point) {
+ attached_tabstrip_ = attached_tabstrip;
+ InitWindowCreatePoint();
+ attached_tabstrip_->GenerateIdealBounds();
+
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_);
+
+ // Update the tab first, so we can ask it for its bounds and determine
+ // where to insert the hidden tab.
+
+ // If this is the first time Attach is called for this drag, we're attaching
+ // to the source tabstrip, and we should assume the tab count already
+ // includes this tab since we haven't been detached yet. If we don't do this,
+ // the dragged representation will be a different size to others in the
+ // tabstrip.
+ int tab_count = attached_tabstrip_->GetTabCount();
+ if (!tab)
+ ++tab_count;
+ double unselected_width = 0, selected_width = 0;
+ attached_tabstrip_->GetDesiredTabWidths(tab_count, &unselected_width,
+ &selected_width);
+ EnsureDraggedTab();
+ dragged_tab_->Attach(static_cast<int>(selected_width));
+
+ if (!tab) {
+ // There is no tab in |attached_tabstrip| that corresponds to the dragged
+ // TabContents. We must now create one.
+
+ // Remove ourselves as the delegate now that the dragged TabContents is
+ // being inserted back into a Browser.
+ dragged_contents_->set_delegate(NULL);
+ original_delegate_ = NULL;
+
+ // Return the TabContents' to normalcy.
+ dragged_contents_->set_capturing_contents(false);
+
+ // We need to ask the tabstrip we're attached to ensure that the ideal
+ // bounds for all its tabs are correctly generated, because the calculation
+ // in GetInsertionIndexForDraggedBounds needs them to be to figure out the
+ // appropriate insertion index.
+ attached_tabstrip_->GenerateIdealBounds();
+
+ // Inserting counts as a move. We don't want the tabs to jitter when the
+ // user moves the tab immediately after attaching it.
+ last_move_screen_x_ = screen_point.x();
+
+ // Figure out where to insert the tab based on the bounds of the dragged
+ // representation and the ideal bounds of the other tabs already in the
+ // strip. ("ideal bounds" are stable even if the tabs' actual bounds are
+ // changing due to animation).
+ gfx::Rect bounds = GetDraggedTabTabStripBounds(screen_point);
+ int index = GetInsertionIndexForDraggedBounds(bounds);
+ index = std::max(std::min(index, attached_tabstrip_->model()->count()), 0);
+ attached_tabstrip_->model()->InsertTabContentsAt(index, dragged_contents_,
+ true, false);
+
+ tab = GetTabMatchingDraggedContents(attached_tabstrip_);
+ }
+ DCHECK(tab); // We should now have a tab.
+ tab->SetVisible(false);
+
+ // TODO(jhawkins): Move the corresponding window to the front.
+}
+
+void DraggedTabControllerGtk::Detach() {
+ // TODO(jhawkins): Detach the dragged tab.
+}
+
+gfx::Point DraggedTabControllerGtk::ConvertScreenPointToTabStripPoint(
+ TabStripGtk* tabstrip, const gfx::Point& screen_point) {
+ gint x, y;
+ gdk_window_get_origin(tabstrip->tabstrip_.get()->window, &x, &y);
+ return gfx::Point(screen_point.x() - x, screen_point.y() - y);
+}
+
+gfx::Rect DraggedTabControllerGtk::GetDraggedTabTabStripBounds(
+ const gfx::Point& screen_point) {
+ gfx::Point client_point =
+ ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point);
+ gfx::Size tab_size = dragged_tab_->attached_tab_size();
+ return gfx::Rect(client_point.x(), client_point.y(),
+ tab_size.width(), tab_size.height());
+}
+
int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds(
const gfx::Rect& dragged_bounds) const {
int right_tab_x = 0;
@@ -243,19 +356,41 @@ int DraggedTabControllerGtk::GetInsertionIndexForDraggedBounds(
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;
+gfx::Point DraggedTabControllerGtk::GetDraggedTabPoint(
+ const gfx::Point& screen_point) {
+ int x = screen_point.x() - mouse_offset_.x();
+ int y = screen_point.y() - mouse_offset_.y();
+ // If we're not attached, we just use x and y from above.
+ if (attached_tabstrip_) {
+ gfx::Rect tabstrip_bounds =
+ gtk_util::GetWidgetScreenBounds(attached_tabstrip_->tabstrip_.get());
+ // Snap the dragged tab to the tabstrip if we are attached, detaching
+ // only when the mouse position (screen_point) exceeds the screen bounds
+ // of the tabstrip.
+ if (x < tabstrip_bounds.x() && screen_point.x() >= tabstrip_bounds.x())
+ x = tabstrip_bounds.x();
+
+ gfx::Size tab_size = dragged_tab_->attached_tab_size();
+ int vertical_drag_magnetism = tab_size.height() * 2;
+ int vertical_detach_point = tabstrip_bounds.y() - vertical_drag_magnetism;
+ if (y < tabstrip_bounds.y() && screen_point.y() >= vertical_detach_point)
+ y = tabstrip_bounds.y();
+
+ // Make sure the tab can't be dragged off the right side of the tabstrip
+ // unless the mouse pointer passes outside the bounds of the strip by
+ // clamping the position of the dragged window to the tabstrip width less
+ // the width of one tab until the mouse pointer (screen_point) exceeds the
+ // screen bounds of the tabstrip.
+ int max_x = tabstrip_bounds.right() - tab_size.width();
+ int max_y = tabstrip_bounds.bottom() - tab_size.height();
+ if (x > max_x && screen_point.x() <= tabstrip_bounds.right())
+ x = max_x;
+ if (y > max_y && screen_point.y() <=
+ (tabstrip_bounds.bottom() + vertical_drag_magnetism)) {
+ y = max_y;
+ }
+ }
return gfx::Point(x, y);
}
@@ -318,7 +453,7 @@ void DraggedTabControllerGtk::RevertDrag() {
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
+ // 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
@@ -327,7 +462,7 @@ void DraggedTabControllerGtk::RevertDrag() {
source_tabstrip_->model()->InsertTabContentsAt(source_model_index_,
dragged_contents_, true, false);
} else {
- // The Tab was moved within the TabStrip where the drag was initiated.
+ // 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);
@@ -336,26 +471,37 @@ void DraggedTabControllerGtk::RevertDrag() {
// 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.
+ // 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);
}
+
+ source_tab_->SetVisible(true);
}
bool DraggedTabControllerGtk::CompleteDrag() {
- // We don't need to do anything other than make the Tab visible again,
+ // 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());
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_);
+ gfx::Rect rect = GetTabScreenBounds(tab);
+ dragged_tab_->AnimateToBounds(GetTabScreenBounds(tab),
+ NewCallback(this, &DraggedTabControllerGtk::OnAnimateToBoundsComplete));
return false;
}
+void DraggedTabControllerGtk::EnsureDraggedTab() {
+ if (!dragged_tab_.get()) {
+ gfx::Rect rect;
+ dragged_contents_->GetContainerBounds(&rect);
+
+ dragged_tab_.reset(new DraggedTabGtk(dragged_contents_, mouse_offset_,
+ gfx::Size(rect.width(), rect.height())));
+ }
+}
+
gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const {
// Get default display and screen.
GdkDisplay* display = gdk_display_get_default();
@@ -366,3 +512,38 @@ gfx::Point DraggedTabControllerGtk::GetCursorScreenPoint() const {
return gfx::Point(x, y);
}
+
+// static
+gfx::Rect DraggedTabControllerGtk::GetTabScreenBounds(TabGtk* tab) {
+ // A hidden widget moved with gtk_fixed_move in a GtkFixed container doesn't
+ // update its allocation until after the widget is shown, so we have to use
+ // the tab bounds we keep track of.
+ int x, y;
+ x = tab->bounds().x();
+ y = tab->bounds().y();
+
+ GtkWidget* widget = tab->widget();
+ GtkWidget* parent = gtk_widget_get_parent(widget);
+ gfx::Point point = gtk_util::GetWidgetScreenPosition(parent);
+ x += point.x();
+ y += point.y();
+
+ return gfx::Rect(x, y, widget->allocation.width, widget->allocation.height);
+}
+
+void DraggedTabControllerGtk::OnAnimateToBoundsComplete() {
+ // Sometimes, for some reason, in automation we can be called back on a
+ // detach even though we aren't attached to a tabstrip. Guard against that.
+ if (attached_tabstrip_) {
+ TabGtk* tab = GetTabMatchingDraggedContents(attached_tabstrip_);
+ if (tab) {
+ tab->SetVisible(true);
+ // Paint the tab now, otherwise there may be slight flicker between the
+ // time the dragged tab window is destroyed and we paint.
+ tab->SchedulePaint();
+ }
+ }
+
+ if (!in_destructor_)
+ source_tabstrip_->DestroyDragController();
+}
diff --git a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h
index 5381b38..29ae2b7 100644
--- a/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h
+++ b/chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h
@@ -5,10 +5,14 @@
#ifndef CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
#define CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
+#include <gtk/gtk.h>
+
+#include "base/scoped_ptr.h"
#include "base/timer.h"
#include "chrome/browser/tab_contents/tab_contents_delegate.h"
#include "chrome/common/notification_registrar.h"
+class DraggedTabGtk;
class TabGtk;
class TabStripGtk;
@@ -74,6 +78,10 @@ class DraggedTabControllerGtk : public NotificationObserver,
const NotificationSource& source,
const NotificationDetails& details);
+ // Initialize the offset used to calculate the position to create windows
+ // in |GetWindowCreatePoint|.
+ void InitWindowCreatePoint();
+
// Sets the TabContents being dragged with the specified |new_contents|.
void SetDraggedContents(TabContents* new_contents);
@@ -88,13 +96,27 @@ class DraggedTabControllerGtk : public NotificationObserver,
// coordinates), or NULL if there is none.
TabStripGtk* GetTabStripForPoint(const gfx::Point& screen_point);
+ // Attach the dragged Tab to the specified TabStrip.
+ void Attach(TabStripGtk* attached_tabstrip, const gfx::Point& screen_point);
+
+ // Detach the dragged Tab from the current TabStrip.
+ void Detach();
+
+ // Converts a screen point to a point relative to the tab strip.
+ gfx::Point ConvertScreenPointToTabStripPoint(TabStripGtk* tabstrip,
+ const gfx::Point& screen_point);
+
+ // Retrieve the bounds of the DraggedTabGtk, relative to the attached
+ // TabStrip, given location of the dragged tab in screen coordinates.
+ gfx::Rect GetDraggedTabTabStripBounds(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);
+ gfx::Point GetDraggedTabPoint(const gfx::Point& screen_point);
// Finds the Tab within the specified TabStrip that corresponds to the
// dragged TabContents.
@@ -111,13 +133,24 @@ class DraggedTabControllerGtk : public NotificationObserver,
// be destroyed immediately, false otherwise.
bool CompleteDrag();
+ // Create the DraggedTabGtk if it does not yet exist.
+ void EnsureDraggedTab();
+
// Utility for getting the mouse position in screen coordinates.
gfx::Point GetCursorScreenPoint() const;
+ // Gets the screen bounds of a tab.
+ static gfx::Rect GetTabScreenBounds(TabGtk* tab);
+
// Utility to convert the specified TabStripModel index to something valid
// for the attached TabStrip.
int NormalizeIndexToAttachedTabStrip(int index) const;
+ // Completes the drag session after the view has animated to its final
+ // position.
+ void OnAnimateToBoundsComplete();
+
+ // Activates whichever window is under the mouse.
void BringWindowUnderMouseToFront();
// Handles registering for notifications.
@@ -145,6 +178,9 @@ class DraggedTabControllerGtk : public NotificationObserver,
// dragged Tab is detached.
TabStripGtk* attached_tabstrip_;
+ // The visual representation of the dragged Tab.
+ scoped_ptr<DraggedTabGtk> dragged_tab_;
+
// 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.
@@ -156,16 +192,20 @@ class DraggedTabControllerGtk : public NotificationObserver,
// detached window is created at the right location.
gfx::Point mouse_offset_;
+ // A hint to use when positioning new windows created by detaching Tabs. This
+ // is the distance of the mouse from the top left of the dragged tab as if it
+ // were the distance of the mouse from the top left of the first tab in the
+ // attached TabStrip from the top left of the window.
+ gfx::Point window_create_point_;
+
+ // Whether we're in the destructor or not. Makes sure we don't destroy the
+ // drag controller more than once.
+ bool in_destructor_;
+
// 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.
diff --git a/chrome/browser/gtk/tabs/dragged_tab_gtk.cc b/chrome/browser/gtk/tabs/dragged_tab_gtk.cc
new file mode 100644
index 0000000..6caa70c0
--- /dev/null
+++ b/chrome/browser/gtk/tabs/dragged_tab_gtk.cc
@@ -0,0 +1,173 @@
+// 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_gtk.h"
+
+#include <gdk/gdk.h>
+
+#include "app/gfx/canvas.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/gtk/tabs/tab_renderer_gtk.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace {
+
+// The size of the dragged window frame.
+const int kDragFrameBorderSize = 2;
+const int kTwiceDragFrameBorderSize = 2 * kDragFrameBorderSize;
+
+// Used to scale the dragged window sizes.
+const float kScalingFactor = 0.5;
+
+const int kAnimateToBoundsDurationMs = 150;
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// DraggedTabGtk, public:
+
+DraggedTabGtk::DraggedTabGtk(TabContents* datasource,
+ const gfx::Point& mouse_tab_offset,
+ const gfx::Size& contents_size)
+ : renderer_(new TabRendererGtk),
+ attached_(false),
+ mouse_tab_offset_(mouse_tab_offset),
+ attached_tab_size_(TabRendererGtk::GetMinimumSelectedSize()),
+ contents_size_(contents_size),
+ close_animation_(this) {
+ renderer_->UpdateData(datasource, false);
+
+ container_ = gtk_window_new(GTK_WINDOW_POPUP);
+ SetContainerColorMap();
+ gtk_widget_set_app_paintable(container_, TRUE);
+ g_signal_connect(G_OBJECT(container_), "expose-event",
+ G_CALLBACK(OnExpose), this);
+ gtk_container_add(GTK_CONTAINER(container_), renderer_->widget());
+ gtk_widget_show_all(container_);
+}
+
+DraggedTabGtk::~DraggedTabGtk() {
+ gtk_widget_destroy(container_);
+}
+
+void DraggedTabGtk::MoveTo(const gfx::Point& screen_point) {
+ int x = screen_point.x() + mouse_tab_offset_.x() -
+ ScaleValue(mouse_tab_offset_.x());
+ int y = screen_point.y() + mouse_tab_offset_.y() -
+ ScaleValue(mouse_tab_offset_.y());
+
+ gtk_window_move(GTK_WINDOW(container_), x, y);
+}
+
+void DraggedTabGtk::Attach(int selected_width) {
+ attached_ = true;
+ attached_tab_size_.set_width(selected_width);
+ ResizeContainer();
+ Update();
+}
+
+void DraggedTabGtk::Update() {
+ gtk_widget_queue_draw(container_);
+}
+
+void DraggedTabGtk::AnimateToBounds(const gfx::Rect& bounds,
+ AnimateToBoundsCallback* callback) {
+ animation_callback_.reset(callback);
+
+ gint x, y, width, height;
+ gdk_window_get_origin(container_->window, &x, &y);
+ gdk_window_get_geometry(container_->window, NULL, NULL,
+ &width, &height, NULL);
+
+ animation_start_bounds_ = gfx::Rect(x, y, width, height);
+ animation_end_bounds_ = bounds;
+
+ close_animation_.SetSlideDuration(kAnimateToBoundsDurationMs);
+ close_animation_.SetTweenType(SlideAnimation::EASE_OUT);
+ if (!close_animation_.IsShowing()) {
+ close_animation_.Reset();
+ close_animation_.Show();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DraggedTabGtk, AnimationDelegate implementation:
+
+void DraggedTabGtk::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();
+ gdk_window_move(container_->window, x, y);
+}
+
+void DraggedTabGtk::AnimationEnded(const Animation* animation) {
+ animation_callback_->Run();
+}
+
+void DraggedTabGtk::AnimationCanceled(const Animation* animation) {
+ AnimationEnded(animation);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DraggedTabGtk, private:
+
+gfx::Size DraggedTabGtk::GetPreferredSize() {
+ if (attached_)
+ return attached_tab_size_;
+
+ int width = std::max(attached_tab_size_.width(), contents_size_.width()) +
+ kTwiceDragFrameBorderSize;
+ int height = attached_tab_size_.height() + kDragFrameBorderSize +
+ contents_size_.height();
+ return gfx::Size(width, height);
+}
+
+void DraggedTabGtk::ResizeContainer() {
+ gfx::Size size = GetPreferredSize();
+ gtk_window_resize(GTK_WINDOW(container_),
+ ScaleValue(size.width()), ScaleValue(size.height()));
+ gfx::Rect bounds = renderer_->bounds();
+ bounds.set_width(ScaleValue(size.width()));
+ bounds.set_height(ScaleValue(size.height()));
+ renderer_->SetBounds(bounds);
+ Update();
+}
+
+int DraggedTabGtk::ScaleValue(int value) {
+ return attached_ ? value : static_cast<int>(value * kScalingFactor);
+}
+
+void DraggedTabGtk::SetContainerColorMap() {
+ GdkScreen* screen = gtk_widget_get_screen(container_);
+ GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
+
+ // If rgba is not available, use rgb instead.
+ if (!colormap)
+ colormap = gdk_screen_get_rgb_colormap(screen);
+
+ gtk_widget_set_colormap(container_, colormap);
+}
+
+// static
+gboolean DraggedTabGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event,
+ DraggedTabGtk* dragged_tab) {
+ cairo_t* cairo_context = gdk_cairo_create(widget->window);
+ if (!cairo_context)
+ return FALSE;
+
+ // Make the background of the dragged tab window fully transparent. All of
+ // the content of the window (child widgets) will be completely opaque.
+ gint width, height;
+ gtk_window_get_size(GTK_WINDOW(widget), &width, &height);
+ cairo_scale(cairo_context, static_cast<double>(width),
+ static_cast<double>(height));
+ cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f);
+ cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
+ cairo_paint(cairo_context);
+ cairo_destroy(cairo_context);
+
+ return FALSE;
+}
diff --git a/chrome/browser/gtk/tabs/dragged_tab_gtk.h b/chrome/browser/gtk/tabs/dragged_tab_gtk.h
new file mode 100644
index 0000000..3a65b2d
--- /dev/null
+++ b/chrome/browser/gtk/tabs/dragged_tab_gtk.h
@@ -0,0 +1,108 @@
+// 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_GTK_H_
+#define CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_GTK_H_
+
+#include <gtk/gtk.h>
+
+#include "app/slide_animation.h"
+#include "base/gfx/point.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "chrome/common/owned_widget_gtk.h"
+
+class ChromeCanvas;
+class TabContents;
+class TabRendererGtk;
+
+class DraggedTabGtk : public AnimationDelegate {
+ public:
+ DraggedTabGtk(TabContents* datasource,
+ const gfx::Point& mouse_tab_offset,
+ const gfx::Size& contents_size);
+ virtual ~DraggedTabGtk();
+
+ // Moves the DraggedTabView to the appropriate location given the mouse
+ // pointer at |screen_point|.
+ void MoveTo(const gfx::Point& screen_point);
+
+ // Notifies the DraggedTabView that it has become attached to a TabStrip.
+ void Attach(int selected_width);
+
+ // Notifies the DraggedTabView that it should update itself.
+ void Update();
+
+ // Animates the DraggedTabView to the specified bounds, then calls back to
+ // |callback|.
+ typedef Callback0::Type AnimateToBoundsCallback;
+ void AnimateToBounds(const gfx::Rect& bounds,
+ AnimateToBoundsCallback* callback);
+
+ // Returns the size of the DraggedTabView. Used when attaching to a TabStrip
+ // to determine where to place the Tab in the attached TabStrip.
+ gfx::Size attached_tab_size() const { return attached_tab_size_; }
+
+ private:
+ // Overridden from AnimationDelegate:
+ virtual void AnimationProgressed(const Animation* animation);
+ virtual void AnimationEnded(const Animation* animation);
+ virtual void AnimationCanceled(const Animation* animation);
+
+ // Gets the preferred size of the dragged tab.
+ virtual gfx::Size GetPreferredSize();
+
+ // Resizes the container to fit the content for the current attachment mode.
+ void ResizeContainer();
+
+ // Utility for scaling a size by the current scaling factor.
+ int ScaleValue(int value);
+
+ // Sets the color map of the container window to allow the window to be
+ // transparent.
+ void SetContainerColorMap();
+
+ // expose-event handler that redraws the dragged tab.
+ static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* event,
+ DraggedTabGtk* dragged_tab);
+
+ // The window that contains the dragged tab or tab contents.
+ GtkWidget* container_;
+
+ // The renderer that paints the dragged tab.
+ scoped_ptr<TabRendererGtk> renderer_;
+
+ // True if the view is currently attached to a TabStrip. Controls rendering
+ // and sizing modes.
+ bool attached_;
+
+ // The unscaled offset of the mouse from the top left of the dragged Tab.
+ // This is used to maintain an appropriate offset for the mouse pointer when
+ // dragging scaled and unscaled representations, and also to calculate the
+ // position of detached windows.
+ gfx::Point mouse_tab_offset_;
+
+ // The desired width of the TabRenderer when the DraggedTabView is attached
+ // to a TabStrip.
+ gfx::Size attached_tab_size_;
+
+ // The dimensions of the TabContents being dragged.
+ gfx::Size contents_size_;
+
+ // The animation used to slide the attached view to its final location.
+ SlideAnimation close_animation_;
+
+ // A callback notified when the animation is complete.
+ scoped_ptr<Callback0::Type> animation_callback_;
+
+ // The start and end bounds of the animation sequence.
+ gfx::Rect animation_start_bounds_;
+ gfx::Rect animation_end_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(DraggedTabGtk);
+};
+
+#endif // CHROME_BROWSER_GTK_TABS_DRAGGED_TAB_GTK_H_
diff --git a/chrome/browser/gtk/tabs/tab_gtk.cc b/chrome/browser/gtk/tabs/tab_gtk.cc
index 2215015..3d85cda 100644
--- a/chrome/browser/gtk/tabs/tab_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_gtk.cc
@@ -100,16 +100,13 @@ class TabGtk::ContextMenuController : public MenuGtk::Delegate {
TabGtk::TabGtk(TabDelegate* delegate)
: TabRendererGtk(),
delegate_(delegate),
- closing_(false) {
+ closing_(false),
+ dragging_(false) {
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",
@@ -119,13 +116,11 @@ TabGtk::TabGtk(TabDelegate* delegate)
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_CALLBACK(OnDragBegin), this);
g_signal_connect_after(G_OBJECT(event_box_.get()), "drag-end",
- G_CALLBACK(&OnDragEnd), this);
+ 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);
+ G_CALLBACK(OnDragFailed), this);
gtk_widget_add_events(event_box_.get(),
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
GDK_LEAVE_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
@@ -148,6 +143,17 @@ TabGtk::~TabGtk() {
}
// static
+void TabGtk::GdkEventHandler(GdkEvent* event, void* data) {
+ TabGtk* tab = static_cast<TabGtk*>(data);
+
+ if (event->type == GDK_MOTION_NOTIFY && tab && tab->dragging_) {
+ tab->delegate_->ContinueDrag(NULL);
+ }
+
+ gtk_main_do_event(event);
+}
+
+// static
gboolean TabGtk::OnMousePress(GtkWidget* widget, GdkEventButton* event,
TabGtk* tab) {
if (event->button == 1) {
@@ -192,17 +198,29 @@ gboolean TabGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event,
// static
void TabGtk::OnDragBegin(GtkWidget* widget, GdkDragContext* context,
TabGtk* tab) {
+ gdk_event_handler_set(TabGtk::GdkEventHandler, tab, NULL);
+
int x, y;
gdk_window_get_pointer(tab->event_box_.get()->window, &x, &y, NULL);
+
+ // Make the mouse coordinate relative to the tab.
+ x -= tab->bounds().x();
+ y -= tab->bounds().y();
+
+ tab->dragging_ = true;
tab->delegate_->MaybeStartDrag(tab, gfx::Point(x, y));
}
// static
void TabGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context,
TabGtk* tab) {
+ tab->dragging_ = false;
// 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);
+
+ // Reset the user data pointer for our event handler.
+ gdk_event_handler_set(TabGtk::GdkEventHandler, NULL, NULL);
}
// static
@@ -219,7 +237,7 @@ gboolean TabGtk::OnDragMotion(GtkWidget* widget,
gboolean TabGtk::OnDragFailed(GtkWidget* widget, GdkDragContext* context,
GtkDragResult result,
TabGtk* tab) {
- tab->delegate_->EndDrag(true);
+ tab->delegate_->EndDrag(false);
return TRUE;
}
@@ -230,6 +248,18 @@ bool TabGtk::IsSelected() const {
return delegate_->IsTabSelected(this);
}
+bool TabGtk::IsVisible() const {
+ return GTK_WIDGET_FLAGS(event_box_.get()) & GTK_VISIBLE;
+}
+
+void TabGtk::SetVisible(bool visible) const {
+ if (visible) {
+ gtk_widget_show(event_box_.get());
+ } else {
+ gtk_widget_hide(event_box_.get());
+ }
+}
+
void TabGtk::CloseButtonResized(const gfx::Rect& bounds) {
gtk_fixed_move(GTK_FIXED(TabRendererGtk::widget()),
close_button_.get()->widget(), bounds.x(), bounds.y());
diff --git a/chrome/browser/gtk/tabs/tab_gtk.h b/chrome/browser/gtk/tabs/tab_gtk.h
index 2999564..044718f 100644
--- a/chrome/browser/gtk/tabs/tab_gtk.h
+++ b/chrome/browser/gtk/tabs/tab_gtk.h
@@ -80,9 +80,15 @@ class TabGtk : public TabRendererGtk {
// TabRendererGtk overrides:
virtual bool IsSelected() const;
+ virtual bool IsVisible() const;
+ virtual void SetVisible(bool visible) const;
virtual void CloseButtonResized(const gfx::Rect& bounds);
virtual void Paint(GdkEventExpose* event);
+ // The callback that is called for every gdk event. We use it to inspect for
+ // drag-motion events when the drag is outside of the source tab.
+ static void GdkEventHandler(GdkEvent* event, void* tab);
+
// button-press-event handler that handles mouse clicks.
static gboolean OnMousePress(GtkWidget* widget, GdkEventButton* event,
TabGtk* tab);
@@ -147,6 +153,9 @@ class TabGtk : public TabRendererGtk {
// The windowless widget used to collect input events for the tab.
OwnedWidgetGtk event_box_;
+ // True if this tab is being dragged.
+ bool dragging_;
+
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 371aa40..07d644f 100644
--- a/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.cc
@@ -125,11 +125,6 @@ void TabRendererGtk::LoadingAnimation::ValidateLoadingAnimation(
}
}
-bool TabRendererGtk::IsVisible() {
- // TODO(jhawkins): Implement this and SetVisible.
- return true;
-}
-
////////////////////////////////////////////////////////////////////////////////
// TabRendererGtk, public:
@@ -183,6 +178,18 @@ bool TabRendererGtk::IsSelected() const {
return true;
}
+bool TabRendererGtk::IsVisible() const {
+ return GTK_WIDGET_FLAGS(tab_.get()) & GTK_VISIBLE;
+}
+
+void TabRendererGtk::SetVisible(bool visible) const {
+ if (visible) {
+ gtk_widget_show(tab_.get());
+ } else {
+ gtk_widget_hide(tab_.get());
+ }
+}
+
void TabRendererGtk::CloseButtonResized(const gfx::Rect& bounds) {
// Nothing to do.
}
@@ -337,6 +344,10 @@ void TabRendererGtk::Paint(GdkEventExpose* event) {
title_bounds_.height());
}
+void TabRendererGtk::SchedulePaint() {
+ gtk_widget_queue_draw(tab_.get());
+}
+
void TabRendererGtk::Layout() {
gfx::Rect local_bounds = bounds_;
if (local_bounds.IsEmpty())
diff --git a/chrome/browser/gtk/tabs/tab_renderer_gtk.h b/chrome/browser/gtk/tabs/tab_renderer_gtk.h
index ef3a9b4..c449a02 100644
--- a/chrome/browser/gtk/tabs/tab_renderer_gtk.h
+++ b/chrome/browser/gtk/tabs/tab_renderer_gtk.h
@@ -84,18 +84,26 @@ class TabRendererGtk : public AnimationDelegate {
// Returns true if the Tab is selected, false otherwise.
virtual bool IsSelected() const;
+ // Returns true if the Tab is visible, false otherwise.
+ virtual bool IsVisible() const;
+
+ // Sets the visibility of the Tab.
+ virtual void SetVisible(bool visible) const;
+
// Notifies subclasses that the close button has been resized to |bounds|.
virtual void CloseButtonResized(const gfx::Rect& bounds);
// Paints the tab into |canvas|.
virtual void Paint(GdkEventExpose* event);
+ // There is no PaintNow available, so the fastest we can do is schedule a
+ // paint with the windowing system.
+ virtual void SchedulePaint();
+
// 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
diff --git a/chrome/browser/gtk/tabs/tab_strip_gtk.cc b/chrome/browser/gtk/tabs/tab_strip_gtk.cc
index 4e49e2f..6d9ae73 100644
--- a/chrome/browser/gtk/tabs/tab_strip_gtk.cc
+++ b/chrome/browser/gtk/tabs/tab_strip_gtk.cc
@@ -859,6 +859,7 @@ void TabStripGtk::GetDesiredTabWidths(int tab_count,
(static_cast<double>(available_width - total_offset) /
static_cast<double>(tab_count)),
static_cast<double>(TabGtk::GetStandardSize().width()));
+
*unselected_width = std::max(desired_tab_width, min_unselected_width);
*selected_width = std::max(desired_tab_width, min_selected_width);