Visible => Mapped constraint



One of the TODO list items for GTK+ has to do with the 
following constraint from widget-system.txt:

5) if !GTK_WIDGET_TOPLEVEL (widget):

[...]

   widget->parent && GTK_WIDGET_MAPPED (widget->parent) && 
     GTK_WIDGET_VISIBLE (widget) => GTK_WIDGET_MAPPED (widget)

There are two problems here

 - Maintanence of this constraint is split between container
   implementations and the GTK+ core in a confusing way.

 - It just isn't valid. A good example of this is the GtkNotebook
   widget; pages other than the current page can be visible,
   and have the parent visible, but still not be mapped.

   The constraint really needs another conditional, something like:

   widget->parent && GTK_WIDGET_MAPPED (widget->parent) && 
     GTK_WIDGET_VISIBLE (widget) && VISIBLE_IN_PARENT (widget, parent)
              
   => GTK_WIDGET_MAPPED (widget)


I think the best way to do things is to add another (probably private)
widget flag, and have:

  void gtk_widget_set_parent_visibility (GtkWidget *widget, 
                                         gboolean   visible_in_parent);

  gboolean gtk_widget_get_parent_visibility (GtkWidget *widget);

We can then move most the enforcement of this constaint, as well
as some related ones into gtk_widget_set_parent ().

Unparenting a widget would automatically restore the parent_visibility
thing to its default state (visible), so that you don't
get carry-over interactions when a widget is removed from a parent
and added to another.

The one thing a container would still have to take care of for
enforcing this constraint is when the parent is mapped - since
the container needs to handle all its children - e.g., 
gtk_bin_map is currently:

===
static void
gtk_bin_map (GtkWidget *widget)
{
  GtkBin *bin;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_BIN (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
  bin = GTK_BIN (widget);

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

  if (!GTK_WIDGET_NO_WINDOW (widget))
    gdk_window_show (widget->window);
}
===

We could, however, provide a default implementation in GtkContainer
that handled this, and other widgets could chain to that if
they have special handling that they need to do in addition
to the default implementation - so gtk_bin_map would look like:

=========
static void
gtk_bin_map (GtkWidget *widget)
{
  GTK_WIDGET_CLASS (parent_class)->map (widget);

  if (!GTK_WIDGET_NO_WINDOW (widget))
    gdk_window_show (widget->window);
}
=========

Another slight ugliness is that there are cases where a widget wants
to take action as its children are hidden and shown. For instance, for
GtkNotebook, when the main widget for a page is hidden, then it
needs to hide the tab label, and redraw stuff.

This isn't really to do with my suggested approach - when a child
is mapped or unmapped, a resize will be queued, and in the size
request, further actions can be taken, but it does mean that
handling of the hide/show is split in rather odd ways.

(And in the case of the notebook you get two request/allocate passes,
since in the size request, the tab label will be unmapped, triggering
another pass.)


There are a couple of other possible approaches

 - Make a child_visible() a virtual_function for GtkContainer - 
   however, this has the problem that you then need something
   like a queue_child_visible_changed() signal, and things gets
   complicated.

 - Simply remove this constraint, and let containers take care 
   of mapping their children as they wish. I think this interferes
   with keeping container writing as simple as possible.


The first approach is, however, I think the cleanest, though
introducing another flag and more state is something I'm
not entirely happy with.

Regards,
                                        Owen




 




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