gross violation of basic MAPPED => VISIBLE constrain in ::addhandlers



hi all,

docs/widget_system.txt (correctly) states that a widget may not be
mapped if it is not visible and realized:

> 2) GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_REALIZED (widget)
> 5) if !GTK_WIDGET_TOPLEVEL (widget):
>
>    GTK_WIDGET_MAPPED (widget) => GTK_WIDGET_VISIBLE (widget)
>                               => GTK_WIDGET_REALIZED (widget)
>
>    widget->parent && GTK_WIDGET_MAPPED (widget->parent) &&
>      GTK_WIDGET_VISIBLE (widget) => GTK_WIDGET_MAPPED (widget)


thus, pretty much any container's _map handler cotains stuff along
the lines of:

   if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
      gtk_widget_map (child);

i.e. map the child only if it is flagged as visible (shown) and not
yet mapped.
this happens when a container is finally mapped and needs to assure that
its visible children also appear on screen.

however, the above constrains need to be taken care off at another point
as well, that is when a child gets added to an already mapped container.
the code in question (for the GtkContainer::add handler) should read:

  if (GTK_WIDGET_VISIBLE (child->parent))
    {
      if (GTK_WIDGET_REALIZED (child->parent) &&
          !GTK_WIDGET_REALIZED (child))
        gtk_widget_realize (child);

      if (GTK_WIDGET_VISIBLE (child) &&
          GTK_WIDGET_MAPPED (child->parent) &&
          !GTK_WIDGET_MAPPED (child))
        gtk_widget_map (child);
    }

i.e. always realize children if their parent is realized, and always map
children if their parent is mapped _and they are flagged visible_.
but as a matter of fact, most GtkContainer::add implementations skip
the extra check for GTK_WIDGET_VISIBLE (child), and map children upon
_add regardlss of whether it wants to be shown or not (for already
mapped containers of course).
for instance, gtkbin.c:gtk_bin_add:

static void
gtk_bin_add (GtkContainer *container,
             GtkWidget    *child)
{
  GtkBin *bin;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_BIN (container));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  bin = GTK_BIN (container);
  g_return_if_fail (bin->child == NULL);

  gtk_widget_set_parent (child, GTK_WIDGET (bin));
  bin->child = child;

  if (GTK_WIDGET_VISIBLE (child->parent))
    {
      if (GTK_WIDGET_REALIZED (child->parent) &&
          !GTK_WIDGET_REALIZED (child))
        gtk_widget_realize (child);

      if (GTK_WIDGET_MAPPED (child->parent) &&
          !GTK_WIDGET_MAPPED (child))
        gtk_widget_map (child);
    }

  if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (container))
    gtk_widget_queue_resize (child);
}

ironically, both visible and !visible children will get mapped, but only
visible children will cause a queued resize ;)

in order to get things working correctly, i'm going to commit some extra
assertments for gtkwidget.c to barf if the constrains are not full filled, i.e.

Fri Apr 30 09:02:28 1999  Tim Janik  <timj@gtk.org>

        * gtk/gtkwidget.c (gtk_widget_real_unrealize): use gtk_container_forall
        instead of gtk_container_foreach to walk and unrealize children, so
        composite children get also unrealized.
        (gtk_widget_real_show): don't call gtk_widget_map() if we don't need to.
        (gtk_widget_map): assert that the widget is visible (basic constrain).
        (gtk_widget_real_map): assert that the widget is realized (basic
        constrain).

we then need to adapt the following functions (i just grepped through gtk+
and libgnomeui, so there might be some other ::add implementations hiding):

GnomeUI:
gnome-dock.c:gnome_dock_set_client_area
gtkpixmapmenuitem.c:gtk_pixmap_menu_item_set_pixmap

Gtk:
gtkbin.c:gtk_bin_add
gtkbox.c:gtk_box_pack_start
gtkbox.c:gtk_box_pack_end
gtkfixed.c:gtk_fixed_put
gtklayout.c:gtk_layout_put
gtklist.c:gtk_list_insert_items
gtkmenushell.c:gtk_menu_shell_insert
gtknotebook.c:gtk_notebook_insert_page_menu
gtkpacker.c:gtk_packer_add_defaults
gtkpacker.c:gtk_packer_add
gtkpaned.c:gtk_paned_pack1
gtkpaned.c:gtk_paned_pack2
gtkscrolledwindow.c:gtk_scrolled_window_add
gtktable.c:gtk_table_attach
gtktoolbar.c:gtk_toolbar_insert_element
gtktree.c:gtk_tree_insert
gtktree.c:gtk_tree_add
gtktreeitem.c:gtk_tree_item_set_subtree
gtkwindow.c:gtk_window_map

by adding appropriate visibility checks to assure hidden childrens never
get mapped.

volounteers to help adding the required if (GTK_WIDGET_VISIBLE (child)
statements and review similar code in other cvs modules are apprechiated.


in case you wonder what the current misbehaviour actually results in,
try code snippets like the following:

window = gtk_widget_new (GTK_TYPE_WINDOW,
                         "visible", TRUE,
                         "signal::destroy", gtk_main_quit, NULL,
                         NULL);
vbox = gtk_widget_new (GTK_TYPE_VBOX, "parent", window, NULL);
hbox = gtk_widget_new (GTK_TYPE_HBOX, "visible", TRUE, "parent", vbox, NULL);
label = gtk_widget_new (GTK_TYPE_LABEL,
                        "parent", hbox,
                        "label", "can you see me?",
                        "visible", TRUE,
                        NULL);
button = gtk_widget_new (GTK_TYPE_BUTTON,
                         "visible", TRUE,
                         "label", "Exit",
                         "parent", hbox,
                         "object_signal::clicked", gtk_widget_destroy, window,
                         NULL);
gtk_widget_show (vbox);


in this example, vbox is mapped immediatedly when added to its parent,
eventhough it is not visible. later on, when widgets get added to the
vbox, they don't get mapped, because the vbox is still !visible. at the
end of the snippet, the vbox is shown and therefore becomes visible, but
because it is already mapped, its mapping code (in gtk_box_map) doesn't
get reinvoked, and therefore the children never actually appear on the screen.


---
ciaoTJ



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]