summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-12 00:47:43 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-12 00:47:43 +0000
commit727079c951951b16c012788ac91e85fde2920c2c (patch)
tree22342b9f73a24911323e1aef35781bcbad12d5f7
parent0d4f8f71ef1f07376f1b005981ecb6ef35921331 (diff)
downloadchromium_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.cc4
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.cc4
-rw-r--r--chrome/browser/extensions/extension_toolbar_model.h2
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.cc192
-rw-r--r--chrome/browser/gtk/browser_actions_toolbar_gtk.h43
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);