summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsuzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-06 04:00:03 +0000
committersuzhe@chromium.org <suzhe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-06 04:00:03 +0000
commitc4673fde58e0d5dc11060d9875b38516d785deed (patch)
treedf9379e79fff755a9193dc7a0f38fe646293be95
parent9c44e82ffdef31fa7658772a668954c6b8c62bd8 (diff)
downloadchromium_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.cc20
-rw-r--r--chrome/browser/gtk/bookmark_bar_instructions_gtk.cc34
-rw-r--r--chrome/browser/gtk/gtk_chrome_shrinkable_hbox.cc241
-rw-r--r--chrome/browser/gtk/gtk_chrome_shrinkable_hbox.h80
-rw-r--r--chrome/browser/gtk/gtk_chrome_shrinkable_hbox_unittest.cc249
-rwxr-xr-xchrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi1
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',