Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3379)

Unified Diff: chrome/browser/views/tabs/tab_strip_2.cc

Issue 42490: Experiments with a new tabstrip + animator (Closed) Base URL: svn://chrome-svn.corp.google.com/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/views/tabs/tab_strip_2.h ('k') | chrome/chrome.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/views/tabs/tab_strip_2.cc
===================================================================
--- chrome/browser/views/tabs/tab_strip_2.cc (revision 0)
+++ chrome/browser/views/tabs/tab_strip_2.cc (revision 0)
@@ -0,0 +1,406 @@
+// 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/views/tabs/tab_strip_2.h"
+
+#include "app/gfx/canvas.h"
+#include "app/slide_animation.h"
+#include "app/win_util.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "chrome/common/chrome_switches.h"
+#include "views/animator.h"
+#include "views/screen.h"
+#include "views/widget/widget.h"
+#include "views/window/non_client_view.h"
+#include "views/window/window.h"
+
+static const int kHorizontalMoveThreshold = 16; // pixels
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, public:
+
+TabStrip2::TabStrip2(TabStrip2Model* model)
+ : model_(model),
+ last_move_screen_x_(0),
+ detach_factory_(this),
+ drag_start_factory_(this) {
+}
+
+TabStrip2::~TabStrip2() {
+}
+
+// static
+bool TabStrip2::Enabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableTabtastic2);
+}
+
+void TabStrip2::AddTabAt(int index) {
+ Tab2* tab = new Tab2(this);
+ int insertion_index = GetInternalIndex(index);
+ tabs_.insert(tabs_.begin() + insertion_index, tab);
+ AddChildView(insertion_index, tab);
+ LayoutImpl(LS_TAB_ADD);
+}
+
+void TabStrip2::RemoveTabAt(int index, Tab2Model* removing_model) {
+ Tab2* tab = GetTabAt(GetInternalIndex(index));
+
+ DCHECK(!tab->removing());
+ tab->set_removing(true);
+
+ DCHECK(removing_model);
+ tab->SetRemovingModel(removing_model);
+
+ LayoutImpl(LS_TAB_REMOVE);
+}
+
+void TabStrip2::SelectTabAt(int index) {
+ LayoutImpl(LS_TAB_SELECT);
+ SchedulePaint();
+}
+
+void TabStrip2::MoveTabAt(int index, int to_index) {
+ int from_index = GetInternalIndex(index);
+ Tab2* tab = GetTabAt(from_index);
+ tabs_.erase(tabs_.begin() + from_index);
+ tabs_.insert(tabs_.begin() + GetInternalIndex(to_index), tab);
+ LayoutImpl(LS_TAB_DRAG_REORDER);
+}
+
+int TabStrip2::GetTabCount() const {
+ return tabs_.size();
+}
+
+Tab2* TabStrip2::GetTabAt(int index) const {
+ return tabs_.at(index);
+}
+
+int TabStrip2::GetTabIndex(Tab2* tab) const {
+ std::vector<Tab2*>::const_iterator it = find(tabs_.begin(), tabs_.end(), tab);
+ if (it != tabs_.end())
+ return it - tabs_.begin();
+ return -1;
+}
+
+int TabStrip2::GetInsertionIndexForPoint(const gfx::Point& point) const {
+ int tab_count = GetTabCount();
+ for (int i = 0; i < tab_count; ++i) {
+ if (GetTabAt(i)->removing())
+ continue;
+ gfx::Rect tab_bounds = GetTabAt(i)->bounds();
+ gfx::Rect tab_left_half = tab_bounds;
+ tab_left_half.set_width(tab_left_half.width() / 2);
+ if (point.x() >= tab_left_half.x() && point.x() <= tab_left_half.right())
+ return i;
+ gfx::Rect tab_right_half = tab_bounds;
+ tab_right_half.set_x(tab_right_half.width() / 2);
+ tab_right_half.set_width(tab_right_half.x());
+ if (point.x() > tab_right_half.x() && point.x() <= tab_right_half.right())
+ if (tab_right_half.Contains(point))
+ return i + 1;
+ }
+ return tab_count;
+}
+
+gfx::Rect TabStrip2::GetDraggedTabScreenBounds(const gfx::Point& screen_point) {
+ gfx::Point tab_screen_origin(screen_point);
+ tab_screen_origin.Offset(mouse_tab_offset_.x(), mouse_tab_offset_.y());
+ return gfx::Rect(tab_screen_origin, GetTabAt(0)->bounds().size());
+}
+
+void TabStrip2::SetDraggedTabBounds(int index, const gfx::Rect& tab_bounds) {
+ // This function should only ever be called goats
+ Tab2* dragged_tab = GetTabAt(index);
+ dragged_tab->SetBounds(tab_bounds);
+ SchedulePaint();
+}
+
+void TabStrip2::SendDraggedTabHome() {
+ LayoutImpl(LS_TAB_DRAG_REORDER);
+}
+
+void TabStrip2::ResumeDraggingTab(int index, const gfx::Rect& tab_bounds) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ drag_start_factory_.NewRunnableMethod(&TabStrip2::StartDragTabImpl, index,
+ tab_bounds));
+}
+
+// static
+bool TabStrip2::IsDragRearrange(TabStrip2* tabstrip,
+ const gfx::Point& screen_point) {
+ gfx::Point origin;
+ View::ConvertPointToScreen(tabstrip, &origin);
+ gfx::Rect tabstrip_bounds_in_screen_coords(origin, tabstrip->bounds().size());
+ if (tabstrip_bounds_in_screen_coords.Contains(screen_point))
+ return true;
+
+ // The tab is only detached if the tab is moved outside the bounds of the
+ // TabStrip to the left or right, or a certain distance above or below the
+ // TabStrip defined by the vertical detach magnetism below. This is to
+ // prevent accidental detaches when rearranging horizontally.
+ static const int kVerticalDetachMagnetism = 45;
+
+ bool rearrange = true;
+ if (screen_point.x() < tabstrip_bounds_in_screen_coords.right() &&
+ screen_point.x() >= tabstrip_bounds_in_screen_coords.x()) {
+ int lower_threshold =
+ tabstrip_bounds_in_screen_coords.bottom() + kVerticalDetachMagnetism;
+ int upper_threshold =
+ tabstrip_bounds_in_screen_coords.y() - kVerticalDetachMagnetism;
+ return screen_point.y() >= upper_threshold &&
+ screen_point.y() <= lower_threshold;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, Tab2Model implementation:
+
+string16 TabStrip2::GetTitle(Tab2* tab) const {
+ return model_->GetTitle(GetTabIndex(tab));
+}
+
+bool TabStrip2::IsSelected(Tab2* tab) const {
+ return model_->IsSelected(GetTabIndex(tab));
+}
+
+void TabStrip2::SelectTab(Tab2* tab) {
+ model_->SelectTabAt(GetTabIndex(tab));
+}
+
+void TabStrip2::CaptureDragInfo(Tab2* tab,
+ const views::MouseEvent& drag_event) {
+ mouse_tab_offset_ = drag_event.location();
+}
+
+bool TabStrip2::DragTab(Tab2* tab, const views::MouseEvent& drag_event) {
+ if (!model_->CanDragTabs())
+ return false;
+
+ int tab_x = tab->x() + drag_event.location().x() - mouse_tab_offset_.x();
+ if (tab_x < 0)
+ tab_x = 0;
+ if ((tab_x + tab->width()) > bounds().right())
+ tab_x = bounds().right() - tab_x - tab->width();
+ tab->SetBounds(tab_x, tab->y(), tab->width(), tab->height());
+ SchedulePaint();
+
+ int tab_index = GetTabIndex(tab);
+ int dest_index = tab_index;
+
+ Tab2* next_tab = NULL;
+ Tab2* prev_tab = NULL;
+ int next_tab_index = tab_index + 1;
+ if (next_tab_index < GetTabCount())
+ next_tab = GetTabAt(next_tab_index);
+ int prev_tab_index = tab_index - 1;
+ if (prev_tab_index >= 0)
+ prev_tab = GetTabAt(prev_tab_index);
+
+ if (next_tab) {
+ int next_tab_middle_x = next_tab->x() + next_tab->bounds().width() / 2;
+ if (!next_tab->IsAnimating() && tab->bounds().right() > next_tab_middle_x)
+ ++dest_index;
+ }
+ if (prev_tab) {
+ int prev_tab_middle_x = prev_tab->x() + prev_tab->bounds().width() / 2;
+ if (!prev_tab->IsAnimating() && tab->bounds().x() < prev_tab_middle_x)
+ --dest_index;
+ }
+
+ gfx::Point screen_point = views::Screen::GetCursorScreenPoint();
+ if (IsDragRearrange(this, screen_point)) {
+ if (abs(screen_point.x() - last_move_screen_x_) >
+ kHorizontalMoveThreshold) {
+ if (dest_index != tab_index) {
+ last_move_screen_x_ = screen_point.x();
+ model_->MoveTabAt(tab_index, dest_index);
+ }
+ }
+ } else {
+ // We're going to detach. We need to release mouse capture so that further
+ // mouse events will be sent to the appropriate window (the detached window)
+ // and so that we don't recursively create nested message loops (dragging
+ // is done by windows in a nested message loop).
+ ReleaseCapture();
+ MessageLoop::current()->PostTask(FROM_HERE,
+ detach_factory_.NewRunnableMethod(&TabStrip2::DragDetachTabImpl,
+ tab, tab_index));
+ }
+ return true;
+}
+
+void TabStrip2::DragEnded(Tab2* tab) {
+ LayoutImpl(LS_TAB_DRAG_NORMALIZE);
+}
+
+views::AnimatorDelegate* TabStrip2::AsAnimatorDelegate() {
+ return this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, views::View overrides:
+
+gfx::Size TabStrip2::GetPreferredSize() {
+ return gfx::Size(0, 27);
+}
+
+void TabStrip2::Layout() {
+ LayoutImpl(LS_OTHER);
+}
+
+void TabStrip2::Paint(gfx::Canvas* canvas) {
+ canvas->FillRectInt(SK_ColorBLUE, 0, 0, width(), height());
+}
+
+void TabStrip2::PaintChildren(gfx::Canvas* canvas) {
+ // Paint the tabs in reverse order, so they stack to the left.
+ Tab2* selected_tab = NULL;
+ for (int i = GetTabCount() - 1; i >= 0; --i) {
+ Tab2* tab = GetTabAt(i);
+ // We must ask the _Tab's_ model, not ourselves, because in some situations
+ // the model will be different to this object, e.g. when a Tab is being
+ // removed after its TabContents has been destroyed.
+ if (!IsSelected(tab)) {
+ tab->ProcessPaint(canvas);
+ } else {
+ selected_tab = tab;
+ }
+ }
+
+ if (GetWindow()->GetNonClientView()->UseNativeFrame()) {
+ // Make sure unselected tabs are somewhat transparent.
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(200, 255, 255, 255));
+ paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->FillRectInt(
+ 0, 0, width(),
+ height() - 2, // Visible region that overlaps the toolbar.
+ paint);
+ }
+
+ // Paint the selected tab last, so it overlaps all the others.
+ if (selected_tab)
+ selected_tab->ProcessPaint(canvas);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, views::AnimatorDelegate implementation:
+
+views::View* TabStrip2::GetClampedView(views::View* host) {
+ int tab_count = GetTabCount();
+ for (int i = 0; i < tab_count; ++i) {
+ Tab2* tab = GetTabAt(i);
+ if (tab == host && i > 0)
+ return GetTabAt(i - 1);
+ }
+ return NULL;
+}
+
+void TabStrip2::AnimationCompletedForHost(View* host) {
+ Tab2* tab = static_cast<Tab2*>(host);
+ if (tab->removing()) {
+ tabs_.erase(find(tabs_.begin(), tabs_.end(), tab));
+ RemoveChildView(tab);
+ delete tab;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TabStrip2, private:
+
+int TabStrip2::GetAnimateFlagsForLayoutSource(LayoutSource source) const {
+ switch (source) {
+ case LS_TAB_ADD:
+ case LS_TAB_SELECT:
+ case LS_TAB_REMOVE:
+ return views::Animator::ANIMATE_WIDTH | views::Animator::ANIMATE_X |
+ views::Animator::ANIMATE_CLAMP;
+ case LS_TAB_DRAG_REORDER:
+ case LS_TAB_DRAG_NORMALIZE:
+ return views::Animator::ANIMATE_X;
+ }
+ DCHECK(source == LS_OTHER);
+ return views::Animator::ANIMATE_NONE;
+}
+
+void TabStrip2::LayoutImpl(LayoutSource source) {
+ int child_count = GetTabCount();
+ if (child_count > 0) {
+ int child_width = width() / child_count;
+ child_width = std::min(child_width, Tab2::GetStandardSize().width());
+
+ int animate_flags = GetAnimateFlagsForLayoutSource(source);
+ int removing_count = 0;
+ for (int i = 0; i < child_count; ++i) {
+ Tab2* tab = GetTabAt(i);
+ if (tab->removing())
+ ++removing_count;
+ if (!tab->dragging()) {
+ int tab_x = i * child_width - removing_count * child_width;
+ int tab_width = tab->removing() ? 0 : child_width;
+ gfx::Rect new_bounds(tab_x, 0, tab_width, height());
+
+ // Tabs that are currently being removed can have their bounds reset
+ // when another tab in the tabstrip is removed before their remove
+ // animation completes. Before they are given a new target bounds to
+ // animate to, we need to unset the removing property so that they are
+ // not pre-emptively deleted.
+ bool removing = tab->removing();
+ tab->set_removing(false);
+ tab->GetAnimator()->AnimateToBounds(new_bounds, animate_flags);
+ // Now restore the removing property.
+ tab->set_removing(removing);
+ }
+ }
+ }
+}
+
+void TabStrip2::DragDetachTabImpl(Tab2* tab, int index) {
+ gfx::Rect tab_bounds = tab->bounds();
+
+ // Determine the origin of the new window. We start with the current mouse
+ // position:
+ gfx::Point new_window_origin(views::Screen::GetCursorScreenPoint());
+ // Subtract the offset of the mouse pointer from the tab top left when the
+ // drag action began.
+ new_window_origin.Offset(-mouse_tab_offset_.x(), -mouse_tab_offset_.y());
+ // Subtract the offset of the tab's current position from the window.
+ gfx::Point tab_window_origin;
+ View::ConvertPointToWidget(tab, &tab_window_origin);
+ new_window_origin.Offset(-tab_window_origin.x(), -tab_window_origin.y());
+
+ // The new window is created with the same size as the source window but at
+ // the origin calculated above.
+ gfx::Rect new_window_bounds = GetWindow()->GetBounds();
+ new_window_bounds.set_origin(new_window_origin);
+
+ model_->DetachTabAt(index, new_window_bounds, tab_bounds);
+}
+
+void TabStrip2::StartDragTabImpl(int index, const gfx::Rect& tab_bounds) {
+ SetDraggedTabBounds(index, tab_bounds);
+ gfx::Rect tab_local_bounds(tab_bounds);
+ tab_local_bounds.set_origin(gfx::Point());
+ GetWidget()->GenerateMousePressedForView(GetTabAt(index),
+ tab_local_bounds.CenterPoint());
+}
+
+int TabStrip2::GetInternalIndex(int public_index) const {
+ std::vector<Tab2*>::const_iterator it;
+ int internal_index = public_index;
+ int valid_tab_count = 0;
+ for (it = tabs_.begin(); it != tabs_.end(); ++it) {
+ if (public_index >= valid_tab_count)
+ break;
+ if ((*it)->removing())
+ ++internal_index;
+ else
+ ++valid_tab_count;
+ }
+ return internal_index;
+}
Property changes on: chrome\browser\views\tabs\tab_strip_2.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « chrome/browser/views/tabs/tab_strip_2.h ('k') | chrome/chrome.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698