diff options
author | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 00:47:43 +0000 |
---|---|---|
committer | estade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-12 00:47:43 +0000 |
commit | 727079c951951b16c012788ac91e85fde2920c2c (patch) | |
tree | 22342b9f73a24911323e1aef35781bcbad12d5f7 | |
parent | 0d4f8f71ef1f07376f1b005981ecb6ef35921331 (diff) | |
download | chromium_src-727079c951951b16c012788ac91e85fde2920c2c.zip chromium_src-727079c951951b16c012788ac91e85fde2920c2c.tar.gz chromium_src-727079c951951b16c012788ac91e85fde2920c2c.tar.bz2 |
GTK: more browser action toolbar functionality.
- Resize smartly after dragging (animate to the "right" size)
- Add the overflow menu (it is clickable, but you can't drag to/from it)
- Resize appropriately after adding/removing buttons
still TODO:
- persist the #icons that are visible
BUG=32101
TEST=manual
Review URL: http://codereview.chromium.org/897001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41372 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | app/gfx/gtk_util.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_toolbar_model.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_toolbar_model.h | 2 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_actions_toolbar_gtk.cc | 192 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_actions_toolbar_gtk.h | 43 |
5 files changed, 207 insertions, 38 deletions
diff --git a/app/gfx/gtk_util.cc b/app/gfx/gtk_util.cc index 03d0504..98b174b 100644 --- a/app/gfx/gtk_util.cc +++ b/app/gfx/gtk_util.cc @@ -29,7 +29,11 @@ const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00); const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00); GdkPixbuf* GdkPixbufFromSkBitmap(const SkBitmap* bitmap) { + if (bitmap->isNull()) + return NULL; + bitmap->lockPixels(); + int width = bitmap->width(); int height = bitmap->height(); int stride = bitmap->rowBytes(); diff --git a/chrome/browser/extensions/extension_toolbar_model.cc b/chrome/browser/extensions/extension_toolbar_model.cc index 2dd23d47..ab16d5b 100644 --- a/chrome/browser/extensions/extension_toolbar_model.cc +++ b/chrome/browser/extensions/extension_toolbar_model.cc @@ -188,6 +188,10 @@ void ExtensionToolbarModel::UpdatePrefs() { service_->extension_prefs()->SetToolbarOrder(ids); } +Extension* ExtensionToolbarModel::GetExtensionByIndex(int index) const { + return toolitems_.at(index); +} + int ExtensionToolbarModel::IncognitoIndexToOriginal(int incognito_index) { int original_index = 0, i = 0; for (ExtensionList::iterator iter = begin(); iter != end(); diff --git a/chrome/browser/extensions/extension_toolbar_model.h b/chrome/browser/extensions/extension_toolbar_model.h index d930c86..2341493b5 100644 --- a/chrome/browser/extensions/extension_toolbar_model.h +++ b/chrome/browser/extensions/extension_toolbar_model.h @@ -53,6 +53,8 @@ class ExtensionToolbarModel : public NotificationObserver { return toolitems_.end(); } + Extension* GetExtensionByIndex(int index) const; + // Utility functions for converting between an index into the list of // incognito-enabled browser actions, and the list of all browser actions. int IncognitoIndexToOriginal(int incognito_index); diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc index a20a4921..4e2aa8a 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.cc @@ -55,38 +55,8 @@ GtkTargetEntry GetDragTargetEntry() { // The minimum width in pixels of the button hbox if |icon_count| icons are // showing. gint WidthForIconCount(gint icon_count) { - return (kButtonSize + kButtonPadding) * icon_count - kButtonPadding; -} - -// These three signal handlers are used to give the gripper the resize -// cursor. Since it doesn't have its own window, we have to set the cursor -// whenever the pointer moves into the button or leaves the button, and be -// sure to leave it on when the user is dragging. -gboolean OnGripperEnterNotify(GtkWidget* gripper, - GdkEventCrossing* event, - gpointer unused) { - gdk_window_set_cursor(gripper->window, - gtk_util::GetCursor(GDK_SB_H_DOUBLE_ARROW)); - return FALSE; -} - -gboolean OnGripperLeaveNotify(GtkWidget* gripper, - GdkEventCrossing* event, - gpointer unused) { - if (!(event->state & GDK_BUTTON1_MASK)) - gdk_window_set_cursor(gripper->window, NULL); - return FALSE; -} - -gboolean OnGripperButtonRelease(GtkWidget* gripper, - GdkEventButton* event, - gpointer unused) { - gfx::Rect gripper_rect(0, 0, - gripper->allocation.width, gripper->allocation.height); - gfx::Point release_point(event->x, event->y); - if (!gripper_rect.Contains(release_point)) - gdk_window_set_cursor(gripper->window, NULL); - return FALSE; + return std::max((kButtonSize + kButtonPadding) * icon_count - kButtonPadding, + 0); } } // namespace @@ -162,8 +132,10 @@ class BrowserActionButton : public NotificationObserver, // ImageLoadingTracker::Observer implementation. void OnImageLoaded(SkBitmap* image, size_t index) { - if (image) + if (image) { + default_skbitmap_ = *image; default_icon_ = gfx::GdkPixbufFromSkBitmap(image); + } tracker_ = NULL; // The tracker object will delete itself when we return. UpdateState(); } @@ -194,6 +166,16 @@ class BrowserActionButton : public NotificationObserver, gtk_widget_queue_draw(button_.get()); } + SkBitmap GetIcon() { + const SkBitmap& image = extension_->browser_action()->GetIcon( + toolbar_->GetCurrentTabId()); + if (!image.isNull()) { + return image; + } else { + return default_skbitmap_; + } + } + private: void SetImage(GdkPixbuf* image) { gtk_button_set_image(GTK_BUTTON(button_.get()), @@ -229,7 +211,7 @@ class BrowserActionButton : public NotificationObserver, if (browser_action->HasPopup(tab_id)) { ExtensionPopupGtk::Show( - action->extension_->browser_action()->GetPopupUrl(tab_id), + browser_action->GetPopupUrl(tab_id), action->toolbar_->browser(), gtk_util::GetWidgetRectRelativeToToplevel(widget)); } else { @@ -283,6 +265,9 @@ class BrowserActionButton : public NotificationObserver, // If the browser action has a default icon, it will be here. GdkPixbuf* default_icon_; + // Same as |default_icon_|, but stored as SkBitmap. + SkBitmap default_skbitmap_; + NotificationRegistrar registrar_; // The context menu view and model for this extension action. @@ -302,6 +287,9 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser) overflow_button_(browser->profile()), drag_button_(NULL), drop_index_(-1), + resize_animation_(this), + desired_width_(0), + start_width_(0), method_factory_(this) { ExtensionsService* extension_service = profile_->GetExtensionsService(); // The |extension_service| can be NULL in Incognito. @@ -316,11 +304,15 @@ BrowserActionsToolbarGtk::BrowserActionsToolbarGtk(Browser* browser) g_signal_connect(gripper, "expose-event", G_CALLBACK(OnGripperExposeThunk), this); g_signal_connect(gripper, "enter-notify-event", - G_CALLBACK(OnGripperEnterNotify), NULL); + G_CALLBACK(OnGripperEnterNotifyThunk), this); g_signal_connect(gripper, "leave-notify-event", - G_CALLBACK(OnGripperLeaveNotify), NULL); + G_CALLBACK(OnGripperLeaveNotifyThunk), this); g_signal_connect(gripper, "button-release-event", - G_CALLBACK(OnGripperButtonRelease), NULL); + G_CALLBACK(OnGripperButtonReleaseThunk), this); + g_signal_connect(gripper, "button-press-event", + G_CALLBACK(OnGripperButtonPressThunk), this); + g_signal_connect(overflow_button_.widget(), "button-press-event", + G_CALLBACK(OnOverflowButtonPressThunk), this); gtk_box_pack_start(GTK_BOX(hbox_.get()), gripper, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox_.get()), button_hbox_, TRUE, TRUE, 0); @@ -454,9 +446,26 @@ void BrowserActionsToolbarGtk::HidePopup() { popup->DestroyPopup(); } +void BrowserActionsToolbarGtk::AnimateToShowNIcons(int count) { + desired_width_ = WidthForIconCount(count); + start_width_ = button_hbox_->allocation.width; + resize_animation_.Reset(); + resize_animation_.Show(); +} + +void BrowserActionsToolbarGtk::ButtonAddedOrRemoved() { + // TODO(estade): this is a little bit janky looking when the removed button + // is not the farthest right button. + if (!GTK_WIDGET_VISIBLE(overflow_button_.widget())) { + AnimateToShowNIcons(button_count()); + model_->SetVisibleIconCount(button_count()); + } +} + void BrowserActionsToolbarGtk::BrowserActionAdded(Extension* extension, int index) { CreateButtonForExtension(extension, index); + ButtonAddedOrRemoved(); } void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) { @@ -466,6 +475,7 @@ void BrowserActionsToolbarGtk::BrowserActionRemoved(Extension* extension) { } RemoveButtonForExtension(extension); + ButtonAddedOrRemoved(); } void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension, @@ -487,6 +497,44 @@ void BrowserActionsToolbarGtk::BrowserActionMoved(Extension* extension, gtk_box_reorder_child(GTK_BOX(button_hbox_), button->widget(), index); } +void BrowserActionsToolbarGtk::AnimationProgressed(const Animation* animation) { + int width = start_width_ + (desired_width_ - start_width_) * + animation->GetCurrentValue(); + gtk_widget_set_size_request(button_hbox_, width, -1); + + if (width == desired_width_) + resize_animation_.Reset(); +} + +void BrowserActionsToolbarGtk::AnimationEnded(const Animation* animation) { + gtk_widget_set_size_request(button_hbox_, desired_width_, -1); +} + +void BrowserActionsToolbarGtk::ExecuteCommandById(int command_id) { + Extension* extension = model_->GetExtensionByIndex(command_id); + ExtensionAction* browser_action = extension->browser_action(); + + int tab_id = GetCurrentTabId(); + if (tab_id < 0) { + NOTREACHED() << "No current tab."; + return; + } + + if (browser_action->HasPopup(tab_id)) { + ExtensionPopupGtk::Show( + browser_action->GetPopupUrl(tab_id), browser(), + gtk_util::GetWidgetRectRelativeToToplevel(overflow_button_.widget())); + } else { + ExtensionBrowserEventRouter::GetInstance()->BrowserActionExecuted( + browser()->profile(), extension->id(), browser()); + } +} + +void BrowserActionsToolbarGtk::StoppedShowing() { + gtk_chrome_button_unset_paint_state( + GTK_CHROME_BUTTON(overflow_button_.widget())); +} + void BrowserActionsToolbarGtk::DragStarted(BrowserActionButton* button, GdkDragContext* drag_context) { // No representation of the widget following the cursor. @@ -614,3 +662,73 @@ gboolean BrowserActionsToolbarGtk::OnGripperExpose(GtkWidget* gripper, return TRUE; } + +// These three signal handlers (EnterNotify, LeaveNotify, and ButtonRelease) +// are used to give the gripper the resize cursor. Since it doesn't have its +// own window, we have to set the cursor whenever the pointer moves into the +// button or leaves the button, and be sure to leave it on when the user is +// dragging. +gboolean BrowserActionsToolbarGtk::OnGripperEnterNotify( + GtkWidget* gripper, GdkEventCrossing* event) { + gdk_window_set_cursor(gripper->window, + gtk_util::GetCursor(GDK_SB_H_DOUBLE_ARROW)); + return FALSE; +} + +gboolean BrowserActionsToolbarGtk::OnGripperLeaveNotify( + GtkWidget* gripper, GdkEventCrossing* event) { + if (!(event->state & GDK_BUTTON1_MASK)) + gdk_window_set_cursor(gripper->window, NULL); + return FALSE; +} + +gboolean BrowserActionsToolbarGtk::OnGripperButtonRelease( + GtkWidget* gripper, GdkEventButton* event) { + gfx::Rect gripper_rect(0, 0, + gripper->allocation.width, gripper->allocation.height); + gfx::Point release_point(event->x, event->y); + if (!gripper_rect.Contains(release_point)) + gdk_window_set_cursor(gripper->window, NULL); + + // After the user resizes the toolbar, we want to smartly resize it to be + // the perfect size to fit the buttons. + int visible_icon_count = + gtk_chrome_shrinkable_hbox_get_visible_child_count( + GTK_CHROME_SHRINKABLE_HBOX(button_hbox_)); + AnimateToShowNIcons(visible_icon_count); + + return FALSE; +} + +gboolean BrowserActionsToolbarGtk::OnGripperButtonPress( + GtkWidget* gripper, GdkEventButton* event) { + resize_animation_.Reset(); + + return FALSE; +} + +gboolean BrowserActionsToolbarGtk::OnOverflowButtonPress( + GtkWidget* overflow, GdkEventButton* event) { + overflow_menu_.reset(new MenuGtk(this)); + + int visible_icon_count = + gtk_chrome_shrinkable_hbox_get_visible_child_count( + GTK_CHROME_SHRINKABLE_HBOX(button_hbox_)); + for (int i = visible_icon_count; i < static_cast<int>(model_->size()); ++i) { + Extension* extension = model_->GetExtensionByIndex(i); + BrowserActionButton* button = extension_button_map_[extension->id()].get(); + + overflow_menu_->AppendMenuItemWithIcon( + i, + extension->name(), + button->GetIcon()); + + // TODO(estade): set the menu item's tooltip. + } + + gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(overflow), + GTK_STATE_ACTIVE); + overflow_menu_->PopupAsFromKeyEvent(overflow); + + return FALSE; +} diff --git a/chrome/browser/gtk/browser_actions_toolbar_gtk.h b/chrome/browser/gtk/browser_actions_toolbar_gtk.h index 6ef7d0a..e2d9739 100644 --- a/chrome/browser/gtk/browser_actions_toolbar_gtk.h +++ b/chrome/browser/gtk/browser_actions_toolbar_gtk.h @@ -11,9 +11,11 @@ #include <string> #include "app/gtk_signal.h" +#include "app/slide_animation.h" #include "base/linked_ptr.h" #include "base/task.h" #include "chrome/browser/extensions/extension_toolbar_model.h" +#include "chrome/browser/gtk/menu_gtk.h" #include "chrome/browser/gtk/overflow_button.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -27,7 +29,9 @@ class Profile; typedef struct _GtkWidget GtkWidget; -class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { +class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer, + public AnimationDelegate, + public MenuGtk::Delegate { public: explicit BrowserActionsToolbarGtk(Browser* browser); virtual ~BrowserActionsToolbarGtk(); @@ -73,6 +77,14 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { // Hide the extension popup, if any. void HidePopup(); + // Animate the toolbar to show the given number of icons. This assumes the + // visibility of the overflow button will not change. + void AnimateToShowNIcons(int count); + + // If the toolbar is at max width and a button is added or removed, then make + // sure it stays at the max width. + void ButtonAddedOrRemoved(); + // Returns true if this extension should be shown in this toolbar. This can // return false if we are in an incognito window and the extension is disabled // for incognito. @@ -83,6 +95,17 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { virtual void BrowserActionRemoved(Extension* extension); virtual void BrowserActionMoved(Extension* extension, int index); + // AnimationDelegate implementation. + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationEnded(const Animation* animation); + + // MenuGtk::Delegate implementation. + // In our case, |command_id| is be the index into the model's extension list. + virtual bool IsCommandEnabled(int command_id) const { return true; } + virtual void ExecuteCommandById(int command_id); + virtual void StoppedShowing(); + virtual bool AlwaysShowImages() const { return true; } + // Called by the BrowserActionButton in response to drag-begin. void DragStarted(BrowserActionButton* button, GdkDragContext* drag_context); @@ -103,6 +126,16 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { OnGripperMotionNotify, GdkEventMotion*); CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, OnGripperExpose, GdkEventExpose*); + CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, + OnGripperEnterNotify, GdkEventCrossing*); + CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, + OnGripperLeaveNotify, GdkEventCrossing*); + CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, + OnGripperButtonRelease, GdkEventButton*); + CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, + OnGripperButtonPress, GdkEventButton*); + CHROMEGTK_CALLBACK_1(BrowserActionsToolbarGtk, gboolean, + OnOverflowButtonPress, GdkEventButton*); Browser* browser_; @@ -118,6 +151,7 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { GtkWidget* button_hbox_; OverflowButton overflow_button_; + scoped_ptr<MenuGtk> overflow_menu_; // The button that is currently being dragged, or NULL. BrowserActionButton* drag_button_; @@ -132,6 +166,13 @@ class BrowserActionsToolbarGtk : public ExtensionToolbarModel::Observer { ExtensionButtonMap; ExtensionButtonMap extension_button_map_; + // We use this animation for the smart resizing of the toolbar. + SlideAnimation resize_animation_; + // This is the final width we are animating towards. + int desired_width_; + // This is the width we were at when we started animating. + int start_width_; + ScopedRunnableMethodFactory<BrowserActionsToolbarGtk> method_factory_; DISALLOW_COPY_AND_ASSIGN(BrowserActionsToolbarGtk); |