diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 20:16:22 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-25 20:16:22 +0000 |
commit | 9fb4ad4cf87dfc132627aef2dd513be8b45940ae (patch) | |
tree | 8f639ec0cbf3230bd2f9de545832ab2da536a110 /ui/base | |
parent | c203f03fc14b048592445ee37620fa5c366b2251 (diff) | |
download | chromium_src-9fb4ad4cf87dfc132627aef2dd513be8b45940ae.zip chromium_src-9fb4ad4cf87dfc132627aef2dd513be8b45940ae.tar.gz chromium_src-9fb4ad4cf87dfc132627aef2dd513be8b45940ae.tar.bz2 |
content: Move gtk_expanded_container to ui/base/gtk/.
This is used in code that will be moved for use in content_shell.
BUG=93804
TEST=none; code move
Review URL: http://codereview.chromium.org/7740003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@98282 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/base')
-rw-r--r-- | ui/base/gtk/gtk_expanded_container.cc | 193 | ||||
-rw-r--r-- | ui/base/gtk/gtk_expanded_container.h | 75 | ||||
-rw-r--r-- | ui/base/gtk/gtk_expanded_container_unittest.cc | 159 |
3 files changed, 427 insertions, 0 deletions
diff --git a/ui/base/gtk/gtk_expanded_container.cc b/ui/base/gtk/gtk_expanded_container.cc new file mode 100644 index 0000000..e5b23b5 --- /dev/null +++ b/ui/base/gtk/gtk_expanded_container.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011 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 "ui/base/gtk/gtk_expanded_container.h" + +#include <gtk/gtk.h> + +#include <algorithm> + +namespace { + +enum { + CHILD_SIZE_REQUEST, + LAST_SIGNAL +}; + +guint expanded_container_signals[LAST_SIGNAL] = { 0 }; + +struct SizeAllocateData { + GtkWidget* container; + GtkAllocation* allocation; + int border_width; +}; + +void GetChildPosition(GtkWidget* container, GtkWidget* child, int* x, int* y) { + GValue v = { 0 }; + g_value_init(&v, G_TYPE_INT); + gtk_container_child_get_property(GTK_CONTAINER(container), child, "x", &v); + *x = g_value_get_int(&v); + gtk_container_child_get_property(GTK_CONTAINER(container), child, "y", &v); + *y = g_value_get_int(&v); + g_value_unset(&v); +} + +void ChildSizeAllocate(GtkWidget* child, gpointer userdata) { + if (!gtk_widget_get_visible(child)) + return; + + SizeAllocateData* data = reinterpret_cast<SizeAllocateData*>(userdata); + + GtkRequisition child_requisition; + child_requisition.width = data->allocation->width - data->border_width * 2; + child_requisition.height = data->allocation->height - data->border_width * 2; + + // We need to give whoever is pulling our strings a chance to adjust the + // size of our children. + g_signal_emit(data->container, + expanded_container_signals[CHILD_SIZE_REQUEST], 0, + child, &child_requisition); + + GtkAllocation child_allocation; + child_allocation.width = child_requisition.width; + child_allocation.height = child_requisition.height; + if (child_allocation.width < 0 || child_allocation.height < 0) { + gtk_widget_get_child_requisition(child, &child_requisition); + if (child_allocation.width < 0) + child_allocation.width = child_requisition.width; + if (child_allocation.height < 0) + child_allocation.height = child_requisition.height; + } + + int x, y; + GetChildPosition(data->container, child, &x, &y); + + child_allocation.x = x + data->border_width; + child_allocation.y = y + data->border_width; + + if (GTK_WIDGET_NO_WINDOW(data->container)) { + child_allocation.x += data->allocation->x; + child_allocation.y += data->allocation->y; + } + gtk_widget_size_allocate(child, &child_allocation); +} + +void Marshal_VOID__OBJECT_BOXED(GClosure* closure, + GValue* return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) { + typedef void (*GMarshalFunc_VOID__OBJECT_BOXED) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__OBJECT_BOXED callback; + register GCClosure *cc = reinterpret_cast<GCClosure*>(closure); + register gpointer data1, data2; + + g_return_if_fail(n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA(closure)) { + data1 = closure->data; + data2 = g_value_peek_pointer(param_values + 0); + } else { + data1 = g_value_peek_pointer(param_values + 0); + data2 = closure->data; + } + + callback = reinterpret_cast<GMarshalFunc_VOID__OBJECT_BOXED>( + marshal_data ? marshal_data : cc->callback); + + callback(data1, + g_value_get_object(param_values + 1), + g_value_get_boxed(param_values + 2), + data2); +} + +} // namespace + +G_BEGIN_DECLS + +static void gtk_expanded_container_size_allocate(GtkWidget* widget, + GtkAllocation* allocation); + +G_DEFINE_TYPE(GtkExpandedContainer, gtk_expanded_container, GTK_TYPE_FIXED) + +static void gtk_expanded_container_class_init( + GtkExpandedContainerClass *klass) { + GtkObjectClass* object_class = + reinterpret_cast<GtkObjectClass*>(klass); + + GtkWidgetClass* widget_class = + reinterpret_cast<GtkWidgetClass*>(klass); + widget_class->size_allocate = gtk_expanded_container_size_allocate; + + expanded_container_signals[CHILD_SIZE_REQUEST] = + g_signal_new("child-size-request", + G_OBJECT_CLASS_TYPE(object_class), + static_cast<GSignalFlags>(G_SIGNAL_RUN_FIRST), + 0, + NULL, NULL, + Marshal_VOID__OBJECT_BOXED, + G_TYPE_NONE, 2, + GTK_TYPE_WIDGET, + GTK_TYPE_REQUISITION | G_SIGNAL_TYPE_STATIC_SCOPE); +} + +static void gtk_expanded_container_init(GtkExpandedContainer* container) { +} + +static void gtk_expanded_container_size_allocate(GtkWidget* widget, + GtkAllocation* allocation) { + widget->allocation = *allocation; + + if (!GTK_WIDGET_NO_WINDOW(widget) && GTK_WIDGET_REALIZED(widget)) { + gdk_window_move_resize(widget->window, + allocation->x, + allocation->y, + allocation->width, + allocation->height); + } + + SizeAllocateData data; + data.container = widget; + data.allocation = allocation; + data.border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); + + gtk_container_foreach(GTK_CONTAINER(widget), ChildSizeAllocate, &data); +} + +GtkWidget* gtk_expanded_container_new() { + return GTK_WIDGET(g_object_new(GTK_TYPE_EXPANDED_CONTAINER, NULL)); +} + +void gtk_expanded_container_put(GtkExpandedContainer* container, + GtkWidget* widget, gint x, gint y) { + g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); + g_return_if_fail(GTK_IS_WIDGET(widget)); + gtk_fixed_put(GTK_FIXED(container), widget, x, y); +} + +void gtk_expanded_container_move(GtkExpandedContainer* container, + GtkWidget* widget, gint x, gint y) { + g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); + g_return_if_fail(GTK_IS_WIDGET(widget)); + gtk_fixed_move(GTK_FIXED(container), widget, x, y); +} + +void gtk_expanded_container_set_has_window(GtkExpandedContainer* container, + gboolean has_window) { + g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); + g_return_if_fail(!GTK_WIDGET_REALIZED(container)); + gtk_fixed_set_has_window(GTK_FIXED(container), has_window); +} + +gboolean gtk_expanded_container_get_has_window( + GtkExpandedContainer* container) { + g_return_val_if_fail(GTK_IS_EXPANDED_CONTAINER(container), FALSE); + return gtk_fixed_get_has_window(GTK_FIXED(container)); +} + +G_END_DECLS diff --git a/ui/base/gtk/gtk_expanded_container.h b/ui/base/gtk/gtk_expanded_container.h new file mode 100644 index 0000000..23f1d69 --- /dev/null +++ b/ui/base/gtk/gtk_expanded_container.h @@ -0,0 +1,75 @@ +// Copyright (c) 2011 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 UI_BASE_GTK_GTK_EXPANDED_CONTAINER_H_ +#define UI_BASE_GTK_GTK_EXPANDED_CONTAINER_H_ +#pragma once + +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#include "ui/base/ui_export.h" + +// A specialized container derived from GtkFixed, which expands the size of its +// children to fill the container, in one or both directions. The usage of this +// container is similar to GtkFixed. +// +// The "child-size-request" signal is optional, if you want to expand child +// widgets to customized size other than the container's size. It should have +// the following signature: +// +// void (*child_size_request)(GtkExpandedContainer* container, +// GtkWidget* child, +// GtkRequisition* requisition); +// +// This signal is emitted for each child with the requisition set to the size of +// the container. Your handler may adjust the value of the requisition. If the +// width or height is set to -1, then that direction will not be expanded, and +// the original size request of the child will be used. + +G_BEGIN_DECLS + +#define GTK_TYPE_EXPANDED_CONTAINER \ + (gtk_expanded_container_get_type()) +#define GTK_EXPANDED_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_EXPANDED_CONTAINER, \ + GtkExpandedContainer)) +#define GTK_EXPANDED_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_EXPANDED_CONTAINER, \ + GtkExpandedContainerClass)) +#define GTK_IS_EXPANDED_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_EXPANDED_CONTAINER)) +#define GTK_IS_EXPANDED_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_EXPANDED_CONTAINER)) +#define GTK_EXPANDED_CONTAINER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_EXPANDED_CONTAINER, \ + GtkExpandedContainerClass)) + +typedef struct _GtkExpandedContainer GtkExpandedContainer; +typedef struct _GtkExpandedContainerClass GtkExpandedContainerClass; + +struct _GtkExpandedContainer { + // Parent class. + GtkFixed fixed; +}; + +struct _GtkExpandedContainerClass { + GtkFixedClass parent_class; +}; + +UI_EXPORT GType gtk_expanded_container_get_type() G_GNUC_CONST; +UI_EXPORT GtkWidget* gtk_expanded_container_new(); +UI_EXPORT void gtk_expanded_container_put(GtkExpandedContainer* container, + GtkWidget* widget, gint x, gint y); +UI_EXPORT void gtk_expanded_container_move(GtkExpandedContainer* container, + GtkWidget* widget, gint x, gint y); +UI_EXPORT void gtk_expanded_container_set_has_window( + GtkExpandedContainer* container, + gboolean has_window); +UI_EXPORT gboolean gtk_expanded_container_get_has_window( + GtkExpandedContainer* container); + +G_END_DECLS + +#endif // UI_BASE_GTK_GTK_EXPANDED_CONTAINER_H_ diff --git a/ui/base/gtk/gtk_expanded_container_unittest.cc b/ui/base/gtk/gtk_expanded_container_unittest.cc new file mode 100644 index 0000000..c8721a3 --- /dev/null +++ b/ui/base/gtk/gtk_expanded_container_unittest.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2011 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 "ui/base/gtk/gtk_expanded_container.h" + +#include "testing/gtest/include/gtest/gtest.h" + +class GtkExpandedContainerTest : public testing::Test { + protected: + GtkExpandedContainerTest() + : window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)), + expanded_(gtk_expanded_container_new()) { + gtk_window_set_default_size(GTK_WINDOW(window_), 200, 200); + gtk_container_add(GTK_CONTAINER(window_), expanded_); + } + ~GtkExpandedContainerTest() { + gtk_widget_destroy(window_); + } + + bool FindChild(GtkWidget* widget) { + GList* children = gtk_container_get_children(GTK_CONTAINER(expanded_)); + for (GList* child = children; child; child = child->next) { + if (GTK_WIDGET(child->data) == widget) { + g_list_free(children); + return true; + } + } + g_list_free(children); + return false; + } + + int GetChildX(GtkWidget* widget) { + GValue x = { 0 }; + g_value_init(&x, G_TYPE_INT); + gtk_container_child_get_property(GTK_CONTAINER(expanded_), widget, "x", &x); + return g_value_get_int(&x); + } + + int GetChildY(GtkWidget* widget) { + GValue y = { 0 }; + g_value_init(&y, G_TYPE_INT); + gtk_container_child_get_property(GTK_CONTAINER(expanded_), widget, "y", &y); + return g_value_get_int(&y); + } + + protected: + GtkWidget* window_; + GtkWidget* expanded_; +}; + +TEST_F(GtkExpandedContainerTest, AddRemove) { + GtkWidget* child1 = gtk_fixed_new(); + GtkWidget* child2 = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(expanded_), child1); + ASSERT_TRUE(FindChild(child1)); + + gtk_container_add(GTK_CONTAINER(expanded_), child2); + ASSERT_TRUE(FindChild(child2)); + ASSERT_TRUE(FindChild(child1)); + + gtk_container_remove(GTK_CONTAINER(expanded_), child1); + ASSERT_FALSE(FindChild(child1)); + ASSERT_TRUE(FindChild(child2)); + + gtk_container_remove(GTK_CONTAINER(expanded_), child2); + ASSERT_FALSE(FindChild(child2)); +} + +TEST_F(GtkExpandedContainerTest, Expand) { + GtkWidget* child1 = gtk_fixed_new(); + GtkWidget* child2 = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(expanded_), child1); + gtk_expanded_container_put(GTK_EXPANDED_CONTAINER(expanded_), + child2, 10, 20); + gtk_widget_show_all(window_); + + GtkAllocation allocation = { 0, 0, 50, 100 }; + gtk_widget_size_allocate(expanded_, &allocation); + + EXPECT_EQ(0, child1->allocation.x); + EXPECT_EQ(0, child1->allocation.y); + EXPECT_EQ(50, child1->allocation.width); + EXPECT_EQ(100, child1->allocation.height); + + EXPECT_EQ(10, child2->allocation.x); + EXPECT_EQ(20, child2->allocation.y); + EXPECT_EQ(50, child2->allocation.width); + EXPECT_EQ(100, child2->allocation.height); + + allocation.x = 10; + allocation.y = 20; + gtk_widget_size_allocate(expanded_, &allocation); + + EXPECT_EQ(10, child1->allocation.x); + EXPECT_EQ(20, child1->allocation.y); + EXPECT_EQ(20, child2->allocation.x); + EXPECT_EQ(40, child2->allocation.y); +} + +// Test if the size allocation for children still works when using own +// GdkWindow. In this case, the children's origin starts from (0, 0) rather +// than the container's origin. +TEST_F(GtkExpandedContainerTest, HasWindow) { + GtkWidget* child = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(expanded_), child); + gtk_expanded_container_set_has_window(GTK_EXPANDED_CONTAINER(expanded_), + TRUE); + gtk_widget_show_all(window_); + + GtkAllocation allocation = { 10, 10, 50, 100 }; + gtk_widget_size_allocate(expanded_, &allocation); + + EXPECT_EQ(0, child->allocation.x); + EXPECT_EQ(0, child->allocation.y); + EXPECT_EQ(50, child->allocation.width); + EXPECT_EQ(100, child->allocation.height); +} + +static void OnChildSizeRequest(GtkExpandedContainer* container, + GtkWidget* child, + GtkRequisition* requisition, + gpointer userdata) { + ASSERT_EQ(child, GTK_WIDGET(userdata)); + requisition->width = 250; + requisition->height = -1; +} + +TEST_F(GtkExpandedContainerTest, ChildSizeRequest) { + GtkWidget* child = gtk_fixed_new(); + gtk_widget_set_size_request(child, 10, 25); + g_signal_connect(expanded_, "child-size-request", + G_CALLBACK(OnChildSizeRequest), child); + gtk_container_add(GTK_CONTAINER(expanded_), child); + gtk_widget_show_all(window_); + + GtkAllocation allocation = { 0, 0, 300, 100 }; + gtk_widget_size_allocate(expanded_, &allocation); + + EXPECT_EQ(0, child->allocation.x); + EXPECT_EQ(0, child->allocation.y); + EXPECT_EQ(250, child->allocation.width); + EXPECT_EQ(25, child->allocation.height); +} + +TEST_F(GtkExpandedContainerTest, ChildPosition) { + GtkWidget* child = gtk_fixed_new(); + gtk_expanded_container_put(GTK_EXPANDED_CONTAINER(expanded_), + child, 10, 20); + gtk_widget_show_all(window_); + + EXPECT_EQ(10, GetChildX(child)); + EXPECT_EQ(20, GetChildY(child)); + + gtk_expanded_container_move(GTK_EXPANDED_CONTAINER(expanded_), + child, 40, 50); + EXPECT_EQ(40, GetChildX(child)); + EXPECT_EQ(50, GetChildY(child)); +} |