Index: chrome/browser/extensions/extension_shelf.cc |
=================================================================== |
--- chrome/browser/extensions/extension_shelf.cc (revision 17478) |
+++ chrome/browser/extensions/extension_shelf.cc (working copy) |
@@ -16,6 +16,8 @@ |
#include "chrome/common/notification_service.h" |
#include "skia/ext/skia_utils.h" |
#include "views/controls/label.h" |
+#include "views/screen.h" |
+#include "views/widget/root_view.h" |
namespace { |
@@ -52,6 +54,9 @@ |
// static const SkColor kBackgroundColor = SkColorSetRGB(237, 244, 252); |
// static const SkColor kTopGradientColor = SkColorSetRGB(222, 234, 248); |
+// Delays for showing and hiding the shelf handle. |
+static const int kHideDelayMs = 500; |
+ |
} // namespace |
@@ -70,35 +75,41 @@ |
virtual void Layout(); |
virtual void OnMouseEntered(const views::MouseEvent& event); |
virtual void OnMouseExited(const views::MouseEvent& event); |
+ virtual bool OnMousePressed(const views::MouseEvent& event); |
+ virtual bool OnMouseDragged(const views::MouseEvent& event); |
+ virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled); |
+ virtual bool IsFocusable() const { return true; } |
private: |
ExtensionShelf* shelf_; |
ExtensionView* extension_view_; |
- views::Label* title_; |
+ scoped_ptr<views::Label> title_; |
+ bool dragging_; |
+ gfx::Point initial_drag_location_; |
DISALLOW_COPY_AND_ASSIGN(ExtensionShelfHandle); |
}; |
ExtensionShelfHandle::ExtensionShelfHandle(ExtensionShelf* shelf) |
- : shelf_(shelf), extension_view_(NULL) { |
+ : shelf_(shelf), extension_view_(NULL), dragging_(false) { |
ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
- title_ = new views::Label(L"", rb.GetFont(ResourceBundle::BaseFont)); |
- // Set enabled to false so we get the events. |
- title_->SetEnabled(false); |
- |
- // Set the colors afterwards so that the label doesn't get a disabled |
- // color. |
+ // |title_| isn't actually put in the view hierarchy. We just use it |
+ // to draw in place. The reason for this is so that we can properly handle |
+ // the various mouse events necessary for hovering and dragging. |
+ title_.reset(new views::Label(L"", rb.GetFont(ResourceBundle::BaseFont))); |
title_->SetColor(kHandleTextColor); |
title_->SetDrawHighlighted(true); |
title_->SetHighlightColor(kHandleTextHighlightColor); |
title_->SetBounds(kHandlePadding, kHandlePadding, 100, 100); |
title_->SizeToPreferredSize(); |
- AddChildView(title_); |
} |
void ExtensionShelfHandle::SetExtensionView(ExtensionView* v) { |
+ DCHECK(v->extension()); |
extension_view_ = v; |
+ if (!extension_view_->extension()) |
+ return; |
title_->SetText(UTF8ToWide(extension_view_->extension()->name())); |
title_->SizeToPreferredSize(); |
SizeToPreferredSize(); |
@@ -115,15 +126,39 @@ |
canvas->FillRectInt(kBorderColor, ext_width, height() - 1, |
width() - ext_width, 1); |
} |
+ |
+ // Draw the title using a Label as a stamp. |
+ // See constructor for comment about this. |
+ title_->ProcessPaint(canvas); |
+ |
+ if (dragging_) { |
+ // when we're dragging, draw the bottom border. |
+ canvas->FillRectInt(kBorderColor, 0, height() - 1, width(), 1); |
+ } |
} |
gfx::Size ExtensionShelfHandle::GetPreferredSize() { |
gfx::Size sz = title_->GetPreferredSize(); |
+ if (extension_view_) { |
+ int width = std::max(extension_view_->width() + 2, sz.width()); |
+ sz.set_width(width); |
+ } |
sz.Enlarge(kHandlePadding * 2, kHandlePadding * 2); |
+ if (dragging_) { |
+ gfx::Size extension_size = extension_view_->GetPreferredSize(); |
+ sz.Enlarge(0, extension_size.height() + 2); |
+ } |
return sz; |
} |
void ExtensionShelfHandle::Layout() { |
+ if (dragging_) { |
+ int y = title_->bounds().bottom() + kHandlePadding + 1; |
+ extension_view_->SetBounds(1, |
+ y, |
+ extension_view_->width(), |
+ extension_view_->height()); |
+ } |
} |
void ExtensionShelfHandle::OnMouseEntered(const views::MouseEvent& event) { |
@@ -136,7 +171,43 @@ |
shelf_->OnExtensionMouseLeave(extension_view_); |
} |
+bool ExtensionShelfHandle::OnMousePressed(const views::MouseEvent& event) { |
+ initial_drag_location_ = event.location(); |
+ return true; |
+} |
+bool ExtensionShelfHandle::OnMouseDragged(const views::MouseEvent& event) { |
+ if (!dragging_) { |
+ int y_delta = abs(initial_drag_location_.y() - event.location().y()); |
+ if (y_delta > GetVerticalDragThreshold()) { |
+ dragging_ = true; |
+ shelf_->DragExtension(); |
+ } |
+ } else { |
+ // When freely dragging a window, you can really only trust the |
+ // actual screen point. Coordinate conversions, just don't work. |
+ gfx::Point screen = views::Screen::GetCursorScreenPoint(); |
+ |
+ // However, the handle is actually a child of the browser window |
+ // so we need to convert it back to local coordinates. |
+ gfx::Point origin(0, 0); |
+ views::View::ConvertPointToScreen(shelf_->GetRootView(), &origin); |
+ screen.set_x(screen.x() - origin.x() - initial_drag_location_.x()); |
+ screen.set_y(screen.y() - origin.y() - initial_drag_location_.y()); |
+ shelf_->DragHandleTo(screen); |
+ } |
+ return true; |
+} |
+ |
+void ExtensionShelfHandle::OnMouseReleased(const views::MouseEvent& event, |
+ bool canceled) { |
+ if (dragging_) { |
+ views::View::OnMouseReleased(event, canceled); |
+ dragging_ = false; |
+ shelf_->DropExtension(event.location(), canceled); |
+ } |
+} |
+ |
//////////////////////////////////////////////// |
ExtensionShelf::ExtensionShelf(Browser* browser) |
@@ -144,7 +215,8 @@ |
handle_(NULL), |
handle_visible_(false), |
current_handle_view_(NULL), |
- ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)) { |
+ ALLOW_THIS_IN_INITIALIZER_LIST(timer_factory_(this)), |
+ drag_placeholder_view_(NULL) { |
// Watch extensions loaded and unloaded notifications. |
registrar_.Add(this, NotificationType::EXTENSIONS_LOADED, |
NotificationService::AllSources()); |
@@ -253,7 +325,7 @@ |
} |
void ExtensionShelf::OnMouseExited(const views::MouseEvent& event) { |
- HideShelfHandle(100); |
+ HideShelfHandle(kHideDelayMs); |
} |
void ExtensionShelf::Observe(NotificationType type, |
@@ -332,6 +404,9 @@ |
} |
void ExtensionShelf::OnExtensionMouseEvent(ExtensionView* view) { |
+ // Ignore these events when dragging. |
+ if (drag_placeholder_view_) |
+ return; |
if (view != current_handle_view_) { |
current_handle_view_ = view; |
} |
@@ -339,8 +414,11 @@ |
} |
void ExtensionShelf::OnExtensionMouseLeave(ExtensionView* view) { |
+ // Ignore these events when dragging. |
+ if (drag_placeholder_view_) |
+ return; |
if (view == current_handle_view_) { |
- HideShelfHandle(100); |
+ HideShelfHandle(kHideDelayMs); |
} |
} |
@@ -353,6 +431,50 @@ |
HideShelfHandle(0); |
} |
+void ExtensionShelf::DragExtension() { |
+ // Construct a placeholder view to replace the view. |
+ // TODO(erikkay) the placeholder should draw a dimmed version of the |
+ // extension view |
+ int index = GetChildIndex(current_handle_view_); |
+ drag_placeholder_view_ = new View(); |
+ drag_placeholder_view_->SetBounds(current_handle_view_->bounds()); |
+ AddChildView(index, drag_placeholder_view_); |
+ |
+ // Now move the view into the handle's widget. |
+ ExtensionShelfHandle* handle_view = |
+ static_cast<ExtensionShelfHandle*>(GetHandle()->view()); |
+ handle_view->AddChildView(current_handle_view_); |
+ handle_view->SizeToPreferredSize(); |
+ handle_->ResizeToView(); |
+ handle_view->Layout(); |
+ handle_->DetachFromBrowser(); |
+ SchedulePaint(); |
+} |
+ |
+void ExtensionShelf::DropExtension(const gfx::Point& pt, bool cancel) { |
+ handle_->AttachToBrowser(); |
+ |
+ // Replace the placeholder view with the original. |
+ int index = GetChildIndex(drag_placeholder_view_); |
+ AddChildView(index, current_handle_view_); |
+ current_handle_view_->SetBounds(drag_placeholder_view_->bounds()); |
+ RemoveChildView(drag_placeholder_view_); |
+ delete drag_placeholder_view_; |
+ drag_placeholder_view_ = NULL; |
+ |
+ ExtensionShelfHandle* handle_view = |
+ static_cast<ExtensionShelfHandle*>(GetHandle()->view()); |
+ handle_view->SizeToPreferredSize(); |
+ handle_view->Layout(); |
+ handle_->ResizeToView(); |
+ LayoutShelfHandle(); |
+ SchedulePaint(); |
+} |
+ |
+void ExtensionShelf::DragHandleTo(const gfx::Point& pt) { |
+ handle_->MoveTo(pt.x(), pt.y()); |
+} |
+ |
void ExtensionShelf::InitBackground(gfx::Canvas* canvas, |
const SkRect& subset) { |
if (!background_.empty()) |
@@ -389,6 +511,8 @@ |
} |
void ExtensionShelf::ShowShelfHandle() { |
+ if (drag_placeholder_view_) |
+ return; |
if (!timer_factory_.empty()) |
timer_factory_.RevokeAll(); |
if (handle_visible_) { |
@@ -410,6 +534,8 @@ |
} |
void ExtensionShelf::HideShelfHandle(int delay_ms) { |
+ if (drag_placeholder_view_) |
+ return; |
if (!timer_factory_.empty()) |
timer_factory_.RevokeAll(); |
if (!handle_visible_) |
@@ -427,8 +553,8 @@ |
if (handle_visible_) { |
handle_visible_ = false; |
handle_->Hide(); |
- // TODO(erikkay) with this enabled, I get an odd crash shortly after hide. |
- //handle_.reset(NULL); |
+ handle_->DetachFromBrowser(); |
+ handle_.reset(NULL); |
current_handle_view_ = NULL; |
} |
} |