Index: chrome/browser/views/browser_actions_container.cc |
=================================================================== |
--- chrome/browser/views/browser_actions_container.cc (revision 37723) |
+++ chrome/browser/views/browser_actions_container.cc (working copy) |
@@ -19,6 +19,7 @@ |
#include "chrome/browser/profile.h" |
#include "chrome/browser/view_ids.h" |
#include "chrome/browser/views/detachable_toolbar_view.h" |
+#include "chrome/browser/views/extensions/browser_action_drag_data.h" |
#include "chrome/browser/views/extensions/browser_action_overflow_menu_controller.h" |
#include "chrome/browser/views/extensions/extension_popup.h" |
#include "chrome/browser/views/toolbar_view.h" |
@@ -40,6 +41,7 @@ |
// The padding between the browser actions and the OmniBox/page menu. |
static const int kHorizontalPadding = 4; |
+static const int kHorizontalPaddingRtl = 8; |
// The padding between browser action buttons. Visually, the actual number of |
// empty (non-drawing) pixels is this value + 2 when adjacent browser icons |
@@ -66,6 +68,16 @@ |
// Extra hit-area for the resize gripper. |
static const int kExtraResizeArea = 4; |
+// Width of the drop indicator. |
+static const int kDropIndicatorWidth = 2; |
+ |
+// Color of the drop indicator. |
+static const SkColor kDropIndicatorColor = SK_ColorBLACK; |
+ |
+// The x offset for the drop indicator (how much we shift it by). |
+static const int kDropIndicatorOffsetLtr = 3; |
+static const int kDropIndicatorOffsetRtl = 9; |
+ |
//////////////////////////////////////////////////////////////////////////////// |
// BrowserActionButton |
@@ -258,6 +270,7 @@ |
BrowserActionsContainer* panel) |
: panel_(panel) { |
button_ = new BrowserActionButton(extension, panel); |
+ button_->SetDragController(panel_); |
AddChildView(button_); |
button_->UpdateState(); |
} |
@@ -291,6 +304,7 @@ |
suppress_chevron_(false), |
resize_amount_(0), |
animation_target_size_(0), |
+ drop_indicator_position_(-1), |
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
SetID(VIEW_ID_BROWSER_ACTION_TOOLBAR); |
@@ -321,6 +335,12 @@ |
int predefined_width = |
profile_->GetPrefs()->GetInteger(prefs::kBrowserActionContainerWidth); |
+ if (predefined_width == 0) { |
+ // The width will never be 0 (due to container min size restriction) |
+ // except when no width has been saved. So, in that case ask the model |
+ // how many icons we'll show and set initial size to that. |
+ predefined_width = IconCountToWidth(model_->size()); |
+ } |
container_size_ = gfx::Size(predefined_width, kButtonSize); |
} |
@@ -368,6 +388,16 @@ |
overflow_menu_->CancelMenu(); |
} |
+void BrowserActionsContainer::CreateBrowserActionViews() { |
+ DCHECK(browser_action_views_.empty()); |
+ for (ExtensionList::iterator iter = model_->begin(); |
+ iter != model_->end(); ++iter) { |
+ BrowserActionView* view = new BrowserActionView(*iter, this); |
+ browser_action_views_.push_back(view); |
+ AddChildView(view); |
+ } |
+} |
+ |
void BrowserActionsContainer::DeleteBrowserActionViews() { |
if (!browser_action_views_.empty()) { |
for (size_t i = 0; i < browser_action_views_.size(); ++i) |
@@ -473,7 +503,7 @@ |
// other words: ContainerMinSize() < width() - resize < ClampTo(MAX). |
int width = std::max(ContainerMinSize(), |
container_size_.width() - resize_amount_); |
- int max_width = ClampToNearestIconCount(-1); // -1 gives max width. |
+ int max_width = ClampToNearestIconCount(-1, false); // -1 gives max width. |
width = std::min(width, max_width); |
return gfx::Size(width, kButtonSize); |
@@ -498,7 +528,7 @@ |
x += sz.width(); |
} |
- x += kHorizontalPadding; |
+ x += UILayoutIsRightToLeft() ? kHorizontalPaddingRtl : kHorizontalPadding; |
// Calculate if all icons fit without showing the chevron. We need to know |
// this beforehand, because showing the chevron will decrease the space that |
@@ -549,6 +579,21 @@ |
DetachableToolbarView::kEdgeDividerColor, |
DetachableToolbarView::kMiddleDividerColor, |
GetThemeProvider()->GetColor(BrowserThemeProvider::COLOR_TOOLBAR)); |
+ |
+ // The two-pixel width drop indicator. |
+ if (drop_indicator_position_ > -1) { |
+ x = drop_indicator_position_; |
+ int y = kDividerVerticalPadding; |
+ gfx::Rect indicator_bounds(x - kDropIndicatorWidth / 2, |
+ y, |
+ kDropIndicatorWidth, |
+ height() - (2 * kDividerVerticalPadding)); |
+ |
+ // TODO(sky/glen): make me pretty! |
+ canvas->FillRectInt(kDropIndicatorColor, indicator_bounds.x(), |
+ indicator_bounds.y(), indicator_bounds.width(), |
+ indicator_bounds.height()); |
+ } |
} |
void BrowserActionsContainer::ViewHierarchyChanged(bool is_add, |
@@ -563,13 +608,107 @@ |
// We do this here instead of in the constructor because AddBrowserAction |
// calls Layout on the Toolbar, which needs this object to be constructed |
// before its Layout function is called. |
- for (ExtensionList::iterator iter = model_->begin(); |
- iter != model_->end(); ++iter) { |
- BrowserActionView* view = new BrowserActionView(*iter, this); |
- browser_action_views_.push_back(view); |
- AddChildView(view); |
+ CreateBrowserActionViews(); |
+ } |
+} |
+ |
+bool BrowserActionsContainer::GetDropFormats( |
+ int* formats, std::set<OSExchangeData::CustomFormat>* custom_formats) { |
+ custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat()); |
+ return true; |
+} |
+ |
+bool BrowserActionsContainer::AreDropTypesRequired() { |
+ return true; |
+} |
+ |
+bool BrowserActionsContainer::CanDrop(const OSExchangeData& data) { |
+ BrowserActionDragData drop_data; |
+ if (!drop_data.Read(data)) |
+ return false; |
+ return drop_data.IsFromProfile(profile_); |
+} |
+ |
+void BrowserActionsContainer::OnDragEntered( |
+ const views::DropTargetEvent& event) { |
+} |
+ |
+int BrowserActionsContainer::OnDragUpdated( |
+ const views::DropTargetEvent& event) { |
+ // Modifying the x value before clamping affects how far you have to drag to |
+ // get the drop indicator to shift to another position. Modifying after |
+ // clamping affects where the drop indicator is drawn. |
+ |
+ // We add half a button size so that when you drag a button to the right and |
+ // you are half-way dragging across a button the drop indicator moves from the |
+ // left of that button to the right of that button. |
+ int x = event.x() + (kButtonSize / 2) + (2 * kBrowserActionButtonPadding); |
+ if (chevron_->IsVisible()) |
+ x += chevron_->bounds().width(); |
+ x = ClampToNearestIconCount(x, false); |
+ |
+ if (!UILayoutIsRightToLeft() && chevron_->IsVisible()) { |
+ // The clamping function includes the chevron width. In LTR locales, the |
+ // chevron is on the right and we never want to account for its width. In |
+ // RTL it is on the left and we always want to count the width. |
+ x -= chevron_->width(); |
+ } |
+ |
+ // Clamping gives us a value where the next button will be drawn, but we want |
+ // to subtract the padding (and then some) to make it appear in-between the |
+ // buttons. |
+ drop_indicator_position_ = x - kBrowserActionButtonPadding - |
+ (UILayoutIsRightToLeft() ? kDropIndicatorOffsetRtl : |
+ kDropIndicatorOffsetLtr); |
+ |
+ SchedulePaint(); |
+ return DragDropTypes::DRAG_MOVE; |
+} |
+ |
+void BrowserActionsContainer::OnDragExited() { |
+ drop_indicator_position_ = -1; |
+ SchedulePaint(); |
+} |
+ |
+int BrowserActionsContainer::OnPerformDrop( |
+ const views::DropTargetEvent& event) { |
+ BrowserActionDragData data; |
+ if (!data.Read(event.GetData())) |
+ return DragDropTypes::DRAG_NONE; |
+ |
+ // Make sure we have the same view as we started with. |
+ DCHECK(browser_action_views_[data.index()]->button()->extension()->id() == |
+ data.id()); |
+ |
+ Extension* dragging = |
+ browser_action_views_[data.index()]->button()->extension(); |
+ |
+ int target_x = drop_indicator_position_; |
+ |
+ size_t i = 0; |
+ for (; i < browser_action_views_.size(); ++i) { |
+ int view_x = |
+ browser_action_views_[i]->GetBounds(APPLY_MIRRORING_TRANSFORMATION).x(); |
+ if (!browser_action_views_[i]->IsVisible() || |
+ (UILayoutIsRightToLeft() ? view_x < target_x : view_x >= target_x)) { |
+ // We have reached the end of the visible icons or found one that has a |
+ // higher x position than the drop point. |
+ break; |
} |
} |
+ |
+ // |i| now points to the item to the right of the drop indicator*, which is |
+ // correct when dragging an icon to the left. When dragging to the right, |
+ // however, we want the icon being dragged to get the index of the item to |
+ // the left of the drop indicator, so we subtract one. |
+ // * Well, it can also point to the end, but not when dragging to the left. :) |
+ if (i > data.index()) |
+ --i; |
+ |
+ model_->MoveBrowserAction(dragging, i); |
+ |
+ OnDragExited(); // Perform clean up after dragging. |
+ return DragDropTypes::DRAG_MOVE; |
} |
void BrowserActionsContainer::Observe(NotificationType type, |
@@ -639,7 +778,31 @@ |
} |
} |
-int BrowserActionsContainer::ClampToNearestIconCount(int pixelWidth) const { |
+void BrowserActionsContainer::WriteDragData( |
+ View* sender, int press_x, int press_y, OSExchangeData* data) { |
+ DCHECK(data); |
+ |
+ for (size_t i = 0; i < browser_action_views_.size(); ++i) { |
+ if (browser_action_views_[i]->button() == sender) { |
+ BrowserActionDragData drag_data( |
+ browser_action_views_[i]->button()->extension()->id(), i); |
+ drag_data.Write(profile_, data); |
+ break; |
+ } |
+ } |
+} |
+ |
+int BrowserActionsContainer::GetDragOperations(View* sender, int x, int y) { |
+ return DragDropTypes::DRAG_MOVE; |
+} |
+ |
+bool BrowserActionsContainer::CanStartDrag( |
+ View* sender, int press_x, int press_y, int x, int y) { |
+ return true; |
+} |
+ |
+int BrowserActionsContainer::ClampToNearestIconCount( |
+ int pixelWidth, bool allow_shrink_to_minimum) const { |
// Calculate the width of one icon. |
int icon_width = (kButtonSize + kBrowserActionButtonPadding); |
@@ -660,19 +823,19 @@ |
// Count the number of icons that fit within that area. |
icon_count = icon_area / icon_width; |
- // No use allowing more than what we have. |
- if (icon_count > browser_action_views_.size()) |
+ if (icon_count == 0 && allow_shrink_to_minimum) { |
+ extras = ContainerMinSize(); // Allow very narrow width if no icons. |
+ } else if (icon_count > browser_action_views_.size()) { |
+ // No use allowing more than what we have. |
icon_count = browser_action_views_.size(); |
- else if (icon_count == 0) |
- extras = ContainerMinSize(); // Allow very narrow width if no icons. |
+ } |
} else { |
// A negative |pixels| count indicates caller wants to know the max width |
// that fits all icons; |
icon_count = browser_action_views_.size(); |
} |
- int returning = extras + (icon_count * icon_width); |
- return returning; |
+ return extras + (icon_count * icon_width); |
} |
void BrowserActionsContainer::BrowserActionAdded(Extension* extension, |
@@ -745,7 +908,7 @@ |
// because we want the container to stay the same size (clamping will take |
// care of shrinking the container if there aren't enough icons to show). |
animation_target_size_ = |
- ClampToNearestIconCount(IconCountToWidth(visible_actions)); |
+ ClampToNearestIconCount(IconCountToWidth(visible_actions), true); |
// Animate! |
resize_animation_->Reset(); |
@@ -756,10 +919,21 @@ |
} |
} |
+void BrowserActionsContainer::BrowserActionMoved(Extension* extension, |
+ int index) { |
+ DCHECK(index >= 0 && index < static_cast<int>(browser_action_views_.size())); |
+ |
+ DeleteBrowserActionViews(); |
+ CreateBrowserActionViews(); |
+ Layout(); |
+} |
+ |
int BrowserActionsContainer::WidthOfNonIconArea() const { |
int chevron_size = (chevron_->IsVisible()) ? |
chevron_->GetPreferredSize().width() : 0; |
- return resize_gripper_->GetPreferredSize().width() + kHorizontalPadding + |
+ int padding = UILayoutIsRightToLeft() ? kHorizontalPaddingRtl : |
+ kHorizontalPadding; |
+ return resize_gripper_->GetPreferredSize().width() + padding + |
chevron_size + kChevronRightMargin + kDividerHorizontalMargin; |
} |
@@ -798,14 +972,14 @@ |
// Clamp lower limit to 0 and upper limit to the amount that allows enough |
// room for all icons to show. |
int new_width = std::max(0, container_size_.width() - resize_amount); |
- int max_width = ClampToNearestIconCount(-1); |
+ int max_width = ClampToNearestIconCount(-1, false); |
new_width = std::min(new_width, max_width); |
// Up until now we've only been modifying the resize_amount, but now it is |
// time to set the container size to the size we have resized to, but then |
// animate to the nearest icon count size (or down to min size if no icon). |
container_size_.set_width(new_width); |
- animation_target_size_ = ClampToNearestIconCount(new_width); |
+ animation_target_size_ = ClampToNearestIconCount(new_width, true); |
resize_animation_->Reset(); |
resize_animation_->SetTweenType(SlideAnimation::EASE_OUT); |
resize_animation_->Show(); |