1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/base/gtk/gtk_floating_container.h" 6 7 #include <gtk/gtk.h> 8 9 #include <algorithm> 10 11 #include "ui/gfx/gtk_compat.h" 12 13 namespace { 14 15 enum { 16 SET_FLOATING_POSITION, 17 LAST_SIGNAL 18 }; 19 20 enum { 21 CHILD_PROP_0, 22 CHILD_PROP_X, 23 CHILD_PROP_Y 24 }; 25 26 // Returns the GtkFloatingContainerChild associated with |widget| (or NULL if 27 // |widget| not found). 28 GtkFloatingContainerChild* GetChild(GtkFloatingContainer* container, 29 GtkWidget* widget) { 30 for (GList* floating_children = container->floating_children; 31 floating_children; floating_children = g_list_next(floating_children)) { 32 GtkFloatingContainerChild* child = 33 reinterpret_cast<GtkFloatingContainerChild*>(floating_children->data); 34 35 if (child->widget == widget) 36 return child; 37 } 38 39 return NULL; 40 } 41 42 const GParamFlags kStaticReadWriteProp = static_cast<GParamFlags>( 43 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); 44 45 } // namespace 46 47 G_BEGIN_DECLS 48 49 static void gtk_floating_container_remove(GtkContainer* container, 50 GtkWidget* widget); 51 static void gtk_floating_container_forall(GtkContainer* container, 52 gboolean include_internals, 53 GtkCallback callback, 54 gpointer callback_data); 55 static void gtk_floating_container_size_request(GtkWidget* widget, 56 GtkRequisition* requisition); 57 static void gtk_floating_container_size_allocate(GtkWidget* widget, 58 GtkAllocation* allocation); 59 static void gtk_floating_container_set_child_property(GtkContainer* container, 60 GtkWidget* child, 61 guint property_id, 62 const GValue* value, 63 GParamSpec* pspec); 64 static void gtk_floating_container_get_child_property(GtkContainer* container, 65 GtkWidget* child, 66 guint property_id, 67 GValue* value, 68 GParamSpec* pspec); 69 70 static guint floating_container_signals[LAST_SIGNAL] = { 0 }; 71 72 G_DEFINE_TYPE(GtkFloatingContainer, gtk_floating_container, GTK_TYPE_BIN) 73 74 static void gtk_floating_container_class_init( 75 GtkFloatingContainerClass *klass) { 76 GtkObjectClass* object_class = 77 reinterpret_cast<GtkObjectClass*>(klass); 78 79 GtkWidgetClass* widget_class = 80 reinterpret_cast<GtkWidgetClass*>(klass); 81 widget_class->size_request = gtk_floating_container_size_request; 82 widget_class->size_allocate = gtk_floating_container_size_allocate; 83 84 GtkContainerClass* container_class = 85 reinterpret_cast<GtkContainerClass*>(klass); 86 container_class->remove = gtk_floating_container_remove; 87 container_class->forall = gtk_floating_container_forall; 88 89 container_class->set_child_property = 90 gtk_floating_container_set_child_property; 91 container_class->get_child_property = 92 gtk_floating_container_get_child_property; 93 94 gtk_container_class_install_child_property( 95 container_class, 96 CHILD_PROP_X, 97 g_param_spec_int("x", 98 "X position", 99 "X position of child widget", 100 G_MININT, 101 G_MAXINT, 102 0, 103 kStaticReadWriteProp)); 104 105 gtk_container_class_install_child_property( 106 container_class, 107 CHILD_PROP_Y, 108 g_param_spec_int("y", 109 "Y position", 110 "Y position of child widget", 111 G_MININT, 112 G_MAXINT, 113 0, 114 kStaticReadWriteProp)); 115 116 floating_container_signals[SET_FLOATING_POSITION] = 117 g_signal_new("set-floating-position", 118 G_OBJECT_CLASS_TYPE(object_class), 119 static_cast<GSignalFlags>(G_SIGNAL_RUN_FIRST | 120 G_SIGNAL_ACTION), 121 0, 122 NULL, NULL, 123 g_cclosure_marshal_VOID__BOXED, 124 G_TYPE_NONE, 1, 125 GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE); 126 } 127 128 static void gtk_floating_container_init(GtkFloatingContainer* container) { 129 gtk_widget_set_has_window(GTK_WIDGET(container), FALSE); 130 container->floating_children = NULL; 131 } 132 133 static void gtk_floating_container_remove(GtkContainer* container, 134 GtkWidget* widget) { 135 g_return_if_fail(GTK_IS_WIDGET(widget)); 136 137 GtkBin* bin = GTK_BIN(container); 138 if (gtk_bin_get_child(bin) == widget) { 139 ((GTK_CONTAINER_CLASS(gtk_floating_container_parent_class))->remove) 140 (container, widget); 141 } else { 142 // Handle the other case where it's in our |floating_children| list. 143 GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container); 144 GList* children = floating->floating_children; 145 gboolean removed_child = false; 146 while (children) { 147 GtkFloatingContainerChild* child = 148 reinterpret_cast<GtkFloatingContainerChild*>(children->data); 149 150 if (child->widget == widget) { 151 removed_child = true; 152 gboolean was_visible = gtk_widget_get_visible(GTK_WIDGET(widget)); 153 154 gtk_widget_unparent(widget); 155 156 floating->floating_children = 157 g_list_remove_link(floating->floating_children, children); 158 g_list_free(children); 159 g_free(child); 160 161 if (was_visible && gtk_widget_get_visible(GTK_WIDGET(container))) 162 gtk_widget_queue_resize(GTK_WIDGET(container)); 163 164 break; 165 } 166 children = children->next; 167 } 168 169 g_return_if_fail(removed_child); 170 } 171 } 172 173 static void gtk_floating_container_forall(GtkContainer* container, 174 gboolean include_internals, 175 GtkCallback callback, 176 gpointer callback_data) { 177 g_return_if_fail(container != NULL); 178 g_return_if_fail(callback != NULL); 179 180 // Let GtkBin do its part of the forall. 181 ((GTK_CONTAINER_CLASS(gtk_floating_container_parent_class))->forall) 182 (container, include_internals, callback, callback_data); 183 184 GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container); 185 GList* children = floating->floating_children; 186 while (children) { 187 GtkFloatingContainerChild* child = 188 reinterpret_cast<GtkFloatingContainerChild*>(children->data); 189 children = children->next; 190 191 (*callback)(child->widget, callback_data); 192 } 193 } 194 195 static void gtk_floating_container_size_request(GtkWidget* widget, 196 GtkRequisition* requisition) { 197 GtkBin* bin = GTK_BIN(widget); 198 if (bin && gtk_bin_get_child(bin)) { 199 gtk_widget_size_request(gtk_bin_get_child(bin), requisition); 200 } else { 201 requisition->width = 0; 202 requisition->height = 0; 203 } 204 } 205 206 static void gtk_floating_container_size_allocate(GtkWidget* widget, 207 GtkAllocation* allocation) { 208 gtk_widget_set_allocation(widget, allocation); 209 210 if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) { 211 gdk_window_move_resize(gtk_widget_get_window(widget), 212 allocation->x, 213 allocation->y, 214 allocation->width, 215 allocation->height); 216 } 217 218 // Give the same allocation to our GtkBin component. 219 GtkBin* bin = GTK_BIN(widget); 220 if (gtk_bin_get_child(bin)) { 221 gtk_widget_size_allocate(gtk_bin_get_child(bin), allocation); 222 } 223 224 // We need to give whoever is pulling our strings a chance to set the "x" and 225 // "y" properties on all of our children. 226 g_signal_emit(widget, floating_container_signals[SET_FLOATING_POSITION], 0, 227 allocation); 228 229 // Our allocation has been set. We've asked our controller to place the other 230 // widgets. Pass out allocations to all our children based on where they want 231 // to be. 232 GtkFloatingContainer* container = GTK_FLOATING_CONTAINER(widget); 233 GList* children = container->floating_children; 234 GtkAllocation child_allocation; 235 GtkRequisition child_requisition; 236 while (children) { 237 GtkFloatingContainerChild* child = 238 reinterpret_cast<GtkFloatingContainerChild*>(children->data); 239 children = children->next; 240 241 if (gtk_widget_get_visible(GTK_WIDGET(child->widget))) { 242 gtk_widget_size_request(child->widget, &child_requisition); 243 child_allocation.x = allocation->x + child->x; 244 child_allocation.y = allocation->y + child->y; 245 child_allocation.width = std::max(1, std::min(child_requisition.width, 246 allocation->width)); 247 child_allocation.height = std::max(1, std::min(child_requisition.height, 248 allocation->height)); 249 gtk_widget_size_allocate(child->widget, &child_allocation); 250 } 251 } 252 } 253 254 static void gtk_floating_container_set_child_property(GtkContainer* container, 255 GtkWidget* child, 256 guint property_id, 257 const GValue* value, 258 GParamSpec* pspec) { 259 GtkFloatingContainerChild* floating_child = 260 GetChild(GTK_FLOATING_CONTAINER(container), child); 261 g_return_if_fail(floating_child); 262 263 switch (property_id) { 264 case CHILD_PROP_X: 265 floating_child->x = g_value_get_int(value); 266 gtk_widget_child_notify(child, "x"); 267 break; 268 case CHILD_PROP_Y: 269 floating_child->y = g_value_get_int(value); 270 gtk_widget_child_notify(child, "y"); 271 break; 272 default: 273 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID( 274 container, property_id, pspec); 275 break; 276 }; 277 } 278 279 static void gtk_floating_container_get_child_property(GtkContainer* container, 280 GtkWidget* child, 281 guint property_id, 282 GValue* value, 283 GParamSpec* pspec) { 284 GtkFloatingContainerChild* floating_child = 285 GetChild(GTK_FLOATING_CONTAINER(container), child); 286 g_return_if_fail(floating_child); 287 288 switch (property_id) { 289 case CHILD_PROP_X: 290 g_value_set_int(value, floating_child->x); 291 break; 292 case CHILD_PROP_Y: 293 g_value_set_int(value, floating_child->y); 294 break; 295 default: 296 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID( 297 container, property_id, pspec); 298 break; 299 }; 300 } 301 302 GtkWidget* gtk_floating_container_new() { 303 return GTK_WIDGET(g_object_new(GTK_TYPE_FLOATING_CONTAINER, NULL)); 304 } 305 306 void gtk_floating_container_add_floating(GtkFloatingContainer* container, 307 GtkWidget* widget) { 308 g_return_if_fail(GTK_IS_FLOATING_CONTAINER(container)); 309 g_return_if_fail(GTK_IS_WIDGET(widget)); 310 311 GtkFloatingContainerChild* child_info = g_new(GtkFloatingContainerChild, 1); 312 child_info->widget = widget; 313 child_info->x = 0; 314 child_info->y = 0; 315 316 gtk_widget_set_parent(widget, GTK_WIDGET(container)); 317 318 container->floating_children = 319 g_list_append(container->floating_children, child_info); 320 } 321 322 G_END_DECLS 323