diff options
author | suzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-06 04:00:03 +0000 |
---|---|---|
committer | suzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-06 04:00:03 +0000 |
commit | c4673fde58e0d5dc11060d9875b38516d785deed (patch) | |
tree | df9379e79fff755a9193dc7a0f38fe646293be95 | |
parent | 9c44e82ffdef31fa7658772a668954c6b8c62bd8 (diff) | |
download | chromium_src-c4673fde58e0d5dc11060d9875b38516d785deed.zip chromium_src-c4673fde58e0d5dc11060d9875b38516d785deed.tar.gz chromium_src-c4673fde58e0d5dc11060d9875b38516d785deed.tar.bz2 |
[Linux] Make bookmark bar instructions shrinkable.
BUG=36876
BUG=34479
TEST=Delete all bookmarks and show bookmark bar, then shirnk the browser window, the bookmark bar instructions should be shrinkable.
Review URL: http://codereview.chromium.org/661364
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40815 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_gtk.cc | 20 | ||||
-rw-r--r-- | chrome/browser/gtk/bookmark_bar_instructions_gtk.cc | 34 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc | 241 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h | 80 | ||||
-rw-r--r-- | chrome/browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc | 249 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
7 files changed, 620 insertions, 7 deletions
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc index 265664e..5e93378 100644 --- a/chrome/browser/gtk/bookmark_bar_gtk.cc +++ b/chrome/browser/gtk/bookmark_bar_gtk.cc @@ -213,7 +213,7 @@ void BookmarkBarGtk::Init(Profile* profile) { instructions_gtk_.reset(new BookmarkBarInstructionsGtk(this, profile)); gtk_container_add(GTK_CONTAINER(instructions_), instructions_gtk_->widget()); gtk_box_pack_start(GTK_BOX(bookmark_hbox_), instructions_, - FALSE, FALSE, 0); + TRUE, TRUE, 0); gtk_drag_dest_set(instructions_, GtkDestDefaults(GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION), @@ -273,7 +273,7 @@ void BookmarkBarGtk::Init(Profile* profile) { ResourceBundle::GetSharedInstance().GetPixbufNamed(IDR_WARNING))); g_signal_connect(sync_error_button_, "button-press-event", G_CALLBACK(OnSyncErrorButtonPressed), this); - gtk_box_pack_start(GTK_BOX(bookmark_hbox_), sync_error_button_ , + gtk_box_pack_start(GTK_BOX(bookmark_hbox_), sync_error_button_, FALSE, FALSE, 0); gtk_widget_set_size_request(event_box_.get(), -1, kBookmarkBarMinimumHeight); @@ -282,10 +282,13 @@ void BookmarkBarGtk::Init(Profile* profile) { ViewIDUtil::SetID(other_bookmarks_button_, VIEW_ID_OTHER_BOOKMARKS); ViewIDUtil::SetID(widget(), VIEW_ID_BOOKMARK_BAR); + + gtk_widget_show_all(widget()); + gtk_widget_hide(widget()); } void BookmarkBarGtk::Show(bool animate) { - gtk_widget_show_all(widget()); + gtk_widget_show(widget()); bool old_floating = floating_; UpdateFloatingState(); // TODO(estade): animate the transition between floating and non. @@ -326,10 +329,14 @@ void BookmarkBarGtk::Show(bool animate) { // Maybe show the instructions if (show_instructions_) { + gtk_widget_hide(bookmark_toolbar_.get()); gtk_widget_show(instructions_); } else { gtk_widget_hide(instructions_); + gtk_widget_show(bookmark_toolbar_.get()); } + + SetChevronState(); } void BookmarkBarGtk::Hide(bool animate) { @@ -510,9 +517,11 @@ void BookmarkBarGtk::CreateAllBookmarkButtons() { void BookmarkBarGtk::SetInstructionState() { show_instructions_ = (model_->GetBookmarkBarNode()->GetChildCount() == 0); if (show_instructions_) { + gtk_widget_hide(bookmark_toolbar_.get()); gtk_widget_show_all(instructions_); } else { gtk_widget_hide(instructions_); + gtk_widget_show(bookmark_toolbar_.get()); } } @@ -520,6 +529,11 @@ void BookmarkBarGtk::SetChevronState() { if (!GTK_WIDGET_VISIBLE(bookmark_hbox_)) return; + if (show_instructions_) { + gtk_widget_hide(overflow_button_); + return; + } + int extra_space = 0; if (GTK_WIDGET_VISIBLE(overflow_button_)) extra_space = overflow_button_->allocation.width; diff --git a/chrome/browser/gtk/bookmark_bar_instructions_gtk.cc b/chrome/browser/gtk/bookmark_bar_instructions_gtk.cc index d5ca8d1..ce380c3 100644 --- a/chrome/browser/gtk/bookmark_bar_instructions_gtk.cc +++ b/chrome/browser/gtk/bookmark_bar_instructions_gtk.cc @@ -10,26 +10,49 @@ #include "app/l10n_util.h" #include "base/observer_list.h" #include "chrome/browser/gtk/gtk_chrome_link_button.h" +#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h" #include "chrome/browser/gtk/gtk_theme_provider.h" #include "chrome/browser/gtk/gtk_util.h" #include "chrome/common/notification_service.h" #include "grit/generated_resources.h" +namespace { + +// Calculates the real size request of a label and set its ellipsize mode to +// PANGO_ELLIPSIZE_END. +// It must be done when the label is mapped (become visible on the screen), +// to make sure the pango can get correct font information for the calculation. +void InitLabelSizeRequestAndEllipsizeMode(GtkWidget* label) { + GtkRequisition size; + gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE); + gtk_widget_set_size_request(label, -1, -1); + gtk_widget_size_request(label, &size); + gtk_widget_set_size_request(label, size.width, size.height); + gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); +} + +} // namespace + BookmarkBarInstructionsGtk::BookmarkBarInstructionsGtk(Delegate* delegate, Profile* profile) : delegate_(delegate), profile_(profile) { - instructions_hbox_ = gtk_hbox_new(FALSE, 0); + instructions_hbox_ = gtk_chrome_shrinkable_hbox_new(FALSE, FALSE, 0); + gtk_widget_set_size_request(instructions_hbox_, 0, -1); - instructions_label_ = - gtk_label_new(l10n_util::GetStringUTF8(IDS_BOOKMARKS_NO_ITEMS).c_str()); instructions_label_ = gtk_label_new( l10n_util::GetStringUTF8(IDS_BOOKMARKS_NO_ITEMS).c_str()); + gtk_misc_set_alignment(GTK_MISC(instructions_label_), 0, 0.5); gtk_util::CenterWidgetInHBox(instructions_hbox_, instructions_label_, false, 1); + g_signal_connect(instructions_label_, "map", + G_CALLBACK(InitLabelSizeRequestAndEllipsizeMode), + NULL); instructions_link_ = gtk_chrome_link_button_new( l10n_util::GetStringUTF8(IDS_BOOKMARK_BAR_IMPORT_LINK).c_str()); + gtk_misc_set_alignment( + GTK_MISC(GTK_CHROME_LINK_BUTTON(instructions_link_)->label), 0, 0.5); g_signal_connect(instructions_link_, "clicked", G_CALLBACK(OnButtonClick), this); gtk_util::SetButtonTriggersNavigation(instructions_link_); @@ -38,7 +61,10 @@ BookmarkBarInstructionsGtk::BookmarkBarInstructionsGtk(Delegate* delegate, gtk_util::ForceFontSizePixels( GTK_CHROME_LINK_BUTTON(instructions_link_)->label, 13.4); gtk_util::CenterWidgetInHBox(instructions_hbox_, instructions_link_, - false, 1); + false, 6); + g_signal_connect(GTK_CHROME_LINK_BUTTON(instructions_link_)->label, "map", + G_CALLBACK(InitLabelSizeRequestAndEllipsizeMode), + NULL); registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, NotificationService::AllSources()); diff --git a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc new file mode 100644 index 0000000..b147f5f --- /dev/null +++ b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc @@ -0,0 +1,241 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h" + +#include <gtk/gtk.h> +#include <algorithm> + +namespace { + +enum { + PROP_0, + PROP_HIDE_CHILD_DIRECTLY +}; + +struct SizeAllocateData { + GtkChromeShrinkableHBox* box; + GtkAllocation* allocation; + GtkTextDirection direction; + bool homogeneous; + int border_width; + + // Maximum child width when |homogeneous| is TRUE. + int homogeneous_child_width; +}; + +void CountVisibleChildren(GtkWidget* child, gpointer userdata) { + if (GTK_WIDGET_VISIBLE(child)) + ++(*reinterpret_cast<int*>(userdata)); +} + +void ChildSizeAllocate(GtkWidget* child, gpointer userdata) { + if (!GTK_WIDGET_VISIBLE(child)) + return; + + SizeAllocateData* data = reinterpret_cast<SizeAllocateData*>(userdata); + GtkAllocation child_allocation = child->allocation; + + if (data->homogeneous) { + // Make sure the child is not overlapped with others' boundary. + if (child_allocation.width > data->homogeneous_child_width) { + child_allocation.x += + (child_allocation.width - data->homogeneous_child_width) / 2; + child_allocation.width = data->homogeneous_child_width; + } + } else { + guint padding; + GtkPackType pack_type; + gtk_box_query_child_packing(GTK_BOX(data->box), child, NULL, NULL, + &padding, &pack_type); + + if ((data->direction == GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_START) || + (data->direction != GTK_TEXT_DIR_RTL && pack_type == GTK_PACK_END)) { + // All children are right aligned, so make sure the child won't overflow + // its parent's left edge. + int overflow = (data->allocation->x + data->border_width + padding - + child_allocation.x); + if (overflow > 0) { + child_allocation.width -= overflow; + child_allocation.x += overflow; + } + } else { + // All children are left aligned, so make sure the child won't overflow + // its parent's right edge. + int overflow = (child_allocation.x + child_allocation.width + padding - + (data->allocation->x + data->allocation->width - data->border_width)); + if (overflow > 0) + child_allocation.width -= overflow; + } + } + + if (child_allocation.width != child->allocation.width) { + if (data->box->hide_child_directly || child_allocation.width <= 1) + gtk_widget_hide(child); + else + gtk_widget_size_allocate(child, &child_allocation); + } +} + +} // namespace + +G_BEGIN_DECLS + +static void gtk_chrome_shrinkable_hbox_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec); +static void gtk_chrome_shrinkable_hbox_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec); +static void gtk_chrome_shrinkable_hbox_size_allocate(GtkWidget* widget, + GtkAllocation* allocation); + +G_DEFINE_TYPE(GtkChromeShrinkableHBox, gtk_chrome_shrinkable_hbox, + GTK_TYPE_HBOX) + +static void gtk_chrome_shrinkable_hbox_class_init( + GtkChromeShrinkableHBoxClass *klass) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + + object_class->set_property = gtk_chrome_shrinkable_hbox_set_property; + object_class->get_property = gtk_chrome_shrinkable_hbox_get_property; + + widget_class->size_allocate = gtk_chrome_shrinkable_hbox_size_allocate; + + g_object_class_install_property(object_class, PROP_HIDE_CHILD_DIRECTLY, + g_param_spec_boolean("hide-child-directly", + "Hide child directly", + "Whether the children should be hid directly, " + "if there is no enough space in its parent", + FALSE, + static_cast<GParamFlags>( + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); +} + +static void gtk_chrome_shrinkable_hbox_init(GtkChromeShrinkableHBox* box) { + box->hide_child_directly = FALSE; +} + +static void gtk_chrome_shrinkable_hbox_set_property(GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { + GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object); + + switch (prop_id) { + case PROP_HIDE_CHILD_DIRECTLY: + gtk_chrome_shrinkable_hbox_set_hide_child_directly( + box, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gtk_chrome_shrinkable_hbox_get_property(GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) { + GtkChromeShrinkableHBox* box = GTK_CHROME_SHRINKABLE_HBOX(object); + + switch (prop_id) { + case PROP_HIDE_CHILD_DIRECTLY: + g_value_set_boolean(value, box->hide_child_directly); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void gtk_chrome_shrinkable_hbox_size_allocate( + GtkWidget* widget, GtkAllocation* allocation) { + // If we are allocated to more width, then we need to show all invisible + // children before calling parent class's size_allocate method, because the + // new width may be enough to show those hidden children. + if (widget->allocation.width < allocation->width) { + gtk_container_foreach(GTK_CONTAINER(widget), + reinterpret_cast<GtkCallback>(gtk_widget_show), + NULL); + } + + // Let the parent class do size allocation first. After that all children will + // be allocated with reasonable position and size according to their size + // request. + (GTK_WIDGET_CLASS(gtk_chrome_shrinkable_hbox_parent_class)->size_allocate) + (widget, allocation); + + int visible_children_count = 0; + gtk_container_foreach(GTK_CONTAINER(widget), CountVisibleChildren, + &visible_children_count); + + if (visible_children_count == 0) + return; + + SizeAllocateData data; + data.box = GTK_CHROME_SHRINKABLE_HBOX(widget); + data.allocation = allocation; + data.direction = gtk_widget_get_direction(widget); + data.homogeneous = gtk_box_get_homogeneous(GTK_BOX(widget)); + data.border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); + data.homogeneous_child_width = + (allocation->width - data.border_width * 2 - + (visible_children_count - 1) * gtk_box_get_spacing(GTK_BOX(widget))) / + visible_children_count; + + // Shrink or hide children if necessary. + gtk_container_foreach(GTK_CONTAINER(widget), ChildSizeAllocate, &data); +} + +GtkWidget* gtk_chrome_shrinkable_hbox_new(gboolean hide_child_directly, + gboolean homogeneous, + gint spacing) { + return GTK_WIDGET(g_object_new(GTK_TYPE_CHROME_SHRINKABLE_HBOX, + "hide-child-directly", hide_child_directly, + "homogeneous", homogeneous, + "spacing", spacing, + NULL)); +} + +void gtk_chrome_shrinkable_hbox_set_hide_child_directly( + GtkChromeShrinkableHBox* box, gboolean hide_child_directly) { + g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box)); + + if (hide_child_directly != box->hide_child_directly) { + box->hide_child_directly = hide_child_directly; + g_object_notify(G_OBJECT(box), "hide-child-directly"); + gtk_widget_queue_resize(GTK_WIDGET(box)); + } +} + +gboolean gtk_chrome_shrinkable_hbox_get_hide_child_directly( + GtkChromeShrinkableHBox* box) { + g_return_val_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box), FALSE); + + return box->hide_child_directly; +} + +void gtk_chrome_shrinkable_hbox_pack_start(GtkChromeShrinkableHBox* box, + GtkWidget* child, + guint padding) { + g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box)); + g_return_if_fail(GTK_IS_WIDGET(child)); + + gtk_box_pack_start(GTK_BOX(box), child, FALSE, FALSE, 0); +} + +void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box, + GtkWidget* child, + guint padding) { + g_return_if_fail(GTK_IS_CHROME_SHRINKABLE_HBOX(box)); + g_return_if_fail(GTK_IS_WIDGET(child)); + + gtk_box_pack_end(GTK_BOX(box), child, FALSE, FALSE, 0); +} + +G_END_DECLS diff --git a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h new file mode 100644 index 0000000..75f9ed2 --- /dev/null +++ b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h @@ -0,0 +1,80 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GTK_GTK_CHROME_SHRINKABLE_HBOX_H_ +#define CHROME_BROWSER_GTK_GTK_CHROME_SHRINKABLE_HBOX_H_ + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +// A specialized container derived from GtkHBox, which can shrink or hide its +// children one by one to fit into its width. +// +// Limitations of this container: +// - All children should have the same pack type, otherwise they may be +// overlapped with each other. +// - All children must be packed with expand == false and fill == false, +// otherwise they may be overlapped with each other. +// - The visibility of a child is adjusted automatically according to the +// container's width. The child may not show or hide itself. + +G_BEGIN_DECLS + +#define GTK_TYPE_CHROME_SHRINKABLE_HBOX \ + (gtk_chrome_shrinkable_hbox_get_type()) +#define GTK_CHROME_SHRINKABLE_HBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CHROME_SHRINKABLE_HBOX, \ + GtkChromeShrinkableHBox)) +#define GTK_CHROME_SHRINKABLE_HBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CHROME_SHRINKABLE_HBOX, \ + GtkChromeShrinkableHBoxClass)) +#define GTK_IS_CHROME_SHRINKABLE_HBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CHROME_SHRINKABLE_HBOX)) +#define GTK_IS_CHROME_SHRINKABLE_HBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CHROME_SHRINKABLE_HBOX)) +#define GTK_CHROME_SHRINKABLE_HBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CHROME_SHRINKABLE_HBOX, \ + GtkChromeShrinkableHBoxClass)) + +typedef struct _GtkChromeShrinkableHBox GtkChromeShrinkableHBox; +typedef struct _GtkChromeShrinkableHBoxClass GtkChromeShrinkableHBoxClass; + +struct _GtkChromeShrinkableHBox { + // Parent class. + GtkHBox hbox; + + gboolean hide_child_directly; +}; + +struct _GtkChromeShrinkableHBoxClass { + GtkHBoxClass parent_class; +}; + +GType gtk_chrome_shrinkable_hbox_get_type() G_GNUC_CONST; + +// Creates a new shrinkable hbox. +// If |hide_child_directly| is true then its child widgets will be hid directly +// if they are too wide to be fit into the hbox's width. Otherwise they will be +// shrunk first before being hid completely. +GtkWidget* gtk_chrome_shrinkable_hbox_new(gboolean hide_child_directly, + gboolean homogeneous, + gint spacing); + +void gtk_chrome_shrinkable_hbox_set_hide_child_directly( + GtkChromeShrinkableHBox* box, gboolean hide_child_directly); + +gboolean gtk_chrome_shrinkable_hbox_get_hide_child_directly( + GtkChromeShrinkableHBox* box); + +void gtk_chrome_shrinkable_hbox_pack_start(GtkChromeShrinkableHBox* box, + GtkWidget* child, + guint padding); + +void gtk_chrome_shrinkable_hbox_pack_end(GtkChromeShrinkableHBox* box, + GtkWidget* child, + guint padding); + +G_END_DECLS + +#endif // CHROME_BROWSER_GTK_GTK_CHROME_SHRINKABLE_HBOX_H_ diff --git a/chrome/browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc new file mode 100644 index 0000000..dd30c2c --- /dev/null +++ b/chrome/browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc @@ -0,0 +1,249 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h" + +#include <vector> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int kSpacing = 3; +const int kBorderWidth = 5; + +} // namespace + +class GtkChromeShrinkableHBoxTest : public testing::Test { + protected: + GtkChromeShrinkableHBoxTest() + : window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)), + box_(gtk_chrome_shrinkable_hbox_new(FALSE, FALSE, kSpacing)) { + gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR); + gtk_window_set_default_size(GTK_WINDOW(window_), 200, 200); + gtk_container_add(GTK_CONTAINER(window_), box_); + gtk_container_set_border_width(GTK_CONTAINER(box_), kBorderWidth); + } + + ~GtkChromeShrinkableHBoxTest() { + gtk_widget_destroy(window_); + } + + // Add some children widgets with arbitrary width and padding. + void AddChildren(bool pack_start) { + static struct { + int width; + int padding; + } kChildrenData[] = { + { 60, 2 }, + { 70, 3 }, + { 80, 5 }, + { 50, 7 }, + { 40, 11 }, + { 60, 0 }, + { 0, 0 } + }; + + for (size_t i = 0; kChildrenData[i].width; ++i) { + GtkWidget* child = gtk_fixed_new(); + gtk_widget_set_size_request(child, kChildrenData[i].width, -1); + if (pack_start) { + gtk_chrome_shrinkable_hbox_pack_start( + GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding); + } else { + gtk_chrome_shrinkable_hbox_pack_end( + GTK_CHROME_SHRINKABLE_HBOX(box_), child, kChildrenData[i].padding); + } + } + } + + // Check if all children's size allocation are inside the |box_|'s boundary. + void Validate(bool pack_start) { + std::vector<ChildData> children_data; + gtk_container_foreach(GTK_CONTAINER(box_), CollectChildData, + &children_data); + + size_t children_count = children_data.size(); + size_t visible_children_count = 0; + for (size_t i = 0; i < children_count; ++i) { + if (children_data[i].visible) + ++visible_children_count; + } + + if (visible_children_count == 0) + return; + + int border_width = gtk_container_get_border_width(GTK_CONTAINER(box_)); + int x = box_->allocation.x + border_width; + int width = box_->allocation.width - border_width * 2; + int spacing = gtk_box_get_spacing(GTK_BOX(box_)); + bool homogeneous = gtk_box_get_homogeneous(GTK_BOX(box_)); + + if (homogeneous) { + // If the |box_| is in homogeneous mode, then check if the visible + // children are not overlapped with each other. + int homogeneous_child_width = + (width - (visible_children_count - 1) * spacing) / + visible_children_count; + + for (size_t i = 0; i < children_count; ++i) { + SCOPED_TRACE(testing::Message() << "Validate homogeneous child " << i + << " visible: " << children_data[i].visible + << " padding: " << children_data[i].padding + << " x: " << children_data[i].x + << " width: " << children_data[i].width); + + if (children_data[i].visible) + ASSERT_LE(children_data[i].width, homogeneous_child_width); + } + } else { + // If the |box_| is not in homogeneous mode, then just check if all + // visible children are inside the |box_|'s boundary. And for those + // hidden children which are out of the boundary, they should only + // be hidden one by one from the end of the |box_|. + bool last_visible = pack_start; + bool visibility_changed = false; + for (size_t i = 0; i < children_count; ++i) { + SCOPED_TRACE(testing::Message() << "Validate child " << i + << " visible: " << children_data[i].visible + << " padding: " << children_data[i].padding + << " x: " << children_data[i].x + << " width: " << children_data[i].width); + + if (last_visible != children_data[i].visible) { + ASSERT_FALSE(visibility_changed); + visibility_changed = true; + last_visible = children_data[i].visible; + } + if (children_data[i].visible) { + ASSERT_GE(children_data[i].x, + x + children_data[i].padding); + ASSERT_LE(children_data[i].x + children_data[i].width, + x + width - children_data[i].padding); + } + } + } + } + + void Test(bool pack_start) { + gtk_widget_show_all(window_); + GtkAllocation allocation = { 0, 0, 0, 200 }; + gtk_chrome_shrinkable_hbox_set_hide_child_directly( + GTK_CHROME_SHRINKABLE_HBOX(box_), FALSE); + for (int width = 500; width > kBorderWidth * 2; --width) { + SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = FALSE," + << " width = " << width); + + allocation.width = width; + // Reducing the width may cause some children to be hidden, which will + // cause queue resize, so it's necessary to do another size allocation to + // emulate the queue resize. + gtk_widget_size_allocate(box_, &allocation); + gtk_widget_size_allocate(box_, &allocation); + ASSERT_NO_FATAL_FAILURE(Validate(pack_start)) << "width = " << width; + } + + for (int width = kBorderWidth * 2; width <= 500; ++width) { + SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = FALSE," + << " width = " << width); + + allocation.width = width; + gtk_widget_size_allocate(box_, &allocation); + ASSERT_NO_FATAL_FAILURE(Validate(pack_start)); + } + + gtk_chrome_shrinkable_hbox_set_hide_child_directly( + GTK_CHROME_SHRINKABLE_HBOX(box_), TRUE); + for (int width = 500; width > kBorderWidth * 2; --width) { + SCOPED_TRACE(testing::Message() << "Shrink hide_child_directly = TRUE," + << " width = " << width); + + allocation.width = width; + gtk_widget_size_allocate(box_, &allocation); + gtk_widget_size_allocate(box_, &allocation); + ASSERT_NO_FATAL_FAILURE(Validate(pack_start)); + } + + for (int width = kBorderWidth * 2; width <= 500; ++width) { + SCOPED_TRACE(testing::Message() << "Expand hide_child_directly = TRUE," + << " width = " << width); + + allocation.width = width; + gtk_widget_size_allocate(box_, &allocation); + ASSERT_NO_FATAL_FAILURE(Validate(pack_start)); + } + } + + protected: + GtkWidget* window_; + GtkWidget* box_; + + private: + struct ChildData { + bool visible; + int padding; + int x; + int width; + }; + + static void CollectChildData(GtkWidget* child, gpointer userdata) { + guint padding; + gtk_box_query_child_packing(GTK_BOX(gtk_widget_get_parent(child)), child, + NULL, NULL, &padding, NULL); + + ChildData data; + data.visible = GTK_WIDGET_VISIBLE(child); + data.padding = padding; + data.x = child->allocation.x; + data.width = child->allocation.width; + + reinterpret_cast<std::vector<ChildData>*>(userdata)->push_back(data); + } +}; + +TEST_F(GtkChromeShrinkableHBoxTest, PackStart) { + AddChildren(true); + + { + SCOPED_TRACE("Test LTR"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR); + EXPECT_NO_FATAL_FAILURE(Test(true)); + } + { + SCOPED_TRACE("Test RTL"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL); + EXPECT_NO_FATAL_FAILURE(Test(true)); + } +} + +TEST_F(GtkChromeShrinkableHBoxTest, PackEnd) { + AddChildren(false); + + { + SCOPED_TRACE("Test LTR"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR); + EXPECT_NO_FATAL_FAILURE(Test(false)); + } + { + SCOPED_TRACE("Test RTL"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL); + EXPECT_NO_FATAL_FAILURE(Test(false)); + } +} + +TEST_F(GtkChromeShrinkableHBoxTest, Homogeneous) { + AddChildren(true); + gtk_box_set_homogeneous(GTK_BOX(box_), true); + + { + SCOPED_TRACE("Test LTR"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_LTR); + EXPECT_NO_FATAL_FAILURE(Test(true)); + } + { + SCOPED_TRACE("Test RTL"); + gtk_widget_set_direction(box_, GTK_TEXT_DIR_RTL); + EXPECT_NO_FATAL_FAILURE(Test(true)); + } +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 310a9d8..8bf953d 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1119,6 +1119,8 @@ 'browser/gtk/gtk_chrome_button.h', 'browser/gtk/gtk_chrome_link_button.cc', 'browser/gtk/gtk_chrome_link_button.h', + 'browser/gtk/gtk_chrome_shrinkable_hbox.cc', + 'browser/gtk/gtk_chrome_shrinkable_hbox.h', 'browser/gtk/gtk_expanded_container.cc', 'browser/gtk/gtk_expanded_container.h', 'browser/gtk/gtk_floating_container.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 3b7da95..f1079d9 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -740,6 +740,7 @@ 'browser/google_update_settings_unittest.cc', 'browser/gtk/bookmark_bar_gtk_unittest.cc', 'browser/gtk/bookmark_editor_gtk_unittest.cc', + 'browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc', 'browser/gtk/gtk_expanded_container_unittest.cc', 'browser/gtk/gtk_theme_provider_unittest.cc', 'browser/gtk/go_button_gtk_unittest.cc', |