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

Side by Side Diff: chrome/browser/views/extensions/extension_shelf.cc

Issue 3129003: remove toolstrips (Closed)
Patch Set: merge Created 10 years, 4 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
OLDNEW
(Empty)
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/views/extensions/extension_shelf.h"
6
7 #include <algorithm>
8
9 #include "app/resource_bundle.h"
10 #include "base/logging.h"
11 #include "base/message_loop.h"
12 #include "base/stl_util-inl.h"
13 #include "base/string_util.h"
14 #include "base/utf_string_conversions.h"
15 #include "chrome/browser/browser.h"
16 #include "chrome/browser/browser_theme_provider.h"
17 #include "chrome/browser/extensions/extension_host.h"
18 #include "chrome/browser/extensions/extension_process_manager.h"
19 #include "chrome/browser/extensions/extensions_service.h"
20 #include "chrome/browser/profile.h"
21 #include "chrome/browser/tab_contents/tab_contents.h"
22 #include "chrome/browser/views/extensions/extension_view.h"
23 #include "chrome/browser/view_ids.h"
24 #include "chrome/common/chrome_switches.h"
25 #include "chrome/common/extensions/extension.h"
26 #include "chrome/common/notification_service.h"
27 #include "chrome/common/pref_names.h"
28 #include "gfx/canvas_skia.h"
29 #include "views/controls/label.h"
30 #include "views/screen.h"
31 #include "views/widget/root_view.h"
32
33 namespace {
34
35 // This is the slight padding that is there around the edge of the browser. This
36 // has been determined empirically.
37 // TODO(sidchat): Compute this value from the root view of the extension shelf.
38 static const int kExtensionShelfPaddingOnLeft = 4;
39
40 // Margins around the content.
41 static const int kTopMarginWhenExtensionsOnTop = 1;
42 static const int kTopMarginWhenExtensionsOnBottom = 2;
43 static const int kBottomMargin = 2;
44 static const int kLeftMargin = 0;
45 static const int kRightMargin = 0;
46
47 // Padding on left and right side of an extension toolstrip.
48 static const int kToolstripPadding = 2;
49
50 // Width of the toolstrip divider.
51 static const int kToolstripDividerWidth = 2;
52
53 // Preferred height of the ExtensionShelf.
54 static const int kShelfHeight = 29;
55
56 // Preferred height of the Extension shelf when only shown on the new tab page.
57 const int kNewtabShelfHeight = 58;
58
59 // How inset the extension shelf is when displayed on the new tab page. This is
60 // in addition to the margins above.
61 static const int kNewtabHorizontalPadding = 8;
62 static const int kNewtabVerticalPadding = 12;
63
64 // We need an extra margin to the left of all the toolstrips in detached mode,
65 // so that the first toolstrip doesn't look so squished against the rounded
66 // corners of the extension shelf.
67 static const int kNewtabExtraHorMargin = 2;
68 static const int kNewtabExtraVerMargin = 2;
69
70 // Padding for the title inside the handle.
71 static const int kHandlePadding = 4;
72
73 // Inset for the extension view when displayed either dragging or expanded.
74 static const int kWindowInset = 1;
75
76 // Delays for showing and hiding the shelf handle.
77 static const int kShowDelayMs = 500;
78 static const int kHideDelayMs = 300;
79
80 } // namespace
81
82
83 // A view that holds the place for a toolstrip in the shelf while the toolstrip
84 // is being dragged or moved.
85 // TODO(erikkay) this should draw a dimmed out version of the toolstrip.
86 class ExtensionShelf::PlaceholderView : public views::View {
87 public:
88 PlaceholderView() {}
89
90 void SetWidth(int width) {
91 SetBounds(x(), y(), width, height());
92 PreferredSizeChanged();
93 }
94
95 // ExtensionShelf resizes its views to their preferred size at layout,
96 // so just always prefer the current size.
97 gfx::Size GetPreferredSize() { return size(); }
98
99 private:
100 DISALLOW_COPY_AND_ASSIGN(PlaceholderView);
101 };
102
103 // A wrapper class for the ExtensionHost displayed as a toolstrip.
104 // The class itself also acts as the View for the handle of the toolstrip
105 // it represents.
106 class ExtensionShelf::Toolstrip : public views::View,
107 public BrowserBubble::Delegate,
108 public AnimationDelegate {
109 public:
110 Toolstrip(ExtensionShelf* shelf, ExtensionHost* host,
111 const Extension::ToolstripInfo& info);
112 virtual ~Toolstrip();
113
114 // Convenience to calculate just the size of the handle.
115 gfx::Size GetHandlePreferredSize();
116
117 // View methods:
118 virtual void Paint(gfx::Canvas* canvas);
119 virtual gfx::Size GetPreferredSize();
120 virtual void Layout();
121 virtual void OnMouseEntered(const views::MouseEvent& event);
122 virtual void OnMouseExited(const views::MouseEvent& event);
123 virtual bool OnMousePressed(const views::MouseEvent& event);
124 virtual bool OnMouseDragged(const views::MouseEvent& event);
125 virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled);
126 virtual bool IsFocusable() const { return true; }
127 virtual void ChildPreferredSizeChanged(View* child);
128
129 // Adjust the size and position of the window/handle.
130 void LayoutWindow();
131
132 // Is the toolstrip window visible (not necessarily the handle).
133 bool window_visible() { return (window_.get() && window_->visible()); }
134
135 // Is the handle for this toolstrip currently visible.
136 bool handle_visible() { return (window_visible() && handle_visible_); }
137
138 // Is the toolstrip opened in expanded mode.
139 bool expanded() { return expanded_; }
140
141 // Is the toolstrip being dragged.
142 bool dragging() { return dragging_; }
143
144 // Accessors for the host and its view.
145 ExtensionHost* host() const { return host_; }
146 ExtensionView* view() const { return host_->view(); }
147
148 // The view that's currently displayed in the shelf. This could be
149 // either the ExtensionView or |placeholder_view_| depending on the
150 // current state.
151 View* GetShelfView() const {
152 if (placeholder_view_)
153 return placeholder_view_;
154 return view();
155 }
156
157 // Detaching the toolstrip from the shelf means that the ExtensionView is
158 // displayed inside of the BrowserBubble rather than the shelf. This may
159 // still visually appear to be part of the shelf (expanded) or completely
160 // separate (dragging).
161 // If |browser| is true, it also will detach/attach to the browser window.
162 void DetachFromShelf(bool browser);
163 void AttachToShelf(bool browser);
164
165 // Show / Hide the shelf handle.
166 void ShowShelfHandle();
167 void DoShowShelfHandle();
168 void HideShelfHandle(int delay_ms);
169 void DoHideShelfHandle();
170 void StopHandleTimer();
171 void HideWindow();
172 void ShowWindow();
173
174 // Expand / Collapse
175 void Expand(int height, const GURL& url);
176 void Collapse(const GURL& url);
177
178 // BrowserBubble::Delegate
179 virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
180 virtual void BubbleBrowserWindowClosing(BrowserBubble* bubble);
181
182 // AnimationDelegate
183 virtual void AnimationProgressed(const Animation* animation);
184 virtual void AnimationEnded(const Animation* animation);
185
186 private:
187 // The actual renderer that this toolstrip contains.
188 ExtensionHost* host_;
189
190 // Manifest definition of this toolstrip.
191 Extension::ToolstripInfo info_;
192
193 // A window that can be logically attached to the browser window. This is
194 // used for two purposes: a handle that sits above the toolstrip when it's
195 // collapsed and not dragging, and as a container for the toolstrip when it's
196 // dragging or expanded.
197 scoped_ptr<BrowserBubble> window_;
198
199 // Used for drawing the name of the extension in the handle.
200 scoped_ptr<views::Label> title_;
201
202 // Pointer back to the containing shelf.
203 ExtensionShelf* shelf_;
204
205 // When dragging, a placeholder view is put into the shelf to hold space
206 // for the ExtensionView. This view is parent owned when it's in the view
207 // hierarchy, so there's no ownership issues here.
208 PlaceholderView* placeholder_view_;
209
210 // Current state of the toolstrip, currently can't both be expanded and
211 // dragging.
212 // TODO(erikkay) Support dragging while expanded.
213 bool dragging_;
214 bool expanded_;
215 bool handle_visible_;
216
217 // The target expanded height of the toolstrip (used for animation).
218 int expanded_height_;
219
220 // If dragging, where did the drag start from.
221 gfx::Point initial_drag_location_;
222
223 // We have to remember the initial drag point in screen coordinates, because
224 // later, when the toolstrip is being dragged around, there is no good way of
225 // computing the screen coordinates given the initial drag view coordinates.
226 gfx::Point initial_drag_screen_point_;
227
228 // Timers for tracking mouse hovering.
229 ScopedRunnableMethodFactory<ExtensionShelf::Toolstrip> timer_factory_;
230
231 // Animate opening and closing the mole.
232 scoped_ptr<SlideAnimation> mole_animation_;
233
234 DISALLOW_COPY_AND_ASSIGN(Toolstrip);
235 };
236
237 ExtensionShelf::Toolstrip::Toolstrip(ExtensionShelf* shelf,
238 ExtensionHost* host,
239 const Extension::ToolstripInfo& info)
240 : host_(host),
241 info_(info),
242 shelf_(shelf),
243 placeholder_view_(NULL),
244 dragging_(false),
245 expanded_(false),
246 handle_visible_(false),
247 expanded_height_(0),
248 ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)) {
249 DCHECK(host->view());
250 // We're owned by shelf_, not the bubble that we get inserted in and out of.
251 set_parent_owned(false);
252
253 mole_animation_.reset(new SlideAnimation(this));
254
255 std::wstring name = UTF8ToWide(host_->extension()->name());
256 // |title_| isn't actually put in the view hierarchy. We just use it
257 // to draw in place. The reason for this is so that we can properly handle
258 // the various mouse events necessary for hovering and dragging.
259 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
260 title_.reset(new views::Label(name, rb.GetFont(ResourceBundle::BaseFont)));
261 title_->SetBounds(kHandlePadding, kHandlePadding, 100, 100);
262 title_->SizeToPreferredSize();
263
264 SizeToPreferredSize();
265 }
266
267 ExtensionShelf::Toolstrip::~Toolstrip() {
268 if (window_.get() && window_->attached())
269 window_->DetachFromBrowser();
270 }
271
272 void ExtensionShelf::Toolstrip::Paint(gfx::Canvas* canvas) {
273 // Paint the background.
274 SkColor theme_toolbar_color =
275 shelf_->GetThemeProvider()->GetColor(BrowserThemeProvider::COLOR_TOOLBAR);
276 canvas->FillRectInt(theme_toolbar_color, 0, 0, width(), height());
277
278 // Paints the handle for the toolstrip (only called on mouse-hover).
279 SkColor border_color = ResourceBundle::toolbar_separator_color;
280 canvas->FillRectInt(border_color, 0, 0, width(), 1);
281 canvas->FillRectInt(border_color, 0, 0, 1, height() - 1);
282 canvas->FillRectInt(border_color, width() - 1, 0, 1, height() - 1);
283 int ext_width = view()->width() + kToolstripPadding +
284 kToolstripDividerWidth;
285 if (ext_width < width()) {
286 canvas->FillRectInt(border_color, ext_width, height() - 1,
287 width() - ext_width, 1);
288 }
289
290 if (handle_visible_) {
291 // Draw the title using a Label as a stamp.
292 // See constructor for comment about this.
293 title_->ProcessPaint(canvas);
294
295 if (dragging_) {
296 // When we're dragging, draw the bottom border.
297 canvas->FillRectInt(border_color, 0, height() - 1, width(), 1);
298 }
299 }
300 }
301
302 gfx::Size ExtensionShelf::Toolstrip::GetHandlePreferredSize() {
303 gfx::Size sz;
304 if (handle_visible_) {
305 sz = title_->GetPreferredSize();
306 sz.set_width(std::max(view()->width(), sz.width()));
307 sz.Enlarge(2 + kHandlePadding * 2, kHandlePadding * 2);
308 }
309 return sz;
310 }
311
312 gfx::Size ExtensionShelf::Toolstrip::GetPreferredSize() {
313 gfx::Size sz;
314 if (handle_visible_)
315 sz = GetHandlePreferredSize();
316 if (view()->GetParent() == this) {
317 gfx::Size extension_size = view()->GetPreferredSize();
318 sz.Enlarge(0, extension_size.height());
319 sz.set_width(extension_size.width());
320 }
321
322 // The view is inset slightly when displayed in the window.
323 sz.Enlarge(kWindowInset * 2, kWindowInset * 2);
324 return sz;
325 }
326
327 void ExtensionShelf::Toolstrip::Layout() {
328 if (view()->GetParent() == this) {
329 int view_y = kWindowInset;
330 if (handle_visible_)
331 view_y += GetHandlePreferredSize().height();
332 view()->SetBounds(kWindowInset, view_y, view()->width(), view()->height());
333 }
334 }
335
336 void ExtensionShelf::Toolstrip::OnMouseEntered(const views::MouseEvent& event) {
337 if (dragging_)
338 return;
339 ShowShelfHandle();
340 }
341
342 void ExtensionShelf::Toolstrip::OnMouseExited(const views::MouseEvent& event) {
343 if (dragging_)
344 return;
345 HideShelfHandle(kHideDelayMs);
346 }
347
348 bool ExtensionShelf::Toolstrip::OnMousePressed(const views::MouseEvent& event) {
349 initial_drag_location_ = event.location();
350 initial_drag_screen_point_ = views::Screen::GetCursorScreenPoint();
351 return true;
352 }
353
354 bool ExtensionShelf::Toolstrip::OnMouseDragged(const views::MouseEvent& event) {
355 if (expanded_) {
356 // Do nothing for now.
357 } else if (!dragging_) {
358 int y_delta = abs(initial_drag_location_.y() - event.location().y());
359 int x_delta = abs(initial_drag_location_.x() - event.location().x());
360 if (y_delta > GetVerticalDragThreshold() ||
361 x_delta > GetHorizontalDragThreshold()) {
362 dragging_ = true;
363 StopHandleTimer();
364 DetachFromShelf(true);
365 }
366 } else {
367 // When freely dragging a window, you can really only trust the
368 // actual screen point. Local coordinate conversions don't work.
369 gfx::Point screen = views::Screen::GetCursorScreenPoint();
370
371 // However, the handle is actually a child of the browser window
372 // so we need to convert it back to local coordinates.
373 gfx::Point origin(0, 0);
374 views::View::ConvertPointToScreen(shelf_->GetRootView(), &origin);
375 int screen_x = screen.x() - initial_drag_location_.x() - origin.x();
376
377 // Restrict the horizontal and vertical motions of the toolstrip so that it
378 // cannot be dragged out of the extension shelf. If the extension shelf is
379 // on the top along with the bookmark bar, the toolstrip cannot be dragged
380 // into the space allocated for bookmarks. The toolstrip cannot be dragged
381 // out of the browser window.
382 if (screen_x < kExtensionShelfPaddingOnLeft) {
383 screen_x = kExtensionShelfPaddingOnLeft;
384 } else if (screen_x > shelf_->width() - width() +
385 kExtensionShelfPaddingOnLeft) {
386 screen_x = shelf_->width() - width() + kExtensionShelfPaddingOnLeft;
387 }
388 screen.set_x(screen_x);
389 screen.set_y(initial_drag_screen_point_.y() - origin.y() -
390 initial_drag_location_.y());
391
392 // TODO(erikkay) as this gets dragged around, update the placeholder view
393 // on the shelf to show where it will get dropped to.
394 window_->MoveTo(screen.x(), screen.y());
395 }
396 return true;
397 }
398
399 void ExtensionShelf::Toolstrip::OnMouseReleased(const views::MouseEvent& event,
400 bool canceled) {
401 StopHandleTimer();
402 if (dragging_) {
403 // Drop the toolstrip roughly where it is now.
404 views::View::OnMouseReleased(event, canceled);
405 dragging_ = false;
406 // |this| and |shelf_| are in different view hierarchies, so we need to
407 // convert to screen coordinates and back again to map locations.
408 gfx::Point loc = event.location();
409 View::ConvertPointToScreen(this, &loc);
410 View::ConvertPointToView(NULL, shelf_, &loc);
411 shelf_->DropExtension(this, loc, canceled);
412 AttachToShelf(true);
413 } else if (!canceled) {
414 // Toggle mole to either expanded or collapsed.
415 // TODO(erikkay) If there's no valid URL in the manifest, should we
416 // post an event to the toolstrip in this case?
417 if (expanded_) {
418 if (info_.toolstrip.is_valid())
419 shelf_->CollapseToolstrip(host_, info_.toolstrip);
420 } else {
421 if (info_.mole.is_valid())
422 shelf_->ExpandToolstrip(host_, info_.mole, info_.mole_height);
423 }
424 }
425 }
426
427 void ExtensionShelf::Toolstrip::LayoutWindow() {
428 if (!window_visible() && !handle_visible_ && !expanded_)
429 return;
430
431 if (!window_.get()) {
432 window_.reset(new BrowserBubble(this, shelf_->GetWidget(),
433 gfx::Point(0, 0),
434 false)); // Do not add a drop-shadow.
435 window_->set_delegate(this);
436 }
437
438 gfx::Size window_size = GetPreferredSize();
439 if (mole_animation_->is_animating()) {
440 // We only want to animate the body of the mole window. When we're
441 // expanding, this is everything except for the handle. When we're
442 // collapsing, this is everything except for the handle and the toolstrip.
443 // We subtract this amount from the target height, figure out the step in
444 // the animation from the rest, and then add it back in.
445 int window_offset = shelf_->height();
446 if (!mole_animation_->IsShowing())
447 window_offset += GetPreferredSize().height();
448 else
449 window_offset += GetHandlePreferredSize().height();
450 int h = expanded_height_ - window_offset;
451 window_size.set_height(window_offset +
452 static_cast<int>(h * mole_animation_->GetCurrentValue()));
453 } else if (!expanded_ && !dragging_) {
454 window_size.set_height(GetHandlePreferredSize().height());
455 }
456
457 // Now figure out where to place the window on the screen. Since it's a top-
458 // level widget, we need to do some coordinate conversion to get this right.
459 gfx::Point origin(-kToolstripPadding, 0);
460 if (expanded_ || mole_animation_->is_animating()) {
461 origin.set_y(GetShelfView()->height() - window_size.height());
462 views::View::ConvertPointToView(GetShelfView(), shelf_->GetRootView(),
463 &origin);
464 } else {
465 origin.set_y(-(window_size.height() + kToolstripPadding - 1));
466 views::View::ConvertPointToWidget(view(), &origin);
467 }
468 SetBounds(0, 0, window_size.width(), window_size.height());
469 window_->SetBounds(origin.x(), origin.y(),
470 window_size.width(), window_size.height());
471 }
472
473 void ExtensionShelf::Toolstrip::ChildPreferredSizeChanged(View* child) {
474 if (child == view()) {
475 child->SizeToPreferredSize();
476 Layout();
477 if (window_visible())
478 LayoutWindow();
479 if (expanded_) {
480 placeholder_view_->SetWidth(child->width());
481 shelf_->Layout();
482 }
483 }
484 }
485
486 void ExtensionShelf::Toolstrip::BubbleBrowserWindowMoved(
487 BrowserBubble* bubble) {
488 if (!expanded_)
489 HideWindow();
490 }
491
492 void ExtensionShelf::Toolstrip::BubbleBrowserWindowClosing(
493 BrowserBubble* bubble) {
494 HideWindow();
495 }
496
497 void ExtensionShelf::Toolstrip::AnimationProgressed(
498 const Animation* animation) {
499 LayoutWindow();
500 }
501
502 void ExtensionShelf::Toolstrip::AnimationEnded(const Animation* animation) {
503 if (window_visible())
504 LayoutWindow();
505 if (!expanded_) {
506 AttachToShelf(false);
507 HideShelfHandle(kHideDelayMs);
508 }
509 }
510
511 void ExtensionShelf::Toolstrip::DetachFromShelf(bool browserDetach) {
512 DCHECK(window_.get());
513 DCHECK(!placeholder_view_);
514 if (browserDetach && window_->attached())
515 window_->DetachFromBrowser();
516
517 // Construct a placeholder view to replace the view.
518 placeholder_view_ = new PlaceholderView();
519 placeholder_view_->SetBounds(view()->bounds());
520 shelf_->AddChildView(placeholder_view_);
521
522 AddChildView(view());
523 SizeToPreferredSize();
524 window_->ResizeToView();
525 Layout();
526 }
527
528 void ExtensionShelf::Toolstrip::AttachToShelf(bool browserAttach) {
529 DCHECK(window_.get());
530 DCHECK(placeholder_view_);
531 if (browserAttach && !window_->attached())
532 window_->AttachToBrowser();
533
534 // Move the view back into the shelf and remove the old placeholder.
535 shelf_->AddChildView(view());
536
537 // The size of the view may have changed, so just set the position.
538 view()->SetX(placeholder_view_->x());
539 view()->SetY(placeholder_view_->y());
540
541 // Remove the old placeholder.
542 shelf_->RemoveChildView(placeholder_view_);
543 delete placeholder_view_;
544 placeholder_view_ = NULL;
545
546 SizeToPreferredSize();
547 Layout();
548 shelf_->Layout();
549 }
550
551 void ExtensionShelf::Toolstrip::DoShowShelfHandle() {
552 if (!handle_visible()) {
553 handle_visible_ = true;
554
555 // Make sure the text color for the title matches the theme colors.
556 title_->SetColor(
557 shelf_->GetThemeProvider()->GetColor(
558 BrowserThemeProvider::COLOR_BOOKMARK_TEXT));
559
560 ShowWindow();
561 }
562 }
563
564 void ExtensionShelf::Toolstrip::HideWindow() {
565 if (!window_visible())
566 return;
567 handle_visible_ = false;
568 window_->Hide();
569 if (expanded_) {
570 if (info_.toolstrip.is_valid())
571 shelf_->CollapseToolstrip(host_, info_.toolstrip);
572 else
573 shelf_->CollapseToolstrip(host_, GURL());
574 }
575 if (window_->attached())
576 window_->DetachFromBrowser();
577 window_.reset(NULL);
578 shelf_->Layout();
579 }
580
581 void ExtensionShelf::Toolstrip::ShowWindow() {
582 DCHECK(handle_visible_ || expanded_);
583
584 LayoutWindow();
585 if (!window_visible())
586 window_->Show(false); // |false| means show, but don't activate.
587 }
588
589 void ExtensionShelf::Toolstrip::DoHideShelfHandle() {
590 if (!handle_visible())
591 return;
592 handle_visible_ = false;
593 if (expanded_) {
594 LayoutWindow();
595 Layout();
596 } else {
597 HideWindow();
598 }
599 }
600
601 void ExtensionShelf::Toolstrip::StopHandleTimer() {
602 if (!timer_factory_.empty())
603 timer_factory_.RevokeAll();
604 }
605
606 void ExtensionShelf::Toolstrip::Expand(int height, const GURL& url) {
607 DCHECK(!expanded_);
608
609 expanded_ = true;
610 ShowWindow();
611
612 bool navigate = (!url.is_empty() && url != host_->GetURL());
613 if (navigate)
614 host_->NavigateToURL(url);
615
616 StopHandleTimer();
617 DetachFromShelf(false);
618
619 mole_animation_->Show();
620
621 gfx::Size extension_size = view()->GetPreferredSize();
622 extension_size.set_height(height);
623 view()->SetPreferredSize(extension_size);
624 expanded_height_ = GetPreferredSize().height();
625
626 // This is to prevent flickering as the page loads and lays out.
627 // Once the navigation is finished, ExtensionView will wind up setting
628 // visibility to true.
629 if (navigate)
630 view()->SetVisible(false);
631 }
632
633 void ExtensionShelf::Toolstrip::Collapse(const GURL& url) {
634 DCHECK(expanded_);
635 expanded_ = false;
636
637 if (window_visible())
638 mole_animation_->Hide();
639
640 gfx::Size extension_size = view()->GetPreferredSize();
641 extension_size.set_height(
642 kShelfHeight - (shelf_->top_margin() + kBottomMargin));
643 view()->SetPreferredSize(extension_size);
644
645 if (!url.is_empty() && url != host_->GetURL()) {
646 host_->NavigateToURL(url);
647
648 // This is to prevent flickering as the page loads and lays out.
649 // Once the navigation is finished, ExtensionView will wind up setting
650 // visibility to true.
651 view()->SetVisible(false);
652 }
653
654 if (!window_visible())
655 AnimationEnded(NULL);
656 }
657
658 void ExtensionShelf::Toolstrip::ShowShelfHandle() {
659 StopHandleTimer();
660 if (handle_visible())
661 return;
662 MessageLoop::current()->PostDelayedTask(FROM_HERE,
663 timer_factory_.NewRunnableMethod(
664 &ExtensionShelf::Toolstrip::DoShowShelfHandle),
665 kShowDelayMs);
666 }
667
668 void ExtensionShelf::Toolstrip::HideShelfHandle(int delay_ms) {
669 StopHandleTimer();
670 if (!handle_visible() || dragging_ || mole_animation_->is_animating())
671 return;
672 if (delay_ms) {
673 MessageLoop::current()->PostDelayedTask(FROM_HERE,
674 timer_factory_.NewRunnableMethod(
675 &ExtensionShelf::Toolstrip::DoHideShelfHandle),
676 delay_ms);
677 } else {
678 DoHideShelfHandle();
679 }
680 }
681
682 ////////////////////////////////////////////////////////////////////////////////
683
684 ExtensionShelf::ExtensionShelf(Browser* browser)
685 : background_needs_repaint_(true),
686 browser_(browser),
687 model_(browser->extension_shelf_model()),
688 fullscreen_(false) {
689 SetID(VIEW_ID_DEV_EXTENSION_SHELF);
690
691 top_margin_ = kTopMarginWhenExtensionsOnBottom;
692
693 model_->AddObserver(this);
694 LoadFromModel();
695 EnableCanvasFlippingForRTLUI(true);
696 registrar_.Add(this,
697 NotificationType::EXTENSION_SHELF_VISIBILITY_PREF_CHANGED,
698 NotificationService::AllSources());
699
700 size_animation_.reset(new SlideAnimation(this));
701 if (IsAlwaysShown())
702 size_animation_->Reset(1);
703 else
704 size_animation_->Reset(0);
705 }
706
707 ExtensionShelf::~ExtensionShelf() {
708 if (model_) {
709 int count = model_->count();
710 for (int i = 0; i < count; ++i) {
711 delete ToolstripAtIndex(i);
712 model_->SetToolstripDataAt(i, NULL);
713 }
714 model_->RemoveObserver(this);
715 }
716 }
717
718 void ExtensionShelf::PaintChildren(gfx::Canvas* canvas) {
719 InitBackground(canvas);
720
721 int max_x = width();
722 if (IsDetached())
723 max_x -= 2 * kNewtabHorizontalPadding;
724
725 // Draw vertical dividers between Toolstrip items in the Extension shelf.
726 int count = GetChildViewCount();
727 for (int i = 0; i < count; ++i) {
728 int right = GetChildViewAt(i)->bounds().right() + kToolstripPadding;
729 if (right >= max_x)
730 break;
731 int vertical_padding = IsDetached() ? (height() - kShelfHeight) / 2 : 1;
732
733 DetachableToolbarView::PaintVerticalDivider(
734 canvas, right, height(), vertical_padding,
735 DetachableToolbarView::kEdgeDividerColor,
736 DetachableToolbarView::kMiddleDividerColor,
737 GetThemeProvider()->GetColor(BrowserThemeProvider::COLOR_TOOLBAR));
738 }
739 }
740
741 // static
742 void ExtensionShelf::ToggleWhenExtensionShelfVisible(Profile* profile) {
743 PrefService* prefs = profile->GetPrefs();
744 const bool always_show = !prefs->GetBoolean(prefs::kShowExtensionShelf);
745
746 // The user changed when the Extension Shelf is shown, update the
747 // preferences.
748 prefs->SetBoolean(prefs::kShowExtensionShelf, always_show);
749 prefs->ScheduleSavePersistentPrefs();
750
751 // And notify the notification service.
752 Source<Profile> source(profile);
753 NotificationService::current()->Notify(
754 NotificationType::EXTENSION_SHELF_VISIBILITY_PREF_CHANGED,
755 source,
756 NotificationService::NoDetails());
757 }
758
759 gfx::Size ExtensionShelf::GetPreferredSize() {
760 return LayoutItems(true);
761 }
762
763 void ExtensionShelf::ChildPreferredSizeChanged(View* child) {
764 PreferredSizeChanged();
765 }
766
767 void ExtensionShelf::Layout() {
768 LayoutItems(false);
769 }
770
771 void ExtensionShelf::OnMouseEntered(const views::MouseEvent& event) {
772 }
773
774 void ExtensionShelf::OnMouseExited(const views::MouseEvent& event) {
775 }
776
777 bool ExtensionShelf::GetAccessibleRole(AccessibilityTypes::Role* role) {
778 DCHECK(role);
779
780 *role = AccessibilityTypes::ROLE_TOOLBAR;
781 return true;
782 }
783
784 void ExtensionShelf::OnThemeChanged() {
785 // Refresh the CSS to update toolstrip text colors from theme.
786 int count = model_->count();
787 for (int i = 0; i < count; ++i)
788 ToolstripAtIndex(i)->view()->host()->InsertThemedToolstripCSS();
789
790 Layout();
791 }
792
793 void ExtensionShelf::ToolstripInsertedAt(ExtensionHost* host,
794 int index) {
795 model_->SetToolstripDataAt(index,
796 new Toolstrip(this, host, model_->ToolstripAt(index).info));
797
798 bool had_views = GetChildViewCount() > 0;
799 ExtensionView* view = host->view();
800 AddChildView(view);
801 view->SetContainer(this);
802 if (!had_views)
803 PreferredSizeChanged();
804 Layout();
805 }
806
807 void ExtensionShelf::ToolstripRemovingAt(ExtensionHost* host, int index) {
808 // Delete the Toolstrip view and remove it from the model.
809 Toolstrip* toolstrip = ToolstripAtIndex(index);
810 View* view = toolstrip->GetShelfView();
811 RemoveChildView(view);
812 delete toolstrip;
813 model_->SetToolstripDataAt(index, NULL);
814
815 // Technically, the toolstrip is still in the model at this point, but
816 // the Layout code handles this case.
817 Layout();
818 }
819
820 void ExtensionShelf::ToolstripDraggingFrom(ExtensionHost* host, int index) {
821 }
822
823 void ExtensionShelf::ToolstripMoved(ExtensionHost* host, int from_index,
824 int to_index) {
825 Layout();
826 }
827
828 void ExtensionShelf::ToolstripChanged(ExtensionShelfModel::iterator toolstrip) {
829 Toolstrip* t = static_cast<Toolstrip*>(toolstrip->data);
830 if (toolstrip->height > 0) {
831 if (!t->expanded()) {
832 t->Expand(toolstrip->height, toolstrip->url);
833 }
834 } else if (t->expanded()) {
835 t->Collapse(toolstrip->url);
836 }
837 }
838
839 void ExtensionShelf::ExtensionShelfEmpty() {
840 PreferredSizeChanged();
841 }
842
843 void ExtensionShelf::ShelfModelReloaded() {
844 // None of the child views are parent owned, so nothing is being leaked here.
845 RemoveAllChildViews(false);
846 LoadFromModel();
847 }
848
849 void ExtensionShelf::ShelfModelDeleting() {
850 int count = model_->count();
851 for (int i = 0; i < count; ++i) {
852 delete ToolstripAtIndex(i);
853 model_->SetToolstripDataAt(i, NULL);
854 }
855 model_->RemoveObserver(this);
856 model_ = NULL;
857 }
858
859 void ExtensionShelf::AnimationProgressed(const Animation* animation) {
860 if (browser_)
861 browser_->ExtensionShelfSizeChanged();
862 }
863
864 void ExtensionShelf::AnimationEnded(const Animation* animation) {
865 if (browser_)
866 browser_->ExtensionShelfSizeChanged();
867
868 Layout();
869 }
870
871 void ExtensionShelf::Observe(NotificationType type,
872 const NotificationSource& source,
873 const NotificationDetails& details) {
874 switch (type.value) {
875 case NotificationType::EXTENSION_SHELF_VISIBILITY_PREF_CHANGED: {
876 if (fullscreen_)
877 break;
878 if (IsAlwaysShown())
879 size_animation_->Show();
880 else
881 size_animation_->Hide();
882 break;
883 }
884 default:
885 NOTREACHED();
886 break;
887 }
888 }
889
890 void ExtensionShelf::OnExtensionMouseMove(ExtensionView* view) {
891 Toolstrip *toolstrip = ToolstripForView(view);
892 if (toolstrip)
893 toolstrip->ShowShelfHandle();
894 }
895
896 void ExtensionShelf::OnExtensionMouseLeave(ExtensionView* view) {
897 Toolstrip *toolstrip = ToolstripForView(view);
898 if (toolstrip)
899 toolstrip->HideShelfHandle(kHideDelayMs);
900 }
901
902 void ExtensionShelf::DropExtension(Toolstrip* toolstrip, const gfx::Point& pt,
903 bool cancel) {
904 Toolstrip* dest_toolstrip = ToolstripAtX(pt.x());
905 if (!dest_toolstrip) {
906 if (pt.x() > 0)
907 dest_toolstrip = ToolstripAtIndex(model_->count() - 1);
908 else
909 dest_toolstrip = ToolstripAtIndex(0);
910 }
911 if (toolstrip == dest_toolstrip)
912 return;
913 int from = model_->IndexOfHost(toolstrip->host());
914 int to = model_->IndexOfHost(dest_toolstrip->host());
915 DCHECK(from != to);
916 model_->MoveToolstripAt(from, to);
917 }
918
919 void ExtensionShelf::ExpandToolstrip(ExtensionHost* host, const GURL& url,
920 int height) {
921 ExtensionShelfModel::iterator toolstrip = model_->ToolstripForHost(host);
922 model_->ExpandToolstrip(toolstrip, url, height);
923 }
924
925 void ExtensionShelf::CollapseToolstrip(ExtensionHost* host, const GURL& url) {
926 ExtensionShelfModel::iterator toolstrip = model_->ToolstripForHost(host);
927 model_->CollapseToolstrip(toolstrip, url);
928 }
929
930 void ExtensionShelf::InitBackground(gfx::Canvas* canvas) {
931 if (!background_needs_repaint_)
932 return;
933
934 // Capture a background bitmap to give to the toolstrips.
935 SkRect background_rect = {
936 SkIntToScalar(0),
937 SkIntToScalar(0),
938 SkIntToScalar(width()),
939 SkIntToScalar(height())
940 };
941
942 // Tell all extension views about the new background.
943 int count = model_->count();
944 for (int i = 0; i < count; ++i) {
945 ExtensionView* view = ToolstripAtIndex(i)->view();
946
947 const SkBitmap& background =
948 canvas->AsCanvasSkia()->getDevice()->accessBitmap(false);
949
950 SkRect mapped_subset = background_rect;
951 gfx::Rect view_bounds = view->bounds();
952 mapped_subset.offset(SkIntToScalar(view_bounds.x()),
953 SkIntToScalar(view_bounds.y()));
954 bool result =
955 canvas->AsCanvasSkia()->getTotalMatrix().mapRect(&mapped_subset);
956 DCHECK(result);
957
958 SkIRect isubset;
959 mapped_subset.round(&isubset);
960 SkBitmap subset_bitmap;
961 // This will create another bitmap that just references pixels in the
962 // actual bitmap.
963 result = background.extractSubset(&subset_bitmap, isubset);
964 if (!result)
965 return;
966
967 // We do a deep copy because extractSubset() returns a bitmap that
968 // references pixels in the original one and we want to actually make a
969 // smaller copy that will have a long lifetime.
970 SkBitmap smaller_copy;
971 if (!subset_bitmap.copyTo(&smaller_copy, SkBitmap::kARGB_8888_Config))
972 return;
973 DCHECK(smaller_copy.readyToDraw());
974
975 view->SetBackground(smaller_copy);
976 }
977
978 background_needs_repaint_ = false;
979 }
980
981 ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripAtX(int x) {
982 int count = model_->count();
983 if (count == 0)
984 return NULL;
985
986 if (x < 0)
987 return NULL;
988
989 for (int i = 0; i < count; ++i) {
990 Toolstrip* toolstrip = ToolstripAtIndex(i);
991 View* view = toolstrip->GetShelfView();
992 int x_mirrored = view->GetRootView()->MirroredXCoordinateInsideView(x);
993 if (x_mirrored > view->x() + view->width() + kToolstripPadding)
994 continue;
995 return toolstrip;
996 }
997
998 return NULL;
999 }
1000
1001 ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripAtIndex(int index) {
1002 return static_cast<Toolstrip*>(model_->ToolstripAt(index).data);
1003 }
1004
1005 ExtensionShelf::Toolstrip* ExtensionShelf::ToolstripForView(
1006 ExtensionView* view) {
1007 int count = model_->count();
1008 for (int i = 0; i < count; ++i) {
1009 Toolstrip* toolstrip = ToolstripAtIndex(i);
1010 if (view == toolstrip->view())
1011 return toolstrip;
1012 }
1013 return NULL;
1014 }
1015
1016 void ExtensionShelf::LoadFromModel() {
1017 int count = model_->count();
1018 for (int i = 0; i < count; ++i)
1019 ToolstripInsertedAt(model_->ToolstripAt(i).host, i);
1020 }
1021
1022 gfx::Size ExtensionShelf::LayoutItems(bool compute_bounds_only) {
1023 if (!GetParent() || !model_ || !model_->count())
1024 return gfx::Size(0, 0);
1025
1026 gfx::Size prefsize;
1027 int x = kLeftMargin;
1028 int y = top_margin_;
1029 int content_height = kShelfHeight - top_margin_ - kBottomMargin;
1030 int max_x = width() - kRightMargin;
1031
1032 if (OnNewTabPage()) {
1033 double current_state = 1 - size_animation_->GetCurrentValue();
1034 x += static_cast<int>(static_cast<double>
1035 (kNewtabHorizontalPadding + kNewtabExtraHorMargin) * current_state);
1036 y += static_cast<int>(static_cast<double>
1037 (kNewtabVerticalPadding + kNewtabExtraVerMargin) * current_state);
1038 max_x -= static_cast<int>(static_cast<double>
1039 (2 * kNewtabHorizontalPadding) * current_state);
1040 }
1041
1042 int count = model_->count();
1043 for (int i = 0; i < count; ++i) {
1044 x += kToolstripPadding; // Left padding.
1045 Toolstrip* toolstrip = ToolstripAtIndex(i);
1046 if (!toolstrip) // Can be NULL while in the process of removing.
1047 continue;
1048 View* view = toolstrip->GetShelfView();
1049 gfx::Size pref = view->GetPreferredSize();
1050
1051 // |next_x| is the x value for where the next toolstrip in the list will be.
1052 int next_x = x + pref.width() + kToolstripPadding; // Right padding.
1053 if (!compute_bounds_only) {
1054 bool clipped = next_x >= max_x;
1055 if (clipped)
1056 pref.set_width(std::max(0, max_x - x));
1057 if (view == toolstrip->view())
1058 toolstrip->view()->SetIsClipped(clipped);
1059 view->SetBounds(x, y, pref.width(), content_height);
1060 view->Layout();
1061 if (toolstrip->window_visible())
1062 toolstrip->LayoutWindow();
1063 }
1064 x = next_x + kToolstripDividerWidth;
1065 }
1066
1067 if (!compute_bounds_only) {
1068 background_needs_repaint_ = true;
1069 SchedulePaint();
1070 } else {
1071 if (OnNewTabPage()) {
1072 prefsize.set_height(kShelfHeight + static_cast<int>(static_cast<double>
1073 (kNewtabShelfHeight - kShelfHeight) *
1074 (1 - size_animation_->GetCurrentValue())));
1075 } else {
1076 prefsize.set_height(static_cast<int>(static_cast<double>(kShelfHeight) *
1077 size_animation_->GetCurrentValue()));
1078 }
1079
1080 x += kRightMargin;
1081 prefsize.set_width(x);
1082 }
1083
1084 return prefsize;
1085 }
1086
1087 bool ExtensionShelf::IsDetached() const {
1088 return OnNewTabPage() && (size_animation_->GetCurrentValue() != 1);
1089 }
1090
1091 bool ExtensionShelf::IsAlwaysShown() const {
1092 Profile* profile = browser_->profile();
1093 return profile->GetPrefs()->GetBoolean(prefs::kShowExtensionShelf);
1094 }
1095
1096 bool ExtensionShelf::OnNewTabPage() const {
1097 return (browser_ && browser_->GetSelectedTabContents() &&
1098 browser_->GetSelectedTabContents()->IsExtensionShelfAlwaysVisible());
1099 }
1100
1101 void ExtensionShelf::OnFullscreenToggled(bool fullscreen) {
1102 if (fullscreen == fullscreen_)
1103 return;
1104 fullscreen_ = fullscreen;
1105 if (!IsAlwaysShown())
1106 return;
1107 size_animation_->Reset(fullscreen ? 0 : 1);
1108 }
OLDNEW
« no previous file with comments | « chrome/browser/views/extensions/extension_shelf.h ('k') | chrome/browser/views/extensions/extension_view.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698