diff options
author | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-30 17:06:21 +0000 |
---|---|---|
committer | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-30 17:06:21 +0000 |
commit | 7209cb729d172a9aa8999dac0c9f6bd80f23c4c2 (patch) | |
tree | 5b0c62d482856faf0982378528031c1c8c4c9eff /chrome/browser | |
parent | 7838c2502799cbfe33ba16575b0e7d0b03740dd1 (diff) | |
download | chromium_src-7209cb729d172a9aa8999dac0c9f6bd80f23c4c2.zip chromium_src-7209cb729d172a9aa8999dac0c9f6bd80f23c4c2.tar.gz chromium_src-7209cb729d172a9aa8999dac0c9f6bd80f23c4c2.tar.bz2 |
GTK: Cleanups to the new wrench menu.
- Make the rendering for multiple buttons pretty by unifying sequnces of
buttons.
- Add the zoom label control and make the wrench menu model listen for
notifications about zoom percentage changing.
- Fixes crash that would have gone away once this was taken out from behind a flag
BUG=45757
TEST=none
Review URL: http://codereview.chromium.org/2799043
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51266 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/gtk/browser_toolbar_gtk.cc | 5 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_custom_menu_item.cc | 130 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_custom_menu_item.h | 11 | ||||
-rw-r--r-- | chrome/browser/gtk/menu_gtk.cc | 59 | ||||
-rw-r--r-- | chrome/browser/gtk/menu_gtk.h | 3 | ||||
-rw-r--r-- | chrome/browser/tabs/tab_strip_model.cc | 3 | ||||
-rw-r--r-- | chrome/browser/tabs/tab_strip_model.h | 4 | ||||
-rw-r--r-- | chrome/browser/wrench_menu_model.cc | 43 | ||||
-rw-r--r-- | chrome/browser/wrench_menu_model.h | 12 |
9 files changed, 249 insertions, 21 deletions
diff --git a/chrome/browser/gtk/browser_toolbar_gtk.cc b/chrome/browser/gtk/browser_toolbar_gtk.cc index 907f1f5..e195ee1 100644 --- a/chrome/browser/gtk/browser_toolbar_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_gtk.cc @@ -758,7 +758,10 @@ void BrowserToolbarGtk::PopupForButtonNextTo(GtkWidget* button, GtkMenuDirectionType dir) { GtkWidget* other_button = button == page_menu_button_.get() ? app_menu_button_.get() : page_menu_button_.get(); - PopupForButton(other_button); + // TODO(erg): When we move to wrench menu only, remove |menu_bar_helper_| so + // this won't be necessary. + if (other_button) + PopupForButton(other_button); } void BrowserToolbarGtk::AnimationEnded(const Animation* animation) { diff --git a/chrome/browser/gtk/gtk_custom_menu_item.cc b/chrome/browser/gtk/gtk_custom_menu_item.cc index f925c48..8c9c923 100644 --- a/chrome/browser/gtk/gtk_custom_menu_item.cc +++ b/chrome/browser/gtk/gtk_custom_menu_item.cc @@ -4,6 +4,7 @@ #include "chrome/browser/gtk/gtk_custom_menu_item.h" +#include "base/i18n/rtl.h" #include "base/logging.h" #include "chrome/browser/gtk/gtk_custom_menu.h" @@ -27,9 +28,23 @@ static void set_selected(GtkCustomMenuItem* item, GtkWidget* selected) { } } +// When GtkButtons set the label text, they rebuild the widget hierarchy each +// and every time. Therefore, we can't just fish out the label from the button +// and set some properties; we have to create this callback function that +// listens on the button's "notify" signal, which is emitted right after the +// label has been (re)created. (Label values can change dynamically.) +static void on_button_label_set(GObject* object) { + GtkButton* button = GTK_BUTTON(object); + gtk_widget_set_sensitive(GTK_BIN(button)->child, FALSE); + gtk_misc_set_padding(GTK_MISC(GTK_BIN(button)->child), 2, 0); +} + static void gtk_custom_menu_item_finalize(GObject *object); static gint gtk_custom_menu_item_expose(GtkWidget* widget, GdkEventExpose* event); +static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget, + GdkEventExpose* event, + GtkCustomMenuItem* menu_item); static void gtk_custom_menu_item_select(GtkItem *item); static void gtk_custom_menu_item_deselect(GtkItem *item); static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item); @@ -56,6 +71,7 @@ static void gtk_custom_menu_item_style_set(GtkCustomMenuItem* item, } static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) { + item->all_widgets = NULL; item->button_widgets = NULL; item->currently_selected_button = NULL; item->previously_selected_button = NULL; @@ -73,6 +89,10 @@ static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) { g_signal_connect(item, "style-set", G_CALLBACK(gtk_custom_menu_item_style_set), NULL); + g_signal_connect(item->hbox, "expose-event", + G_CALLBACK(gtk_custom_menu_item_hbox_expose), + item); + gtk_widget_show_all(menu_hbox); } @@ -103,6 +123,7 @@ static void gtk_custom_menu_item_class_init(GtkCustomMenuItemClass* klass) { static void gtk_custom_menu_item_finalize(GObject *object) { GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(object); + g_list_free(item->all_widgets); g_list_free(item->button_widgets); G_OBJECT_CLASS(gtk_custom_menu_item_parent_class)->finalize(object); @@ -123,6 +144,97 @@ static gint gtk_custom_menu_item_expose(GtkWidget* widget, return FALSE; } +static void gtk_custom_menu_item_expose_button(GtkWidget* hbox, + GdkEventExpose* event, + GList* button_item) { + // We search backwards to find the leftmost and rightmost buttons. The + // current button may be that button. + GtkWidget* current_button = GTK_WIDGET(button_item->data); + GtkWidget* first_button = current_button; + for (GList* i = button_item; i && GTK_IS_BUTTON(i->data); + i = g_list_previous(i)) { + first_button = GTK_WIDGET(i->data); + } + + GtkWidget* last_button = current_button; + for (GList* i = button_item; i && GTK_IS_BUTTON(i->data); + i = g_list_next(i)) { + last_button = GTK_WIDGET(i->data); + } + + if (base::i18n::IsRTL()) + std::swap(first_button, last_button); + + int x = first_button->allocation.x; + int y = first_button->allocation.y; + int width = last_button->allocation.width + last_button->allocation.x - + first_button->allocation.x; + int height = last_button->allocation.height; + + gtk_paint_box(hbox->style, hbox->window, + static_cast<GtkStateType>( + GTK_WIDGET_STATE(current_button)), + GTK_SHADOW_OUT, + ¤t_button->allocation, hbox, "button", + x, y, width, height); + + // Propagate to the button's children. + gtk_container_propagate_expose( + GTK_CONTAINER(current_button), + gtk_bin_get_child(GTK_BIN(current_button)), + event); +} + +static gboolean gtk_custom_menu_item_hbox_expose(GtkWidget* widget, + GdkEventExpose* event, + GtkCustomMenuItem* menu_item) { + // First render all the buttons that aren't the currently selected item. + for (GList* current_item = menu_item->all_widgets; + current_item != NULL; current_item = g_list_next(current_item)) { + if (GTK_IS_BUTTON(current_item->data)) { + if (GTK_WIDGET(current_item->data) != + menu_item->currently_selected_button) { + gtk_custom_menu_item_expose_button(widget, event, current_item); + } + } + } + + // As a separate pass, draw the buton separators above. We need to draw the + // separators in a separate pass because we are drawing on top of the + // buttons. Otherwise, the vlines are overwritten by the next button. + for (GList* current_item = menu_item->all_widgets; + current_item != NULL; current_item = g_list_next(current_item)) { + if (GTK_IS_BUTTON(current_item->data)) { + // Check to see if this is the last button in a run. + GList* next_item = g_list_next(current_item); + if (next_item && GTK_IS_BUTTON(next_item->data)) { + GtkWidget* current_button = GTK_WIDGET(current_item->data); + GtkAllocation child_alloc = + gtk_bin_get_child(GTK_BIN(current_button))->allocation; + int half_offset = widget->style->xthickness / 2; + gtk_paint_vline(widget->style, widget->window, + static_cast<GtkStateType>( + GTK_WIDGET_STATE(current_button)), + &event->area, widget, "button", + child_alloc.y, + child_alloc.y + child_alloc.height, + current_button->allocation.x + + current_button->allocation.width - half_offset); + } + } + } + + // Finally, draw the selected item on top of the separators so there are no + // artifacts inside the button area. + GList* selected = g_list_find(menu_item->all_widgets, + menu_item->currently_selected_button); + if (selected) { + gtk_custom_menu_item_expose_button(widget, event, selected); + } + + return TRUE; +} + static void gtk_custom_menu_item_select(GtkItem* item) { GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item); @@ -185,7 +297,23 @@ GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item, gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0); gtk_widget_show(button); + menu_item->all_widgets = g_list_append(menu_item->all_widgets, button); menu_item->button_widgets = g_list_append(menu_item->button_widgets, button); + + return button; +} + +GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item, + int command_id) { + GtkWidget* button = gtk_button_new_with_label(""); + g_object_set_data(G_OBJECT(button), "command-id", + GINT_TO_POINTER(command_id)); + gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0); + g_signal_connect(button, "notify", G_CALLBACK(on_button_label_set), NULL); + gtk_widget_show(button); + + menu_item->all_widgets = g_list_append(menu_item->all_widgets, button); + return button; } @@ -195,6 +323,8 @@ void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item) { gtk_box_pack_start(GTK_BOX(menu_item->hbox), fixed, FALSE, FALSE, 0); gtk_widget_show(fixed); + + menu_item->all_widgets = g_list_append(menu_item->all_widgets, fixed); } void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item, diff --git a/chrome/browser/gtk/gtk_custom_menu_item.h b/chrome/browser/gtk/gtk_custom_menu_item.h index a7c152b..c89db5d 100644 --- a/chrome/browser/gtk/gtk_custom_menu_item.h +++ b/chrome/browser/gtk/gtk_custom_menu_item.h @@ -62,7 +62,11 @@ struct _GtkCustomMenuItem { // Label on left side of menu item. GtkWidget* label; - // Possible button widgets + // List of all widgets we added. Used to find the leftmost and rightmost + // continuous buttons. + GList* all_widgets; + + // Possible button widgets. Used for keyboard navigation. GList* button_widgets; // The widget that currently has highlight. @@ -86,6 +90,11 @@ GtkWidget* gtk_custom_menu_item_new(const char* title); GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item, int command_id); +// Adds a button to our list of items in the |hbox|, but that isn't part of +// |button_widgets| to prevent it from being activatable. +GtkWidget* gtk_custom_menu_item_add_button_label(GtkCustomMenuItem* menu_item, + int command_id); + // Adds a vertical space in the |hbox|. void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item); diff --git a/chrome/browser/gtk/menu_gtk.cc b/chrome/browser/gtk/menu_gtk.cc index 104b39f..d38b505 100644 --- a/chrome/browser/gtk/menu_gtk.cc +++ b/chrome/browser/gtk/menu_gtk.cc @@ -72,6 +72,33 @@ void OnSubmenuShow(GtkWidget* widget, gpointer user_data) { #endif } +void OnSubmenuShowButtonMenuItem(GtkWidget* widget, GtkButton* button) { + menus::ButtonMenuItemModel* model = + reinterpret_cast<menus::ButtonMenuItemModel*>( + g_object_get_data(G_OBJECT(button), "button-model")); + int index = GPOINTER_TO_INT(g_object_get_data( + G_OBJECT(button), "button-model-id")); + + std::string label = + ConvertAcceleratorsFromWindowsStyle( + UTF16ToUTF8(model->GetLabelAt(index))); + gtk_button_set_label(GTK_BUTTON(button), label.c_str()); +} + +void SetupDynamicLabelMenuButton(GtkWidget* button, + GtkWidget* menu, + menus::ButtonMenuItemModel* model, + int index) { + if (model->IsLabelDynamicAt(index)) { + g_object_set_data(G_OBJECT(button), "button-model", + reinterpret_cast<void*>(model)); + g_object_set_data(G_OBJECT(button), "button-model-id", + GINT_TO_POINTER(index)); + g_signal_connect(menu, "show", G_CALLBACK(OnSubmenuShowButtonMenuItem), + button); + } +} + // Popup menus may get squished if they open up too close to the bottom of the // screen. This function takes the size of the screen, the size of the menu, // an optional widget, the Y position of the mouse click, and adjusts the popup @@ -269,9 +296,7 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) { case menus::MenuModel::TYPE_BUTTON_ITEM: { menus::ButtonMenuItemModel* button_menu_item_model = model->GetButtonMenuItemAt(i); - - menu_item = BuildButtomMenuItem(button_menu_item_model); - + menu_item = BuildButtomMenuItem(button_menu_item_model, menu); connect_to_activate = false; break; } @@ -316,9 +341,17 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) { } } -GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model) { +GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model, + GtkWidget* menu) { GtkWidget* menu_item = gtk_custom_menu_item_new( RemoveWindowsStyleAccelerators(UTF16ToUTF8(model->label())).c_str()); + + // Set up the callback to the model for when it is clicked. + g_object_set_data(G_OBJECT(menu_item), "button-model", + reinterpret_cast<void*>(model)); + g_signal_connect(menu_item, "button-pushed", + G_CALLBACK(OnMenuButtonPressed), this); + for (int i = 0; i < model->GetItemCount(); ++i) { switch (model->GetTypeAt(i)) { case menus::ButtonMenuItemModel::TYPE_SPACE: { @@ -346,17 +379,23 @@ GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model) { UTF16ToUTF8(model->GetLabelAt(i))).c_str()); } - // TODO(erg): Set up dynamic labels here. + SetupDynamicLabelMenuButton(button, menu, model, i); + break; + } + case menus::ButtonMenuItemModel::TYPE_BUTTON_LABEL: { + GtkWidget* button = gtk_custom_menu_item_add_button_label( + GTK_CUSTOM_MENU_ITEM(menu_item), + model->GetCommandIdAt(i)); + gtk_button_set_label( + GTK_BUTTON(button), + RemoveWindowsStyleAccelerators( + UTF16ToUTF8(model->GetLabelAt(i))).c_str()); + SetupDynamicLabelMenuButton(button, menu, model, i); break; } } } - // Set up the callback to the model for when it is clicked. - g_object_set_data(G_OBJECT(menu_item), "button-model", - reinterpret_cast<void*>(model)); - g_signal_connect(menu_item, "button-pushed", - G_CALLBACK(OnMenuButtonPressed), this); return menu_item; } diff --git a/chrome/browser/gtk/menu_gtk.h b/chrome/browser/gtk/menu_gtk.h index a9a703a..721b736 100644 --- a/chrome/browser/gtk/menu_gtk.h +++ b/chrome/browser/gtk/menu_gtk.h @@ -117,7 +117,8 @@ class MenuGtk { // Implementation of the above; called recursively. void BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu); // Builds a menu item with buttons in it from the data in the model. - GtkWidget* BuildButtomMenuItem(menus::ButtonMenuItemModel* model); + GtkWidget* BuildButtomMenuItem(menus::ButtonMenuItemModel* model, + GtkWidget* menu); // Contains implementation for OnMenuShow. void UpdateMenu(); diff --git a/chrome/browser/tabs/tab_strip_model.cc b/chrome/browser/tabs/tab_strip_model.cc index 5304100..32c55fd 100644 --- a/chrome/browser/tabs/tab_strip_model.cc +++ b/chrome/browser/tabs/tab_strip_model.cc @@ -68,6 +68,9 @@ TabStripModel::TabStripModel(TabStripModelDelegate* delegate, Profile* profile) } TabStripModel::~TabStripModel() { + FOR_EACH_OBSERVER(TabStripModelObserver, observers_, + TabStripModelDeleted()); + STLDeleteContainerPointers(contents_data_.begin(), contents_data_.end()); delete order_controller_; } diff --git a/chrome/browser/tabs/tab_strip_model.h b/chrome/browser/tabs/tab_strip_model.h index f9cb205..bfca5ec 100644 --- a/chrome/browser/tabs/tab_strip_model.h +++ b/chrome/browser/tabs/tab_strip_model.h @@ -125,6 +125,10 @@ class TabStripModelObserver { // use this as a trigger to try and close the window containing the // TabStripModel, for example... virtual void TabStripEmpty() {} + + // Sent when the tabstrip model is about to be deleted and any reference held + // must be dropped. + virtual void TabStripModelDeleted() {} }; /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/browser/wrench_menu_model.cc b/chrome/browser/wrench_menu_model.cc index c3d9012..28c7315 100644 --- a/chrome/browser/wrench_menu_model.cc +++ b/chrome/browser/wrench_menu_model.cc @@ -23,6 +23,7 @@ #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/upgrade_detector.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" #include "chrome/common/notification_source.h" #include "chrome/common/notification_type.h" #include "grit/chromium_strings.h" @@ -68,15 +69,22 @@ WrenchMenuModel::WrenchMenuModel(menus::SimpleMenuModel::Delegate* delegate, Browser* browser) : menus::SimpleMenuModel(delegate), delegate_(delegate), - browser_(browser) { + browser_(browser), + tabstrip_model_(browser_->tabstrip_model()) { Build(); UpdateZoomControls(); + tabstrip_model_->AddObserver(this); + registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED, Source<Profile>(browser_->profile())); + registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, + NotificationService::AllSources()); } WrenchMenuModel::~WrenchMenuModel() { + if (tabstrip_model_) + tabstrip_model_->RemoveObserver(this); } static bool CalculateEnabled() { @@ -137,12 +145,12 @@ bool WrenchMenuModel::GetIconAt(int index, SkBitmap* icon) const { } bool WrenchMenuModel::IsLabelForCommandIdDynamic(int command_id) const { - return false; + return command_id == IDC_ZOOM_PERCENT_DISPLAY; } string16 WrenchMenuModel::GetLabelForCommandId(int command_id) const { - // TODO(erg): Hook up percentage calculation once I add that widget. - return string16(); + DCHECK_EQ(IDC_ZOOM_PERCENT_DISPLAY, command_id); + return zoom_label_; } void WrenchMenuModel::ExecuteCommand(int command_id) { @@ -150,10 +158,30 @@ void WrenchMenuModel::ExecuteCommand(int command_id) { delegate_->ExecuteCommand(command_id); } +void WrenchMenuModel::TabSelectedAt(TabContents* old_contents, + TabContents* new_contents, + int index, + bool user_gesture) { + // The user has switched between tabs and the new tab may have a different + // zoom setting. + UpdateZoomControls(); +} + +void WrenchMenuModel::TabReplacedAt(TabContents* old_contents, + TabContents* new_contents, int index) { + UpdateZoomControls(); +} + +void WrenchMenuModel::TabStripModelDeleted() { + // During views shutdown, the tabstrip model/browser is deleted first, while + // it is the opposite in gtk land. + tabstrip_model_->RemoveObserver(this); + tabstrip_model_ = NULL; +} + void WrenchMenuModel::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { - DCHECK_EQ(NotificationType::ZOOM_LEVEL_CHANGED, type.value); UpdateZoomControls(); } @@ -179,6 +207,8 @@ void WrenchMenuModel::Build() { zoom_menu_item_model_.reset( new menus::ButtonMenuItemModel(IDS_ZOOM_MENU, this)); zoom_menu_item_model_->AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS2); + zoom_menu_item_model_->AddButtonLabel(IDC_ZOOM_PERCENT_DISPLAY, + IDS_ZOOM_PLUS2); zoom_menu_item_model_->AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS2); zoom_menu_item_model_->AddSpace(); zoom_menu_item_model_->AddItemWithImage( @@ -247,9 +277,6 @@ void WrenchMenuModel::UpdateZoomControls() { bool enable_increment, enable_decrement; int zoom_percent = static_cast<int>(GetZoom(&enable_increment, &enable_decrement) * 100); - - // TODO(erg): Route the stringified zoom_percent through - // GetLabelForCommandId(). Also make a way to use enable_increment/decrement. zoom_label_ = l10n_util::GetStringFUTF16( IDS_ZOOM_PERCENT, IntToString16(zoom_percent)); } diff --git a/chrome/browser/wrench_menu_model.h b/chrome/browser/wrench_menu_model.h index 5b7d2d5..e9cb1b1 100644 --- a/chrome/browser/wrench_menu_model.h +++ b/chrome/browser/wrench_menu_model.h @@ -11,6 +11,7 @@ #include "app/menus/simple_menu_model.h" #include "base/ref_counted.h" #include "base/scoped_ptr.h" +#include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" @@ -37,6 +38,7 @@ class ToolsMenuModel : public menus::SimpleMenuModel { // A menu model that builds the contents of the wrench menu. class WrenchMenuModel : public menus::SimpleMenuModel, public menus::ButtonMenuItemModel::Delegate, + public TabStripModelObserver, public NotificationObserver { public: WrenchMenuModel(menus::SimpleMenuModel::Delegate* delegate, @@ -57,6 +59,15 @@ class WrenchMenuModel : public menus::SimpleMenuModel, virtual string16 GetLabelForCommandId(int command_id) const; virtual void ExecuteCommand(int command_id); + // Overridden from TabStripModelObserver: + virtual void TabSelectedAt(TabContents* old_contents, + TabContents* new_contents, + int index, + bool user_gesture); + virtual void TabReplacedAt(TabContents* old_contents, + TabContents* new_contents, int index); + virtual void TabStripModelDeleted(); + // Overridden from NotificationObserver: virtual void Observe(NotificationType type, const NotificationSource& source, @@ -91,6 +102,7 @@ class WrenchMenuModel : public menus::SimpleMenuModel, menus::SimpleMenuModel::Delegate* delegate_; // weak Browser* browser_; // weak + TabStripModel* tabstrip_model_; // weak NotificationRegistrar registrar_; |