summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-03 23:00:09 +0000
committererikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-03 23:00:09 +0000
commit41291b0e3555aa77d3501c02a75679b1d734d4ee (patch)
tree07a73989105d161f7a02cd7f9d87ca14827005a3
parent5e9877eb405ec97622d419a146b9b7c331568bf6 (diff)
downloadchromium_src-41291b0e3555aa77d3501c02a75679b1d734d4ee.zip
chromium_src-41291b0e3555aa77d3501c02a75679b1d734d4ee.tar.gz
chromium_src-41291b0e3555aa77d3501c02a75679b1d734d4ee.tar.bz2
Part 1 of dragging extensions on the shelf. This part was just about getting the mechanics of the shelf handle and the dragging correct. Part 2 will actually allow the order to be changed.TEST=hover over an extension toolstrip, grab the handle and drag. release.
Review URL: http://codereview.chromium.org/119103 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17559 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_shelf.cc154
-rw-r--r--chrome/browser/extensions/extension_shelf.h8
-rw-r--r--chrome/browser/extensions/extension_view.cc9
-rw-r--r--chrome/browser/views/browser_bubble.cc48
-rw-r--r--chrome/browser/views/browser_bubble.h22
-rw-r--r--chrome/browser/views/browser_bubble_win.cc27
-rw-r--r--chrome/browser/views/frame/browser_view.cc4
7 files changed, 229 insertions, 43 deletions
diff --git a/chrome/browser/extensions/extension_shelf.cc b/chrome/browser/extensions/extension_shelf.cc
index 0c56833..782ddff 100644
--- a/chrome/browser/extensions/extension_shelf.cc
+++ b/chrome/browser/extensions/extension_shelf.cc
@@ -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 int kHandlePadding = 4;
// 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 @@ class ExtensionShelfHandle : public views::View {
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 @@ void ExtensionShelfHandle::Paint(gfx::Canvas* canvas) {
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,6 +171,42 @@ void ExtensionShelfHandle::OnMouseExited(const views::MouseEvent& event) {
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);
+ }
+}
////////////////////////////////////////////////
@@ -144,7 +215,8 @@ ExtensionShelf::ExtensionShelf(Browser* browser)
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::OnMouseEntered(const views::MouseEvent& event) {
}
void ExtensionShelf::OnMouseExited(const views::MouseEvent& event) {
- HideShelfHandle(100);
+ HideShelfHandle(kHideDelayMs);
}
void ExtensionShelf::Observe(NotificationType type,
@@ -332,6 +404,9 @@ bool ExtensionShelf::HasExtensionViews() {
}
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::OnExtensionMouseEvent(ExtensionView* view) {
}
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 @@ void ExtensionShelf::BubbleBrowserWindowClosed(BrowserBubble* bubble) {
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::InitBackground(gfx::Canvas* canvas,
}
void ExtensionShelf::ShowShelfHandle() {
+ if (drag_placeholder_view_)
+ return;
if (!timer_factory_.empty())
timer_factory_.RevokeAll();
if (handle_visible_) {
@@ -410,6 +534,8 @@ void ExtensionShelf::DoShowShelfHandle() {
}
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 @@ void ExtensionShelf::DoHideShelfHandle() {
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;
}
}
diff --git a/chrome/browser/extensions/extension_shelf.h b/chrome/browser/extensions/extension_shelf.h
index 59ef31d..7ca5cd6 100644
--- a/chrome/browser/extensions/extension_shelf.h
+++ b/chrome/browser/extensions/extension_shelf.h
@@ -55,6 +55,11 @@ class ExtensionShelf : public views::View,
virtual void BubbleBrowserWindowMoved(BrowserBubble* bubble);
virtual void BubbleBrowserWindowClosed(BrowserBubble* bubble);
+ // Dragging toolstrips
+ void DragExtension();
+ void DropExtension(const gfx::Point& pt, bool cancel);
+ void DragHandleTo(const gfx::Point& pt);
+
protected:
// View
virtual void ChildPreferredSizeChanged(View* child);
@@ -96,6 +101,9 @@ class ExtensionShelf : public views::View,
// Timers for tracking mouse hovering.
ScopedRunnableMethodFactory<ExtensionShelf> timer_factory_;
+ // A placeholder for a pending drag
+ View* drag_placeholder_view_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionShelf);
};
diff --git a/chrome/browser/extensions/extension_view.cc b/chrome/browser/extensions/extension_view.cc
index 763924d..40b6d76 100644
--- a/chrome/browser/extensions/extension_view.cc
+++ b/chrome/browser/extensions/extension_view.cc
@@ -42,13 +42,11 @@ void ExtensionView::SetVisible(bool is_visible) {
void ExtensionView::DidChangeBounds(const gfx::Rect& previous,
const gfx::Rect& current) {
+ View::DidChangeBounds(previous, current);
// Propagate the new size to RenderWidgetHostView.
// We can't send size zero because RenderWidget DCHECKs that.
if (render_view_host()->view() && !current.IsEmpty())
render_view_host()->view()->SetSize(gfx::Size(width(), height()));
- // Layout is where the HWND is properly positioned.
- // TODO(erikkay) - perhaps this should be in NativeViewHost
- Layout();
}
void ExtensionView::ShowIfCompletelyLoaded() {
@@ -86,9 +84,8 @@ void ExtensionView::ViewHierarchyChanged(bool is_add,
NativeViewHost::ViewHierarchyChanged(is_add, parent, child);
if (is_add && GetWidget() && !initialized_) {
initialized_ = true;
-
- RenderWidgetHostView* view = RenderWidgetHostView::CreateViewForWidget(
- render_view_host());
+ RenderWidgetHostView* view =
+ RenderWidgetHostView::CreateViewForWidget(render_view_host());
// TODO(mpcomplete): RWHV needs a cross-platform Init function.
#if defined(OS_WIN)
diff --git a/chrome/browser/views/browser_bubble.cc b/chrome/browser/views/browser_bubble.cc
index cfab7ca..1df878d 100644
--- a/chrome/browser/views/browser_bubble.cc
+++ b/chrome/browser/views/browser_bubble.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/views/browser_bubble.h"
#include "app/l10n_util.h"
+#include "chrome/browser/views/frame/browser_view.h"
#include "views/widget/root_view.h"
BrowserBubble::BrowserBubble(views::View* view, views::Widget* frame,
@@ -12,14 +13,47 @@ BrowserBubble::BrowserBubble(views::View* view, views::Widget* frame,
: frame_(frame),
view_(view),
visible_(false),
- delegate_(NULL) {
+ delegate_(NULL),
+ attached_(false) {
+ frame_native_view_ = frame_->GetNativeView();
gfx::Size size = view->GetPreferredSize();
bounds_.SetRect(origin.x(), origin.y(), size.width(), size.height());
InitPopup();
}
BrowserBubble::~BrowserBubble() {
- DestroyPopup();
+ DCHECK(!attached_);
+ popup_->CloseNow();
+ // Don't call DetachFromBrowser from here. It needs to talk to the
+ // BrowserView to deregister itself, and if BrowserBubble is owned
+ // by a child of BrowserView, then it's possible that this stack frame
+ // is a descendant of BrowserView's destructor, which leads to problems.
+ // In that case, Detach doesn't need to get called anyway since BrowserView
+ // will do the necessary cleanup.
+}
+
+void BrowserBubble::DetachFromBrowser() {
+ DCHECK(attached_);
+ if (!attached_)
+ return;
+ attached_ = false;
+ BrowserView* browser_view =
+ BrowserView::GetBrowserViewForNativeWindow(frame_native_view_);
+ if (browser_view)
+ browser_view->DetachBrowserBubble(this);
+}
+
+void BrowserBubble::AttachToBrowser() {
+ DCHECK(!attached_);
+ if (attached_)
+ return;
+ BrowserView* browser_view =
+ BrowserView::GetBrowserViewForNativeWindow(frame_native_view_);
+ DCHECK(browser_view);
+ if (browser_view) {
+ browser_view->AttachBrowserBubble(this);
+ attached_ = true;
+ }
}
void BrowserBubble::BrowserWindowMoved() {
@@ -48,6 +82,10 @@ void BrowserBubble::SetBounds(int x, int y, int w, int h) {
Reposition();
}
+void BrowserBubble::MoveTo(int x, int y) {
+ SetBounds(x, y, bounds_.width(), bounds_.height());
+}
+
void BrowserBubble::Reposition() {
gfx::Point top_left;
views::View::ConvertPointToScreen(frame_->GetRootView(), &top_left);
@@ -56,3 +94,9 @@ void BrowserBubble::Reposition() {
bounds_.width(),
bounds_.height());
}
+
+void BrowserBubble::ResizeToView() {
+ gfx::Size size = view_->GetPreferredSize();
+ SetBounds(bounds_.x(), bounds_.y(), size.width(), size.height());
+}
+
diff --git a/chrome/browser/views/browser_bubble.h b/chrome/browser/views/browser_bubble.h
index 49ce7fe..702fb1b 100644
--- a/chrome/browser/views/browser_bubble.h
+++ b/chrome/browser/views/browser_bubble.h
@@ -30,6 +30,15 @@ class BrowserBubble {
const gfx::Point& origin);
virtual ~BrowserBubble();
+ // Call manually if you need to detach the bubble from tracking the browser's
+ // position. Note that you must call this manually before deleting this
+ // object since it can't be safely called from the destructor.
+ void DetachFromBrowser();
+
+ // Normally called automatically during construction, but if DetachFromBrowser
+ // has been called manually, then this call will reattach.
+ void AttachToBrowser();
+
// Get/Set the delegate.
Delegate* delegate() const { return delegate_; }
void set_delegate(Delegate* del) { delegate_ = del; }
@@ -49,6 +58,7 @@ class BrowserBubble {
// Set the bounds of the bubble relative to the browser window.
void SetBounds(int x, int y, int w, int h);
+ void MoveTo(int x, int y);
int width() { return bounds_.width(); }
int height() { return bounds_.height(); }
@@ -56,24 +66,25 @@ class BrowserBubble {
// we have to manually position it when the browser window moves.
void Reposition();
+ // Resize the bubble to fit the view.
+ void ResizeToView();
+
protected:
// Create the popup widget.
virtual void InitPopup();
- // Destroy the popup widget.
- virtual void DestroyPopup();
-
// Move the popup to an absolute position.
void MovePopup(int x, int y, int w, int h);
private:
// The frame that this bubble is attached to.
views::Widget* frame_;
+ gfx::NativeView frame_native_view_;
// The view that is displayed in this bubble.
views::View* view_;
- // The actual widget that this bubble is in.
+ // The widget that this bubble is in.
scoped_ptr<views::Widget> popup_;
// The bounds relative to the frame.
@@ -85,6 +96,9 @@ class BrowserBubble {
// The delegate isn't owned by the bubble.
Delegate* delegate_;
+ // Is the bubble attached to a Browser window.
+ bool attached_;
+
DISALLOW_COPY_AND_ASSIGN(BrowserBubble);
};
diff --git a/chrome/browser/views/browser_bubble_win.cc b/chrome/browser/views/browser_bubble_win.cc
index 6cedf6a..7b47fe9 100644
--- a/chrome/browser/views/browser_bubble_win.cc
+++ b/chrome/browser/views/browser_bubble_win.cc
@@ -10,33 +10,28 @@
#include "views/window/window.h"
void BrowserBubble::InitPopup() {
- gfx::NativeView native_view = frame_->GetNativeView();
gfx::NativeWindow native_window = frame_->GetWindow()->GetNativeWindow();
views::WidgetWin* pop = new views::WidgetWin();
pop->set_delete_on_destroy(false);
pop->set_window_style(WS_POPUP);
+#if 0
+ // TODO(erikkay) Layered windows don't draw child windows.
+ // Apparently there's some tricks you can do to handle that.
+ // Do the research so we can use this.
pop->set_window_ex_style(WS_EX_LAYERED |
- WS_EX_TOOLWINDOW |
l10n_util::GetExtendedTooltipStyles());
pop->SetOpacity(0xFF);
- pop->Init(native_view, bounds_, false);
+#endif
+ // A focus manager is necessary if you want to be able to handle various
+ // mouse events properly.
+ pop->Init(frame_native_view_,
+ bounds_,
+ true); // Give the widget a focus manager.
pop->SetContentsView(view_);
popup_.reset(pop);
Reposition();
- BrowserView* browser_view =
- BrowserView::GetBrowserViewForNativeWindow(native_window);
- DCHECK(browser_view);
- if (browser_view)
- browser_view->AttachBrowserBubble(this);
-}
-
-void BrowserBubble::DestroyPopup() {
- gfx::NativeWindow native_window = frame_->GetWindow()->GetNativeWindow();
- BrowserView* browser_view =
- BrowserView::GetBrowserViewForNativeWindow(native_window);
- if (browser_view)
- browser_view->DetachBrowserBubble(this);
+ AttachToBrowser();
}
void BrowserBubble::MovePopup(int x, int y, int w, int h) {
diff --git a/chrome/browser/views/frame/browser_view.cc b/chrome/browser/views/frame/browser_view.cc
index 3714952..078faac 100644
--- a/chrome/browser/views/frame/browser_view.cc
+++ b/chrome/browser/views/frame/browser_view.cc
@@ -567,7 +567,9 @@ void BrowserView::AttachBrowserBubble(BrowserBubble* bubble) {
}
void BrowserView::DetachBrowserBubble(BrowserBubble* bubble) {
- browser_bubbles_.erase(bubble);
+ BubbleSet::iterator it = browser_bubbles_.find(bubble);
+ if (it != browser_bubbles_.end())
+ browser_bubbles_.erase(it);
}
///////////////////////////////////////////////////////////////////////////////