diff options
author | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-18 21:08:48 +0000 |
---|---|---|
committer | jhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-18 21:08:48 +0000 |
commit | 43b7725b6686228f86ef1306703e6dade09dcc27 (patch) | |
tree | 638b550d39cc864af5c470f0259c7fe99671435c /chrome/browser | |
parent | 606d7469d6da2cc63a7188b75059bd6e5fe1f82f (diff) | |
download | chromium_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.cc | 249 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/dragged_tab_controller_gtk.h | 54 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/dragged_tab_gtk.cc | 173 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/dragged_tab_gtk.h | 108 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_gtk.cc | 52 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_gtk.h | 9 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_renderer_gtk.cc | 21 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_renderer_gtk.h | 12 | ||||
-rw-r--r-- | chrome/browser/gtk/tabs/tab_strip_gtk.cc | 1 |
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); |