Home | History | Annotate | Download | only in gtk
      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