diff options
author | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 23:00:09 +0000 |
---|---|---|
committer | erikkay@google.com <erikkay@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-06-03 23:00:09 +0000 |
commit | 41291b0e3555aa77d3501c02a75679b1d734d4ee (patch) | |
tree | 07a73989105d161f7a02cd7f9d87ca14827005a3 | |
parent | 5e9877eb405ec97622d419a146b9b7c331568bf6 (diff) | |
download | chromium_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.cc | 154 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_shelf.h | 8 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_view.cc | 9 | ||||
-rw-r--r-- | chrome/browser/views/browser_bubble.cc | 48 | ||||
-rw-r--r-- | chrome/browser/views/browser_bubble.h | 22 | ||||
-rw-r--r-- | chrome/browser/views/browser_bubble_win.cc | 27 | ||||
-rw-r--r-- | chrome/browser/views/frame/browser_view.cc | 4 |
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); } /////////////////////////////////////////////////////////////////////////////// |