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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file.
4
5 #include "chrome/browser/views/tabs/tab_strip_2.h"
6
7 #include "app/gfx/canvas.h"
8 #include "app/slide_animation.h"
9 #include "app/win_util.h"
10 #include "base/command_line.h"
11 #include "base/message_loop.h"
12 #include "chrome/common/chrome_switches.h"
13 #include "views/animator.h"
14 #include "views/screen.h"
15 #include "views/widget/widget.h"
16 #include "views/window/non_client_view.h"
17 #include "views/window/window.h"
18
19 static const int kHorizontalMoveThreshold = 16; // pixels
20
21 ////////////////////////////////////////////////////////////////////////////////
22 // TabStrip2, public:
23
24 TabStrip2::TabStrip2(TabStrip2Model* model)
25 : model_(model),
26 last_move_screen_x_(0),
27 detach_factory_(this),
28 drag_start_factory_(this) {
29 }
30
31 TabStrip2::~TabStrip2() {
32 }
33
34 // static
35 bool TabStrip2::Enabled() {
36 return CommandLine::ForCurrentProcess()->HasSwitch(
37 switches::kEnableTabtastic2);
38 }
39
40 void TabStrip2::AddTabAt(int index) {
41 Tab2* tab = new Tab2(this);
42 int insertion_index = GetInternalIndex(index);
43 tabs_.insert(tabs_.begin() + insertion_index, tab);
44 AddChildView(insertion_index, tab);
45 LayoutImpl(LS_TAB_ADD);
46 }
47
48 void TabStrip2::RemoveTabAt(int index, Tab2Model* removing_model) {
49 Tab2* tab = GetTabAt(GetInternalIndex(index));
50
51 DCHECK(!tab->removing());
52 tab->set_removing(true);
53
54 DCHECK(removing_model);
55 tab->SetRemovingModel(removing_model);
56
57 LayoutImpl(LS_TAB_REMOVE);
58 }
59
60 void TabStrip2::SelectTabAt(int index) {
61 LayoutImpl(LS_TAB_SELECT);
62 SchedulePaint();
63 }
64
65 void TabStrip2::MoveTabAt(int index, int to_index) {
66 int from_index = GetInternalIndex(index);
67 Tab2* tab = GetTabAt(from_index);
68 tabs_.erase(tabs_.begin() + from_index);
69 tabs_.insert(tabs_.begin() + GetInternalIndex(to_index), tab);
70 LayoutImpl(LS_TAB_DRAG_REORDER);
71 }
72
73 int TabStrip2::GetTabCount() const {
74 return tabs_.size();
75 }
76
77 Tab2* TabStrip2::GetTabAt(int index) const {
78 return tabs_.at(index);
79 }
80
81 int TabStrip2::GetTabIndex(Tab2* tab) const {
82 std::vector<Tab2*>::const_iterator it = find(tabs_.begin(), tabs_.end(), tab);
83 if (it != tabs_.end())
84 return it - tabs_.begin();
85 return -1;
86 }
87
88 int TabStrip2::GetInsertionIndexForPoint(const gfx::Point& point) const {
89 int tab_count = GetTabCount();
90 for (int i = 0; i < tab_count; ++i) {
91 if (GetTabAt(i)->removing())
92 continue;
93 gfx::Rect tab_bounds = GetTabAt(i)->bounds();
94 gfx::Rect tab_left_half = tab_bounds;
95 tab_left_half.set_width(tab_left_half.width() / 2);
96 if (point.x() >= tab_left_half.x() && point.x() <= tab_left_half.right())
97 return i;
98 gfx::Rect tab_right_half = tab_bounds;
99 tab_right_half.set_x(tab_right_half.width() / 2);
100 tab_right_half.set_width(tab_right_half.x());
101 if (point.x() > tab_right_half.x() && point.x() <= tab_right_half.right())
102 if (tab_right_half.Contains(point))
103 return i + 1;
104 }
105 return tab_count;
106 }
107
108 gfx::Rect TabStrip2::GetDraggedTabScreenBounds(const gfx::Point& screen_point) {
109 gfx::Point tab_screen_origin(screen_point);
110 tab_screen_origin.Offset(mouse_tab_offset_.x(), mouse_tab_offset_.y());
111 return gfx::Rect(tab_screen_origin, GetTabAt(0)->bounds().size());
112 }
113
114 void TabStrip2::SetDraggedTabBounds(int index, const gfx::Rect& tab_bounds) {
115 // This function should only ever be called goats
116 Tab2* dragged_tab = GetTabAt(index);
117 dragged_tab->SetBounds(tab_bounds);
118 SchedulePaint();
119 }
120
121 void TabStrip2::SendDraggedTabHome() {
122 LayoutImpl(LS_TAB_DRAG_REORDER);
123 }
124
125 void TabStrip2::ResumeDraggingTab(int index, const gfx::Rect& tab_bounds) {
126 MessageLoop::current()->PostTask(FROM_HERE,
127 drag_start_factory_.NewRunnableMethod(&TabStrip2::StartDragTabImpl, index,
128 tab_bounds));
129 }
130
131 // static
132 bool TabStrip2::IsDragRearrange(TabStrip2* tabstrip,
133 const gfx::Point& screen_point) {
134 gfx::Point origin;
135 View::ConvertPointToScreen(tabstrip, &origin);
136 gfx::Rect tabstrip_bounds_in_screen_coords(origin, tabstrip->bounds().size());
137 if (tabstrip_bounds_in_screen_coords.Contains(screen_point))
138 return true;
139
140 // The tab is only detached if the tab is moved outside the bounds of the
141 // TabStrip to the left or right, or a certain distance above or below the
142 // TabStrip defined by the vertical detach magnetism below. This is to
143 // prevent accidental detaches when rearranging horizontally.
144 static const int kVerticalDetachMagnetism = 45;
145
146 bool rearrange = true;
147 if (screen_point.x() < tabstrip_bounds_in_screen_coords.right() &&
148 screen_point.x() >= tabstrip_bounds_in_screen_coords.x()) {
149 int lower_threshold =
150 tabstrip_bounds_in_screen_coords.bottom() + kVerticalDetachMagnetism;
151 int upper_threshold =
152 tabstrip_bounds_in_screen_coords.y() - kVerticalDetachMagnetism;
153 return screen_point.y() >= upper_threshold &&
154 screen_point.y() <= lower_threshold;
155 }
156 return false;
157 }
158
159 ////////////////////////////////////////////////////////////////////////////////
160 // TabStrip2, Tab2Model implementation:
161
162 string16 TabStrip2::GetTitle(Tab2* tab) const {
163 return model_->GetTitle(GetTabIndex(tab));
164 }
165
166 bool TabStrip2::IsSelected(Tab2* tab) const {
167 return model_->IsSelected(GetTabIndex(tab));
168 }
169
170 void TabStrip2::SelectTab(Tab2* tab) {
171 model_->SelectTabAt(GetTabIndex(tab));
172 }
173
174 void TabStrip2::CaptureDragInfo(Tab2* tab,
175 const views::MouseEvent& drag_event) {
176 mouse_tab_offset_ = drag_event.location();
177 }
178
179 bool TabStrip2::DragTab(Tab2* tab, const views::MouseEvent& drag_event) {
180 if (!model_->CanDragTabs())
181 return false;
182
183 int tab_x = tab->x() + drag_event.location().x() - mouse_tab_offset_.x();
184 if (tab_x < 0)
185 tab_x = 0;
186 if ((tab_x + tab->width()) > bounds().right())
187 tab_x = bounds().right() - tab_x - tab->width();
188 tab->SetBounds(tab_x, tab->y(), tab->width(), tab->height());
189 SchedulePaint();
190
191 int tab_index = GetTabIndex(tab);
192 int dest_index = tab_index;
193
194 Tab2* next_tab = NULL;
195 Tab2* prev_tab = NULL;
196 int next_tab_index = tab_index + 1;
197 if (next_tab_index < GetTabCount())
198 next_tab = GetTabAt(next_tab_index);
199 int prev_tab_index = tab_index - 1;
200 if (prev_tab_index >= 0)
201 prev_tab = GetTabAt(prev_tab_index);
202
203 if (next_tab) {
204 int next_tab_middle_x = next_tab->x() + next_tab->bounds().width() / 2;
205 if (!next_tab->IsAnimating() && tab->bounds().right() > next_tab_middle_x)
206 ++dest_index;
207 }
208 if (prev_tab) {
209 int prev_tab_middle_x = prev_tab->x() + prev_tab->bounds().width() / 2;
210 if (!prev_tab->IsAnimating() && tab->bounds().x() < prev_tab_middle_x)
211 --dest_index;
212 }
213
214 gfx::Point screen_point = views::Screen::GetCursorScreenPoint();
215 if (IsDragRearrange(this, screen_point)) {
216 if (abs(screen_point.x() - last_move_screen_x_) >
217 kHorizontalMoveThreshold) {
218 if (dest_index != tab_index) {
219 last_move_screen_x_ = screen_point.x();
220 model_->MoveTabAt(tab_index, dest_index);
221 }
222 }
223 } else {
224 // We're going to detach. We need to release mouse capture so that further
225 // mouse events will be sent to the appropriate window (the detached window)
226 // and so that we don't recursively create nested message loops (dragging
227 // is done by windows in a nested message loop).
228 ReleaseCapture();
229 MessageLoop::current()->PostTask(FROM_HERE,
230 detach_factory_.NewRunnableMethod(&TabStrip2::DragDetachTabImpl,
231 tab, tab_index));
232 }
233 return true;
234 }
235
236 void TabStrip2::DragEnded(Tab2* tab) {
237 LayoutImpl(LS_TAB_DRAG_NORMALIZE);
238 }
239
240 views::AnimatorDelegate* TabStrip2::AsAnimatorDelegate() {
241 return this;
242 }
243
244 ////////////////////////////////////////////////////////////////////////////////
245 // TabStrip2, views::View overrides:
246
247 gfx::Size TabStrip2::GetPreferredSize() {
248 return gfx::Size(0, 27);
249 }
250
251 void TabStrip2::Layout() {
252 LayoutImpl(LS_OTHER);
253 }
254
255 void TabStrip2::Paint(gfx::Canvas* canvas) {
256 canvas->FillRectInt(SK_ColorBLUE, 0, 0, width(), height());
257 }
258
259 void TabStrip2::PaintChildren(gfx::Canvas* canvas) {
260 // Paint the tabs in reverse order, so they stack to the left.
261 Tab2* selected_tab = NULL;
262 for (int i = GetTabCount() - 1; i >= 0; --i) {
263 Tab2* tab = GetTabAt(i);
264 // We must ask the _Tab's_ model, not ourselves, because in some situations
265 // the model will be different to this object, e.g. when a Tab is being
266 // removed after its TabContents has been destroyed.
267 if (!IsSelected(tab)) {
268 tab->ProcessPaint(canvas);
269 } else {
270 selected_tab = tab;
271 }
272 }
273
274 if (GetWindow()->GetNonClientView()->UseNativeFrame()) {
275 // Make sure unselected tabs are somewhat transparent.
276 SkPaint paint;
277 paint.setColor(SkColorSetARGB(200, 255, 255, 255));
278 paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
279 paint.setStyle(SkPaint::kFill_Style);
280 canvas->FillRectInt(
281 0, 0, width(),
282 height() - 2, // Visible region that overlaps the toolbar.
283 paint);
284 }
285
286 // Paint the selected tab last, so it overlaps all the others.
287 if (selected_tab)
288 selected_tab->ProcessPaint(canvas);
289 }
290
291 ////////////////////////////////////////////////////////////////////////////////
292 // TabStrip2, views::AnimatorDelegate implementation:
293
294 views::View* TabStrip2::GetClampedView(views::View* host) {
295 int tab_count = GetTabCount();
296 for (int i = 0; i < tab_count; ++i) {
297 Tab2* tab = GetTabAt(i);
298 if (tab == host && i > 0)
299 return GetTabAt(i - 1);
300 }
301 return NULL;
302 }
303
304 void TabStrip2::AnimationCompletedForHost(View* host) {
305 Tab2* tab = static_cast<Tab2*>(host);
306 if (tab->removing()) {
307 tabs_.erase(find(tabs_.begin(), tabs_.end(), tab));
308 RemoveChildView(tab);
309 delete tab;
310 }
311 }
312
313 ////////////////////////////////////////////////////////////////////////////////
314 // TabStrip2, private:
315
316 int TabStrip2::GetAnimateFlagsForLayoutSource(LayoutSource source) const {
317 switch (source) {
318 case LS_TAB_ADD:
319 case LS_TAB_SELECT:
320 case LS_TAB_REMOVE:
321 return views::Animator::ANIMATE_WIDTH | views::Animator::ANIMATE_X |
322 views::Animator::ANIMATE_CLAMP;
323 case LS_TAB_DRAG_REORDER:
324 case LS_TAB_DRAG_NORMALIZE:
325 return views::Animator::ANIMATE_X;
326 }
327 DCHECK(source == LS_OTHER);
328 return views::Animator::ANIMATE_NONE;
329 }
330
331 void TabStrip2::LayoutImpl(LayoutSource source) {
332 int child_count = GetTabCount();
333 if (child_count > 0) {
334 int child_width = width() / child_count;
335 child_width = std::min(child_width, Tab2::GetStandardSize().width());
336
337 int animate_flags = GetAnimateFlagsForLayoutSource(source);
338 int removing_count = 0;
339 for (int i = 0; i < child_count; ++i) {
340 Tab2* tab = GetTabAt(i);
341 if (tab->removing())
342 ++removing_count;
343 if (!tab->dragging()) {
344 int tab_x = i * child_width - removing_count * child_width;
345 int tab_width = tab->removing() ? 0 : child_width;
346 gfx::Rect new_bounds(tab_x, 0, tab_width, height());
347
348 // Tabs that are currently being removed can have their bounds reset
349 // when another tab in the tabstrip is removed before their remove
350 // animation completes. Before they are given a new target bounds to
351 // animate to, we need to unset the removing property so that they are
352 // not pre-emptively deleted.
353 bool removing = tab->removing();
354 tab->set_removing(false);
355 tab->GetAnimator()->AnimateToBounds(new_bounds, animate_flags);
356 // Now restore the removing property.
357 tab->set_removing(removing);
358 }
359 }
360 }
361 }
362
363 void TabStrip2::DragDetachTabImpl(Tab2* tab, int index) {
364 gfx::Rect tab_bounds = tab->bounds();
365
366 // Determine the origin of the new window. We start with the current mouse
367 // position:
368 gfx::Point new_window_origin(views::Screen::GetCursorScreenPoint());
369 // Subtract the offset of the mouse pointer from the tab top left when the
370 // drag action began.
371 new_window_origin.Offset(-mouse_tab_offset_.x(), -mouse_tab_offset_.y());
372 // Subtract the offset of the tab's current position from the window.
373 gfx::Point tab_window_origin;
374 View::ConvertPointToWidget(tab, &tab_window_origin);
375 new_window_origin.Offset(-tab_window_origin.x(), -tab_window_origin.y());
376
377 // The new window is created with the same size as the source window but at
378 // the origin calculated above.
379 gfx::Rect new_window_bounds = GetWindow()->GetBounds();
380 new_window_bounds.set_origin(new_window_origin);
381
382 model_->DetachTabAt(index, new_window_bounds, tab_bounds);
383 }
384
385 void TabStrip2::StartDragTabImpl(int index, const gfx::Rect& tab_bounds) {
386 SetDraggedTabBounds(index, tab_bounds);
387 gfx::Rect tab_local_bounds(tab_bounds);
388 tab_local_bounds.set_origin(gfx::Point());
389 GetWidget()->GenerateMousePressedForView(GetTabAt(index),
390 tab_local_bounds.CenterPoint());
391 }
392
393 int TabStrip2::GetInternalIndex(int public_index) const {
394 std::vector<Tab2*>::const_iterator it;
395 int internal_index = public_index;
396 int valid_tab_count = 0;
397 for (it = tabs_.begin(); it != tabs_.end(); ++it) {
398 if (public_index >= valid_tab_count)
399 break;
400 if ((*it)->removing())
401 ++internal_index;
402 else
403 ++valid_tab_count;
404 }
405 return internal_index;
406 }
OLDNEW
« 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