| OLD | NEW |
| (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 } | |
| OLD | NEW |