Re: Extended Layout



Hello,
   I've been recently making some drastic changes to
the native-layout branch and I'd like to again share my
findings and uncertainties with the list.

This text is a bit long (although I tried to keep it skinny
because of horrible seemingly unavoidable word wrapping)

If you are missing time but may have something to contribute
to the discussion I urge you to skip down to the bottom
where its marked "Problem".

I'm also attaching here the new header files defining the
new extended layout apis (as they've changed since last week).

First off one of the changes I made to the api was to add:

gboolean gtk_extended_layout_is_height_for_width (GtkExtendedLayout *layout);

This API is like ClutterActor:request-mode except that it cannot
currently be set as a property, as I see it so far GTK+ does not
have this need.

Instead a widget by its own nature knows its preference, an
example of it's usefulness can be:

GtkWindow {
   GtkVBox {
     GtkLabel, GtkLabel...
   }
}

Usually apps are constructed with a vbox inside a window,
in this case for the purpose of the toplevel GtkWindow size
constraints; it makes more sense to request the smallest
possible width for the desired height, the resulting effect being
a small window with its labels wrapped to their smallest possible
widths.

Note that in almost every case besides GtkWindow or GtkScrolledWindow;
a child will always be allocated in the orientation of its parent.

I.e. a GtkVBox will first desire/request a width;
when it's allocated an arbitrary width; the vbox will always query
it's children what is the preferred and minimum *height* based on
its allocated width.

Moving along, Owen said it to me first and then Behdad said it too,
now I understand exactly why the following API does not make sense:

gtk_extended_layout_get_desired_size (GtkExtendedLayout *layout,

GtkRequisition *minimum_requisition,

GtkRequisition *natural_requisition);

And so yesterday I spent the day getting rid of that API and implementing
the api originally proposed by Havoc as follows:

gtk_extended_layout_get_desired_width  (GtkExtendedLayout *layout,
                                                            gint
*min_width,  gint *nat_width);
gtk_extended_layout_get_desired_height (GtkExtendedLayout *layout,
                                                            gint
*min_height, gint *nat_height);

There are some reasons why the original API was not cutting it; let's try to
enumerate some:

  - GtkLabel was implementing this in the awkward way the
    API suggests, it would go ahead and fill the minimum GtkRequisition
    with a "size" that it considers "small" and then fill the natural
GtkRequisition
    with a "size" that it consider's natural.

    Leaving us with a natural height of one line and a natural width of the
    full text, Or the minimum height of the fully wrapped text and minimum
    width that is a guess at what the label should look like when wrapped
    to the invented "minimum width".

    The new api will discourage this ambiguity. For text that wraps; the
    minimum width should be the width of the longest word or the widest
    charachter in the text depending on the label's wrap mode (word or char),
    the natural width would be something like the value of "max-width-chars",
    the natural height would be the collective height of the lines needed when
    the text is wrapped at "max-width-chars" width.

  - Another reason why the api doesnt make sense is because half of
    this initially requested value will absolutely never be used.

    Because a widget is asked a sequence like: what's your desired width,
    and then what's your desired height for *another* width (or possibly the
    natural width); in this case the desired height will always be contextual
    to the width.

    Which also means that all the sizegroup stuff in the patch that tries
    caching additional natural sizes to "extend" the "minimum size", also
    makes no sense as it's caching values that are not ever intended to be
    used.

So, after I made the switch and fixed the bugs which cropped up during
the switch; I found that it's all very very slow; luckily Clutter had the same
problem and offers a simple solution to this: Caching a collection of
widths/heights contextual to their opposing dimension. Which I still need to
implement and try.

Also some interfaces are not showing up correctly anymore; as the
GtkLabel is the only widget that actually *does* height-for-width and
it's still
not reporting correct values for that; my thoughts are that under the newer
and truer api it is only the label's breakage that is showing through.

------------- Problem --------------

There is another problem I cant seem to figure out at this point
and it would be great if someone with prior experience in this area
could explain.

The problem in a phrase is this:
  How do you get the collective minimum and natural height for all
  the children in a GtkHBox given a width for the box ?

In UI terms, A label will unwrap and request less height inside a
VBox - pretty easy. But a Label with a Button inside an HBox
placed in a GtkVBox will unwrap when widened; the HBox will not
request any less height for that.

I wrote an algorithm for this but its expensive; the HBox has
to guess some heights and query all children if they would
horizontally fit into the HBox given a guessed height, for each
guessed height until the minimum and natural heights are obtained.

I also found that in some UIs my algorythm works perfectly,
but as soon as you have to query a GtkLabel for its desired
width for a given height - things break; because GtkLabel
does not implement anything like that.

The really confusing and important detail to note here is that
Clutter does none of the above, clutter does not return any
contextual width-for-height information on a ClutterText, nor
does it calculate the minimum/natural height of a horizontal box
given an allocated width.

In the case of ClutterText, the width-for-height is basically
a default minimum and natural width based on the current text.

In the case of the ClutterBoxLayout; it returns the MAX's of the
default minimum and natural heights of all of its children.

It would be really great if someone could explain to me what
are the tradeoffs of not implementing width-for-height in GtkLabel,
and how it is in general that Clutter gets away with not implementing
these.

Note that while the problem as I stated it is about shrinking the height
of a widely allocated HBox in a VBox - I'm quite convinced the opposite
problem also exists and the HBox will not grow for text that gets wrapped
out of proportion (only GtkWindow constraints and lack of paned windows
in my UIs are just hiding that problem from me).

Best Regards,
             -Tristan
/* GTK - The GIMP Toolkit
 * Copyright (C) 2007 Openismus GmbH
 *
 * Author:
 *      Mathias Hasselmann <mathias openismus com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef __GTK_EXTENDED_LAYOUT_H__
#define __GTK_EXTENDED_LAYOUT_H__

#include <gtk/gtkwidget.h>

G_BEGIN_DECLS

#define GTK_TYPE_EXTENDED_LAYOUT            (gtk_extended_layout_get_type ())
#define GTK_EXTENDED_LAYOUT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayout))
#define GTK_EXTENDED_LAYOUT_CLASS(klass)    ((GtkExtendedLayoutIface*)g_type_interface_peek ((klass), GTK_TYPE_EXTENDED_LAYOUT))
#define GTK_IS_EXTENDED_LAYOUT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EXTENDED_LAYOUT))
#define GTK_EXTENDED_LAYOUT_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_EXTENDED_LAYOUT, GtkExtendedLayoutIface))

typedef struct _GtkExtendedLayout           GtkExtendedLayout;
typedef struct _GtkExtendedLayoutIface      GtkExtendedLayoutIface;

struct _GtkExtendedLayoutIface
{
  GTypeInterface g_iface;

  /* virtual table */
  gboolean  (* is_height_for_width)  (GtkExtendedLayout  *layout);

  void      (* get_desired_width)    (GtkExtendedLayout  *layout,
				      gint               *minimum_width,
				      gint               *natural_width);
  void      (* get_desired_height)   (GtkExtendedLayout  *layout,
				      gint               *minimum_height,
				      gint               *natural_height);
  void      (* get_width_for_height) (GtkExtendedLayout  *layout,
				      gint                height,
				      gint               *minimum_width,
				      gint               *natural_width);
  void      (* get_height_for_width) (GtkExtendedLayout  *layout,
				      gint                width,
				      gint               *minimum_height,
				      gint               *natural_height);
};

GType     gtk_extended_layout_get_type             (void) G_GNUC_CONST;

gboolean  gtk_extended_layout_is_height_for_width  (GtkExtendedLayout *layout);
void      gtk_extended_layout_get_desired_width    (GtkExtendedLayout *layout,
						    gint              *minimum_width,
						    gint              *natural_width);
void      gtk_extended_layout_get_desired_height   (GtkExtendedLayout *layout,
						    gint              *minimum_height,
						    gint              *natural_height);
void      gtk_extended_layout_get_width_for_height (GtkExtendedLayout *layout,
						    gint               height,
						    gint              *minimum_width,
						    gint              *natural_width);
void      gtk_extended_layout_get_height_for_width (GtkExtendedLayout *layout,
						    gint               width,
						    gint              *minimum_height,
						    gint              *natural_height);

void      gtk_extended_layout_get_desired_size     (GtkExtendedLayout *layout,
						    GtkRequisition    *minimum_size,
						    GtkRequisition    *natural_size);


G_END_DECLS

#endif /* __GTK_EXTENDED_LAYOUT_H__ */
/* GTK - The GIMP Toolkit
 * Copyright (C) 2010 Openismus GmbH
 *
 * Author:
 *      Tristan Van Berkom <tristan van berkom gmail com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef __GTK_EXTENDED_CELL_H__
#define __GTK_EXTENDED_CELL_H__

#include <gtk/gtkwidget.h>

G_BEGIN_DECLS

#define GTK_TYPE_EXTENDED_CELL            (gtk_extended_cell_get_type ())
#define GTK_EXTENDED_CELL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_EXTENDED_CELL, GtkExtendedCell))
#define GTK_EXTENDED_CELL_CLASS(klass)    ((GtkExtendedCellIface*)g_type_interface_peek ((klass), GTK_TYPE_EXTENDED_CELL))
#define GTK_IS_EXTENDED_CELL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_EXTENDED_CELL))
#define GTK_EXTENDED_CELL_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GTK_TYPE_EXTENDED_CELL, GtkExtendedCellIface))

typedef struct _GtkExtendedCell           GtkExtendedCell;
typedef struct _GtkExtendedCellIface      GtkExtendedCellIface;

struct _GtkExtendedCellIface
{
  GTypeInterface g_iface;

  /* virtual table */

  void      (* get_desired_width)    (GtkExtendedCell    *cell,
				      GtkWidget          *widget,
				      gint               *minimum_size,
				      gint               *natural_size);
  void      (* get_desired_height)   (GtkExtendedCell    *cell,
				      GtkWidget          *widget,
				      gint               *minimum_size,
				      gint               *natural_size);
  void      (* get_width_for_height) (GtkExtendedCell    *cell,
				      GtkWidget          *widget,
				      gint                height,
				      gint               *minimum_width,
				      gint               *natural_width);
  void      (* get_height_for_width) (GtkExtendedCell    *cell,
				      GtkWidget          *widget,
				      gint                width,
				      gint               *minimum_height,
				      gint               *natural_height);
};

GType gtk_extended_cell_get_type             (void) G_GNUC_CONST;

void  gtk_extended_cell_get_desired_width    (GtkExtendedCell   *cell,
					      GtkWidget         *widget,
					      gint              *minimum_size,
					      gint              *natural_size);
void  gtk_extended_cell_get_desired_height   (GtkExtendedCell   *cell,
					      GtkWidget         *widget,
					      gint              *minimum_size,
					      gint              *natural_size);
void  gtk_extended_cell_get_width_for_height (GtkExtendedCell   *cell,
					      GtkWidget         *widget,
					      gint               height,
					      gint              *minimum_width,
					      gint              *natural_width);
void  gtk_extended_cell_get_height_for_width (GtkExtendedCell   *cell,
					      GtkWidget         *widget,
					      gint               width,
					      gint              *minimum_height,
					      gint              *natural_height);

G_END_DECLS

#endif /* __GTK_EXTENDED_CELL_H__ */


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