More gzilla/gtk issues



Hi gtk developers,

   I'm continuing to think about the issues involved in implementing
gzilla smoothly on gtk.

   I've decided that the page display widget should do its own
scrolling, rather than creating a large window and relying on
scrolledwindow to do the scrolling. The issue here is the 32767 pixel
limit on the height of the window. If I use a scrolledwindow, there just
isn't any good way of getting around it. I am still concerned about
aesthetics problems with doing the scrolling inside the page display
widget (most importantly, embedded widgets without the NO_WINDOW
property will lag slightly while scrolling, and will also cause some
flashing of the underlying text). However, this problem has two
solutions which preserve the interface to the page display widget. Thus,
it's possible to agree on an interface now and possibly fiddle with the
implementation later.

   The first possiblity (suggested by Owen Taylor) is to put each
paragraph in its own window. The page display widget would then juggle
these windows, only displaying the ones that were actually visible.
Scrolling would be done by moving each of the windows individually.

   The second possibility is to extend the gtk infrastructure to support
events in NO_WINDOW widgets, and retrofit all widgets that would be
included in a page to be NO_WINDOW. This requires a significant change
to all containers: they need to be able to dispatch mouse and keyboard
events to their child widgets. This is a fairly radical change to gtk,
but might be worthwhile anyway for the performance increase.

   But, as I said, it's definitely possible to go forward without
implementing either of these alternatives.

   Now that I've decided to redo a large part of the page display
widget, it reopens the possibility of merging gtk_page and gtk_text. The
advantages are clear. However, there are certain disadvantages as well.
For one, we'd have to coordinate between the two major developers of the
widget. What I think would work best is for Josh to polish it to the
point that he described a week or so ago, then hand it over to me for
adding all of the Web-specific stuff. Otherwise, I think we'd be
stepping on each other's toes too much. It would also help if Josh gave
me some guidelines on how new attributes should be added.

   A second disadvantage is that, to support all of the Web extensions,
the interface would have to change quite dramatically. We could either
go back and change all the code which uses gtk_text, or we could retain
a backwards compatible interface in addition to the extended one. This
may not be a big problem, because the only code that seems to use
gtk_text now is install.c in the Gimp.

   Finally, the widget would be huge. It's easy to imagine that it could
hit 10 kloc by the time all the floating images, alignment and spacing
options, etc., are added. It may be that two smaller widgets would be
more manageable than one large one, even if the total number of lines of
code comes out a little bigger.

   I'll leave the choice to Josh. If he agrees to hand over a reasonably
full-featured gtk_text widget to be extended for Web page display, I'll
scrap gtk_page and retrofit gtk_text to do what I need. Otherwise, I'll
just continue developing gtk_page.

   The next point is size negotiation. There are basically two problems
with size negotiation in the current gtk. The first is that check_resize
often forces a lot more work than really needs to be done. The second is
support for wrapping layouts in general (more on this later).

   I'm still not sure whether check_resize presents a real problem. The
disable_resize / enable_resize hack actually works pretty well. If not,
it should be possible for the page display widget to override
need_resize to add the child widget to a queue, and set an idle handler
which will resize only the child widgets that are in the queue. Perhaps
the same change should be made to boxes and tables as well, so as to
avoid the need for the disable_resize / enable_resize hack altogether.

   In any case, this doesn't bother me very much now. It's possible to
fix without changing any of the interfaces, and in any case it's just a
performance bug.

   The next big decision is how to handle tables. The original design
for gzilla called for a modular structure corresponding exactly with the
gtk widget hierarchy. Thus, a table would be implemented using an
embedded gtk_table widget, and each of the table entries would be a
child widget of the gtk_table. However, as clean as this idea seems
conceptually, in practice there would be a lot of problems:

* Tables would be limited to 32767 pixels in size

* The size negotiation of gtk is inadequate to handle wrapped text

* In the most straightforward implementation, each table entry would
have its own window. That could be a _lot_ of windows, which could be a
big performance hit (on my system, testgtk takes about 2 seconds to pop
up 400 buttons in the scrolledwindow test).

* The gtk_table widget doesn't draw borders, and would have to be
extended to do so

   I think I've pretty much figured out how to fix the size negotiation.
Just to recap, gtk's size negotiation is based around the size_request
and size_allocate methods. The size_request method is called as part of
the initialization phase of a widget's lifecycle. It returns a "minimum
comfortable size" for the widget. Containers generally try to allocate
at least this much space to their child widgets. Conversely,
size_allocate is called by the container to tell the child what size it
actually is. This is called at least once in the initialization phase,
and also whenever the container gets rearranged (for example, if the
window is resized).

   Whenever a child widget wants to resize, it calls
gtk_container_check_resize on its parent. This, in turn, causes
size_request to get called again on the child, then (after the container
is rearranged), size_allocate and then draw on the child. Most of the
time, size_allocate clears the widget, so draw is drawing on blank
pixels. Thus, check_resize causes a total redraw of the widget, whether
the size has actually changed or not. This is why many of the widgets do
a check_resize whenever their state changes - it's the easiest way to
force a total repaint, even though it may cause serious performance
implications.

   Ok, now for some ideas on how to extend this to "wrapping widgets."
Obviously, the way gtk_page wraps text makes it a wrapping widget, but
there are plenty of other possibilites. For example, menus may be
designed to take more than one line if there's not enough horizontal
space for the whole thing. If the gimp's toolbar was defined as a
wrapped collection of rectangles, it could reshape to 21x1, 3x7, 7x3, or
1x21, depending on the user's preference. So this may be something that
belongs in the gtk generally.

   The main property of wrapping widgets is that their requisition
height depends on their allocation width. If you make the window wider,
it will (usually) become less deep. In gtk's current size negotiation,
the requisition depends only on the internal state of the widget.

   My solution to this problem is to add another method to gtk_widget
called wrap_allocate. It takes a width as an argument, and returns a
height. For a container to do a size_allocate, it will (in general) want
to determine widths for each of its children, call wrap_allocate on each
of the children using this width, then do its usual size allocation,
treating the returned height as the child widget's requisition.

   This still leaves the question open of how the container determines
the width of the child widget. For some containers (vbox), it's pretty
simple, but for tables it needs to be a bit more complex. For example,
consider a table with two columns. In the first column of each row is a
single word. In the second column of each row is a good-sized paragraph.
A good table layout would give a hundred pixels or so to the first
column, and the rest of the window to the second. But how does the table
widget know to do this? Certainly, there's not enough information from
just the size_request call. My solution is to add a gtk_wrap_request
method to gtk_widget, which works just like size_request, but returns
both a "minimum comfortable width" and a "maximum comfortable width".
For wrapped text such as a web page, the minimum comfortable width is
the width of the narrowest unbreakable word (or embedded widget), and
the maximum comfortable width is the width of the widest paragraph if
there are no line breaks at all. Both of these are pretty
straightforward and efficient to calculate.

   This extra method makes table layout a bit more tricky, but it's not
too bad. Basically, it needs to allocate all of the minimum widths
first. Of the remaining space, it tries to fulfill the maximum width
requests of each of the columns, in order of increasing
(maximum-minimum) width. Finally, as soon as it reaches a column for
which it's not capable of filling the maximum width request, or if it
fills all of them, it distributes the extra space among the remaining
columns, using the same algorithm it already uses (i.e. if the expand
flags are set, it will distribute it evenly across all columns).

   In my opinion, this will get table layout exactly right. Have any of
you noticed how sloppy Netscape's table layout is (at least on Unix)? It
almost always causes the set width to exceed the window size, which
causes a horizontal scrollbar. I'm confident my approach will avoid this
problem.

   The changes to gtk are nontrivial but not too bad. The default
implementation for wrap_allocate should just be to return the
requisition height. Similarly, wrap_request just returns the requisition
width as both the minimum and maximum values. Thus, non-wrapping widgets
will not have to change at all. Containers will have to change, but not
too dramatically.

   I'm pretty comfortable with this recommendation, but am not sure if
the rest of the gtk community is willing to accept the extra complexity.
It's likely that wrapping widgets would only be heavily used in gzilla.
It wouldn't be too hard to just do tables within the page widget, and
I'd also avoid the 32767 pixel limit and thousands-of-teeny-windows
performance bug. But I'd like to avoid the duplication of code with
gtktable.c if it's at all practical.

   Again, I'm open to ideas.

   Finally, I've been experimenting with antialised text using t1lib.
The initial results are encouraging, but it's going to need a bit more
work. For one, t1lib's antialiasing code is very slow. It's also stuck
at 2x antialiasing, while I think the extra smoothness of 4x is worth
it. In any case, I sent some of my fast antialising code to Reiner, so
it's likely that a future release of t1lib will contain some really
spiffy antialising code. In any case, it's fun to play with.

Raph



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