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

Side by Side Diff: chrome/browser/ui/views/toolbar/browser_actions_container.cc

Issue 556293003: Allow the user to drag an extension to the overflow menu, even when its empty (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Peter's Created 6 years, 3 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h" 5 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
6 6
7 #include "base/compiler_specific.h" 7 #include "base/compiler_specific.h"
8 #include "base/stl_util.h" 8 #include "base/stl_util.h"
9 #include "chrome/browser/extensions/extension_action_manager.h" 9 #include "chrome/browser/extensions/extension_action_manager.h"
10 #include "chrome/browser/extensions/extension_util.h" 10 #include "chrome/browser/extensions/extension_util.h"
(...skipping 250 matching lines...) Expand 10 before | Expand all | Expand 10 after
261 // Global commands are handled by the ExtensionCommandsGlobalRegistry 261 // Global commands are handled by the ExtensionCommandsGlobalRegistry
262 // instance. 262 // instance.
263 DCHECK(!command.global()); 263 DCHECK(!command.global());
264 extension_keybinding_registry_->ExecuteCommand(extension->id(), 264 extension_keybinding_registry_->ExecuteCommand(extension->id(),
265 command.accelerator()); 265 command.accelerator());
266 } 266 }
267 267
268 void BrowserActionsContainer::NotifyActionMovedToOverflow() { 268 void BrowserActionsContainer::NotifyActionMovedToOverflow() {
269 // When an action is moved to overflow, we shrink the size of the container 269 // When an action is moved to overflow, we shrink the size of the container
270 // by 1. 270 // by 1.
271 if (!profile_->IsOffTheRecord()) 271 if (!profile_->IsOffTheRecord()) {
272 model_->SetVisibleIconCount(model_->GetVisibleIconCount() - 1); 272 int icon_count = model_->GetVisibleIconCount();
273 // Since this happens when an icon moves from the main bar to overflow, we
274 // can't possibly have had no visible icons on the main bar.
275 DCHECK_NE(0, icon_count);
276 if (icon_count == -1)
277 icon_count = browser_action_views_.size();
278 model_->SetVisibleIconCount(icon_count - 1);
279 }
273 Animate(gfx::Tween::EASE_OUT, 280 Animate(gfx::Tween::EASE_OUT,
274 VisibleBrowserActionsAfterAnimation() - 1); 281 VisibleBrowserActionsAfterAnimation() - 1);
275 } 282 }
276 283
277 bool BrowserActionsContainer::ShownInsideMenu() const { 284 bool BrowserActionsContainer::ShownInsideMenu() const {
278 return in_overflow_mode(); 285 return in_overflow_mode();
279 } 286 }
280 287
281 void BrowserActionsContainer::OnBrowserActionViewDragDone() { 288 void BrowserActionsContainer::OnBrowserActionViewDragDone() {
282 ToolbarVisibleCountChanged(); 289 ToolbarVisibleCountChanged();
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 BrowserActionsContainerObserver* observer) { 322 BrowserActionsContainerObserver* observer) {
316 observers_.AddObserver(observer); 323 observers_.AddObserver(observer);
317 } 324 }
318 325
319 void BrowserActionsContainer::RemoveObserver( 326 void BrowserActionsContainer::RemoveObserver(
320 BrowserActionsContainerObserver* observer) { 327 BrowserActionsContainerObserver* observer) {
321 observers_.RemoveObserver(observer); 328 observers_.RemoveObserver(observer);
322 } 329 }
323 330
324 gfx::Size BrowserActionsContainer::GetPreferredSize() const { 331 gfx::Size BrowserActionsContainer::GetPreferredSize() const {
325 // Note: We can't use GetIconCount() for the main bar, since we may also
326 // have to include items that are in the chevron's overflow.
327 size_t icon_count =
328 in_overflow_mode() ? GetIconCount() : browser_action_views_.size();
329
330 // If there are no actions to show, or we are in overflow mode and the main
331 // container is already showing them all, then no further work is required.
332 if (icon_count == 0)
333 return gfx::Size();
334
335 if (in_overflow_mode()) { 332 if (in_overflow_mode()) {
336 // When in overflow, y is multiline, so the pixel count is IconHeight() 333 int icon_count = GetIconCount();
337 // times the number of rows needed. 334 // In overflow, we always have a preferred size of a full row (even if we
335 // don't use it), and always of at least one row. The parent will determine
336 // if the container shouldn't be shown.
Peter Kasting 2014/09/16 22:15:57 Nit: How about something like: ...and always of a
Devlin 2014/09/16 22:25:37 Done.
337 int row_count =
338 ((std::max(0, icon_count - 1)) / icons_per_overflow_menu_row_) + 1;
338 return gfx::Size( 339 return gfx::Size(
339 IconCountToWidth(icons_per_overflow_menu_row_, false), 340 IconCountToWidth(icons_per_overflow_menu_row_, false),
340 (((icon_count - 1) / icons_per_overflow_menu_row_) + 1) * IconHeight()); 341 row_count * IconHeight());
341 } 342 }
342 343
344 // If there are no actions to show, then don't show the container at all.
345 if (browser_action_views_.empty())
346 return gfx::Size();
347
343 // We calculate the size of the view by taking the current width and 348 // We calculate the size of the view by taking the current width and
344 // subtracting resize_amount_ (the latter represents how far the user is 349 // subtracting resize_amount_ (the latter represents how far the user is
345 // resizing the view or, if animating the snapping, how far to animate it). 350 // resizing the view or, if animating the snapping, how far to animate it).
346 // But we also clamp it to a minimum size and the maximum size, so that the 351 // But we also clamp it to a minimum size and the maximum size, so that the
347 // container can never shrink too far or take up more space than it needs. 352 // container can never shrink too far or take up more space than it needs.
348 // In other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX). 353 // In other words: MinimumNonemptyWidth() < width() - resize < ClampTo(MAX).
349 int preferred_width = std::min( 354 int preferred_width = std::min(
350 std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_), 355 std::max(MinimumNonemptyWidth(), container_width_ - resize_amount_),
351 IconCountToWidth(-1, false)); 356 IconCountToWidth(-1, false));
352 return gfx::Size(preferred_width, IconHeight()); 357 return gfx::Size(preferred_width, IconHeight());
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
441 int BrowserActionsContainer::OnDragUpdated( 446 int BrowserActionsContainer::OnDragUpdated(
442 const ui::DropTargetEvent& event) { 447 const ui::DropTargetEvent& event) {
443 // First check if we are above the chevron (overflow) menu. 448 // First check if we are above the chevron (overflow) menu.
444 if (chevron_ && GetEventHandlerForPoint(event.location()) == chevron_) { 449 if (chevron_ && GetEventHandlerForPoint(event.location()) == chevron_) {
445 if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_) 450 if (!show_menu_task_factory_.HasWeakPtrs() && !overflow_menu_)
446 StartShowFolderDropMenuTimer(); 451 StartShowFolderDropMenuTimer();
447 return ui::DragDropTypes::DRAG_MOVE; 452 return ui::DragDropTypes::DRAG_MOVE;
448 } 453 }
449 StopShowFolderDropMenuTimer(); 454 StopShowFolderDropMenuTimer();
450 455
451 // Figure out where to display the indicator. This is a complex calculation: 456 size_t row_index = 0;
457 size_t before_icon_in_row = 0;
458 // If there are no visible browser actions (such as when dragging an icon to
459 // an empty overflow/main container), then 0, 0 for row, column is correct.
460 if (VisibleBrowserActions() != 0) {
461 // Figure out where to display the indicator. This is a complex calculation:
452 462
453 // First, we figure out how much space is to the left of the icon area, so we 463 // First, we subtract out the padding to the left of the icon area, which is
454 // can calculate the true offset into the icon area. The easiest way to do 464 // ToolbarView::kStandardSpacing. If we're right-to-left, we also mirror the
455 // this is to just find where the first icon starts. 465 // event.x() so that our calculations are consistent with left-to-right.
456 int width_before_icons = 466 int offset_into_icon_area =
457 browser_action_views_[GetFirstVisibleIconIndex()]->x(); 467 GetMirroredXInView(event.x()) - ToolbarView::kStandardSpacing;
458 468
459 // If we're right-to-left, we flip the mirror the event.x() so that our 469 // Next, figure out what row we're on. This only matters for overflow mode,
460 // calculations are consistent with left-to-right. 470 // but the calculation is the same for both.
461 int offset_into_icon_area = 471 row_index = event.y() / IconHeight();
462 GetMirroredXInView(event.x()) - width_before_icons;
463 472
464 // Next, figure out what row we're on. This only matters for overflow mode, 473 // Sanity check - we should never be on a different row in the main
465 // but the calculation is the same for both. 474 // container.
466 size_t row_index = event.y() / IconHeight(); 475 DCHECK(in_overflow_mode() || row_index == 0);
467 476
468 // Sanity check - we should never be on a different row in the main container. 477 // Next, we determine which icon to place the indicator in front of. We want
469 DCHECK(in_overflow_mode() || row_index == 0); 478 // to place the indicator in front of icon n when the cursor is between the
479 // midpoints of icons (n - 1) and n. To do this we take the offset into the
480 // icon area and transform it as follows:
481 //
482 // Real icon area:
483 // 0 a * b c
484 // | | | |
485 // |[IC|ON] [IC|ON] [IC|ON]
486 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc.
487 // Here the "*" represents the offset into the icon area, and since it's
488 // between a and b, we want to return "1".
489 //
490 // Transformed "icon area":
491 // 0 a * b c
492 // | | | |
493 // |[ICON] |[ICON] |[ICON] |
494 // If we shift both our offset and our divider points later by half an icon
495 // plus one spacing unit, then it becomes very easy to calculate how many
496 // divider points we've passed, because they're the multiples of "one icon
497 // plus padding".
498 int before_icon_unclamped =
499 (offset_into_icon_area + (IconWidth(false) / 2) +
500 kItemSpacing) / IconWidth(true);
470 501
471 // Next, we determine which icon to place the indicator in front of. We want 502 // We need to figure out how many icons are visible on the relevant row.
472 // to place the indicator in front of icon n when the cursor is between the 503 // In the main container, this will just be the visible actions.
473 // midpoints of icons (n - 1) and n. To do this we take the offset into the 504 int visible_icons_on_row = VisibleBrowserActionsAfterAnimation();
474 // icon area and transform it as follows: 505 if (in_overflow_mode()) {
475 // 506 // If this is the final row of the overflow, then this is the remainder of
476 // Real icon area: 507 // visible icons. Otherwise, it's a full row (kIconsPerRow).
477 // 0 a * b c 508 visible_icons_on_row =
478 // | | | | 509 row_index ==
479 // |[IC|ON] [IC|ON] [IC|ON] 510 static_cast<size_t>(visible_icons_on_row /
480 // We want to be before icon 0 for 0 < x <= a, icon 1 for a < x <= b, etc. 511 icons_per_overflow_menu_row_) ?
481 // Here the "*" represents the offset into the icon area, and since it's 512 visible_icons_on_row % icons_per_overflow_menu_row_ :
482 // between a and b, we want to return "1". 513 icons_per_overflow_menu_row_;
483 // 514 }
484 // Transformed "icon area":
485 // 0 a * b c
486 // | | | |
487 // |[ICON] |[ICON] |[ICON] |
488 // If we shift both our offset and our divider points later by half an icon
489 // plus one spacing unit, then it becomes very easy to calculate how many
490 // divider points we've passed, because they're the multiples of "one icon
491 // plus padding".
492 int before_icon_unclamped = (offset_into_icon_area + (IconWidth(false) / 2) +
493 kItemSpacing) / IconWidth(true);
494 515
495 // We need to figure out how many icons are visible on the relevant row. 516 // Because the user can drag outside the container bounds, we need to clamp
496 // In the main container, this will just be the visible actions. 517 // to the valid range. Note that the maximum allowable value is (num icons),
497 int visible_icons_on_row = VisibleBrowserActionsAfterAnimation(); 518 // not (num icons - 1), because we represent the indicator being past the
498 if (in_overflow_mode()) { 519 // last icon as being "before the (last + 1) icon".
499 // If this is the final row of the overflow, then this is the remainder of 520 before_icon_in_row =
500 // visible icons. Otherwise, it's a full row (kIconsPerRow). 521 std::min(std::max(before_icon_unclamped, 0), visible_icons_on_row);
501 visible_icons_on_row =
502 row_index ==
503 static_cast<size_t>(visible_icons_on_row /
504 icons_per_overflow_menu_row_) ?
505 visible_icons_on_row % icons_per_overflow_menu_row_ :
506 icons_per_overflow_menu_row_;
507 } 522 }
508 523
509 // Because the user can drag outside the container bounds, we need to clamp to
510 // the valid range. Note that the maximum allowable value is (num icons), not
511 // (num icons - 1), because we represent the indicator being past the last
512 // icon as being "before the (last + 1) icon".
513 size_t before_icon_in_row =
514 std::min(std::max(before_icon_unclamped, 0), visible_icons_on_row);
515
516 if (!drop_position_.get() || 524 if (!drop_position_.get() ||
517 !(drop_position_->row == row_index && 525 !(drop_position_->row == row_index &&
518 drop_position_->icon_in_row == before_icon_in_row)) { 526 drop_position_->icon_in_row == before_icon_in_row)) {
519 drop_position_.reset(new DropPosition(row_index, before_icon_in_row)); 527 drop_position_.reset(new DropPosition(row_index, before_icon_in_row));
520 SchedulePaint(); 528 SchedulePaint();
521 } 529 }
522 530
523 return ui::DragDropTypes::DRAG_MOVE; 531 return ui::DragDropTypes::DRAG_MOVE;
524 } 532 }
525 533
(...skipping 10 matching lines...) Expand all
536 return ui::DragDropTypes::DRAG_NONE; 544 return ui::DragDropTypes::DRAG_NONE;
537 545
538 // Make sure we have the same view as we started with. 546 // Make sure we have the same view as we started with.
539 DCHECK_EQ(browser_action_views_[data.index()]->extension()->id(), 547 DCHECK_EQ(browser_action_views_[data.index()]->extension()->id(),
540 data.id()); 548 data.id());
541 DCHECK(model_); 549 DCHECK(model_);
542 550
543 size_t i = drop_position_->row * icons_per_overflow_menu_row_ + 551 size_t i = drop_position_->row * icons_per_overflow_menu_row_ +
544 drop_position_->icon_in_row; 552 drop_position_->icon_in_row;
545 if (in_overflow_mode()) 553 if (in_overflow_mode())
546 i += GetFirstVisibleIconIndex(); 554 i += main_container_->VisibleBrowserActionsAfterAnimation();
547 // |i| now points to the item to the right of the drop indicator*, which is 555 // |i| now points to the item to the right of the drop indicator*, which is
548 // correct when dragging an icon to the left. When dragging to the right, 556 // correct when dragging an icon to the left. When dragging to the right,
549 // however, we want the icon being dragged to get the index of the item to 557 // however, we want the icon being dragged to get the index of the item to
550 // the left of the drop indicator, so we subtract one. 558 // the left of the drop indicator, so we subtract one.
551 // * Well, it can also point to the end, but not when dragging to the left. :) 559 // * Well, it can also point to the end, but not when dragging to the left. :)
552 if (i > data.index()) 560 if (i > data.index())
553 --i; 561 --i;
554 562
555 if (profile_->IsOffTheRecord()) 563 if (profile_->IsOffTheRecord())
556 i = model_->IncognitoIndexToOriginal(i); 564 i = model_->IncognitoIndexToOriginal(i);
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
695 extensions::ActiveTabPermissionGranter* 703 extensions::ActiveTabPermissionGranter*
696 BrowserActionsContainer::GetActiveTabPermissionGranter() { 704 BrowserActionsContainer::GetActiveTabPermissionGranter() {
697 content::WebContents* web_contents = 705 content::WebContents* web_contents =
698 browser_->tab_strip_model()->GetActiveWebContents(); 706 browser_->tab_strip_model()->GetActiveWebContents();
699 if (!web_contents) 707 if (!web_contents)
700 return NULL; 708 return NULL;
701 return extensions::TabHelper::FromWebContents(web_contents)-> 709 return extensions::TabHelper::FromWebContents(web_contents)->
702 active_tab_permission_granter(); 710 active_tab_permission_granter();
703 } 711 }
704 712
705 size_t BrowserActionsContainer::GetFirstVisibleIconIndex() const {
706 return in_overflow_mode() ?
707 main_container_->VisibleBrowserActionsAfterAnimation() : 0;
708 }
709
710 ExtensionPopup* BrowserActionsContainer::TestGetPopup() { 713 ExtensionPopup* BrowserActionsContainer::TestGetPopup() {
711 return popup_owner_ ? popup_owner_->view_controller()->popup() : NULL; 714 return popup_owner_ ? popup_owner_->view_controller()->popup() : NULL;
712 } 715 }
713 716
714 void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) { 717 void BrowserActionsContainer::TestSetIconVisibilityCount(size_t icons) {
715 model_->SetVisibleIconCountForTest(icons); 718 model_->SetVisibleIconCountForTest(icons);
716 } 719 }
717 720
718 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) { 721 void BrowserActionsContainer::OnPaint(gfx::Canvas* canvas) {
719 // If the views haven't been initialized yet, wait for the next call to 722 // If the views haven't been initialized yet, wait for the next call to
720 // paint (one will be triggered by entering highlight mode). 723 // paint (one will be triggered by entering highlight mode).
721 if (model_->is_highlighting() && !browser_action_views_.empty()) { 724 if (model_->is_highlighting() && !browser_action_views_.empty()) {
722 views::Painter::PaintPainterAt( 725 views::Painter::PaintPainterAt(
723 canvas, highlight_painter_.get(), GetLocalBounds()); 726 canvas, highlight_painter_.get(), GetLocalBounds());
724 } 727 }
725 728
726 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while 729 // TODO(sky/glen): Instead of using a drop indicator, animate the icons while
727 // dragging (like we do for tab dragging). 730 // dragging (like we do for tab dragging).
728 if (drop_position_.get()) { 731 if (drop_position_.get()) {
729 // The two-pixel width drop indicator. 732 // The two-pixel width drop indicator.
730 static const int kDropIndicatorWidth = 2; 733 static const int kDropIndicatorWidth = 2;
731 734
732 // Convert back to a pixel offset into the container. First find the X 735 // Convert back to a pixel offset into the container. First find the X
733 // coordinate of the drop icon. 736 // coordinate of the drop icon.
734 int drop_icon_x = browser_action_views_[GetFirstVisibleIconIndex()]->x() + 737 int drop_icon_x = ToolbarView::kStandardSpacing +
735 (drop_position_->icon_in_row * IconWidth(true)); 738 (drop_position_->icon_in_row * IconWidth(true));
736 // Next, find the space before the drop icon. This will either be 739 // Next, find the space before the drop icon. This will either be
737 // kItemSpacing or ToolbarView::kStandardSpacing, depending on whether this 740 // kItemSpacing or ToolbarView::kStandardSpacing, depending on whether this
738 // is the first icon. 741 // is the first icon.
739 // NOTE: Right now, these are the same. But let's do this right for if they 742 // NOTE: Right now, these are the same. But let's do this right for if they
740 // ever aren't. 743 // ever aren't.
741 int space_before_drop_icon = drop_position_->icon_in_row == 0 ? 744 int space_before_drop_icon = drop_position_->icon_in_row == 0 ?
742 ToolbarView::kStandardSpacing : kItemSpacing; 745 ToolbarView::kStandardSpacing : kItemSpacing;
743 // Now place the drop indicator halfway between this and the end of the 746 // Now place the drop indicator halfway between this and the end of the
744 // previous icon. If there is an odd amount of available space between the 747 // previous icon. If there is an odd amount of available space between the
(...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after
1132 if (initialized_ && profile_->IsOffTheRecord()) { 1135 if (initialized_ && profile_->IsOffTheRecord()) {
1133 main_displayed = in_overflow_mode() ? 1136 main_displayed = in_overflow_mode() ?
1134 main_container_->VisibleBrowserActionsAfterAnimation() : 1137 main_container_->VisibleBrowserActionsAfterAnimation() :
1135 VisibleBrowserActionsAfterAnimation(); 1138 VisibleBrowserActionsAfterAnimation();
1136 } 1139 }
1137 1140
1138 // The overflow displays any (displayable) icons not shown by the main bar. 1141 // The overflow displays any (displayable) icons not shown by the main bar.
1139 return in_overflow_mode() ? 1142 return in_overflow_mode() ?
1140 displayable_icon_count - main_displayed : main_displayed; 1143 displayable_icon_count - main_displayed : main_displayed;
1141 } 1144 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698