Conferences Finally Over
by havoc
What an exhausting week; XDevConf
last weekend, LWE, then FUDCon. Really enjoyed
FUDCon today, I thought it went really well. Big thanks to the
organizers. We also had a very productive meeting yesterday with some
of the major external contributors and some Red Hat people; decisions
were reached and action items assigned on a variety of issues.
Since I suck at displays of enthusiasm Seth is picking up the slack
explaining some of the Red Hat team’s work. We also presented some of
this stuff at XDevConf and FUDCon this week.
I think some people didn’t catch on to how Sabayon works and what it
does; this thing is not a control panel. It’s a sort of live
summarizer of changes you’ve made to a prototype user account, and
lets you choose the changes to be included in a user profile. The idea
is to take care of any needed hacks as well, for example stripping out
user home directories hardcoded in settings. As far as we can tell
this automates what most admins already do by hand today. Any cruise
through list archives reveals that admins have a lot of trouble
figuring out which files to extract and what to do with them after
they set up a prototype user the way they want. Even the strongest
mind can be crushed by the GConf and OpenOffice.org tag team.
Colin has been doing a ton of work to create GObject bindings for
D-BUS; looking nice so far, see the list
archives.
I want to elaborate a bit on one aspect of “next generation rendering”
that we haven’t really worked on yet. Everyone is working on the
ability to do OS X or Enlightenment style effects; essentially,
enabling the window manager to use OpenGL and enabling the toolkit to
use Cairo. This gives us drop shadows and minimize animations, and
kills off a bunch of flicker/tearing artifacts. Very good stuff.
However, it doesn’t address one huge limitation: themes can only be
designed piecemeal (button, scrollbar, etc.). Graphical elements can’t
span multiple widgets. An especially hard case to solve is that
graphical elements can’t cover both the window manager frame and
inside the application window.
To make the desktop look really nice, you want the ability to theme a
window (or sub-component thereof) as a whole. This could mean graphics
that span multiple widgets, it could mean moving widgets around, it
could mean changing the spacing between widgets, etc.
To address this, I believe we’d need to rework GTK+ a fair bit. I have
a bunch of vague thoughts on how to do so.
First, if you look at most widget implementations they
effectively hardcode their layout and graphics in two places: the
expose handler (painting the widget) and the
events-that-have-coordinates (button/motion) handlers. So to allow
more free-form theming of single widgets, an approach would be to have
a set of GnomeCanvas-style primitives (lines, rectangles, etc.) and
assemble widgets from those. Rather than hit-testing in the widget
code, event handlers would be connected to “the rectangle” or “the
line” and the theme could move around said rectangle or line.
Second, you need to derive these drawing primitives and their
layout from the theme rather than hardcoding them. The obvious
approach here is something like libglade (resource files), where the
theme provides or modifies the resources.
There are some special “drawing primitives” we could support to handle
some back compat cases. One primitive could be a “custom-rendered
area” and would be the equivalent of an X window (or in web terms, an
image map). A compositing manager treats an app window as this kind of
primitive. GtkWidget could be wrapped in this kind of primitive.
Another special primitive would be the equivalent of an input-only X
window: an invisible event receiver. So e.g. a button could have one
of these and the button code would refer to it. The theme would set
the location and size of this thing. The point is to allow receiving
events without hardcoding any visible graphics primitives. Alternative
or additional approach: something like GnomeCanvasGroup. So the button
itself would be a group object, and could say “a click anywhere on
this group counts as a button press” (equivalent to saying “clicking
any object the theme provides”)
Third, something I’ve often thought is that the need for a
“canvas widget” comes from having a limited widget system in the first
place. “Canvas items” are just widgets that are lightweight, don’t
have to be rectangular, have Z-order controls, etc. So if we say that
we want to build widgets from drawing primitives, a logical extension
is that widgets and drawing primitives are the same thing; and we can
build widgets from other widgets in the same way we build them from
drawing primitives. “Composite widgets” are a sort of wacky
special-case in GTK+ today but all widgets become composite if
you count lines and other primitives as widgets.
Thus there’s a pretty straightforward way to introduce a more powerful
widget system into GTK+: a canvas widget. Think of the canvas widget
specifically as a new widget system. Support placing an old-style
GtkWidget on a canvas by treating it as a raster image. Make
CanvasItem an interface, and a single GtkWidget could even implement
both the old GtkWidget stuff and support the new features of an
improved widget system. An unmodified GtkButton looks to the new
canvas like an X Window, but when support for the new interfaces gets
added to GtkButton the new canvas can see the lines and other
sub-components inside the button and those things can be themed, etc.
Fourth, Seth suggested an interesting idea which is to make
resource files primary. In other words, to create a widget you first
make a resource file (think of it as an HTML page) and then you
specify some code to go with the resource file (think of JavaScript,
though the idea is that you could also specify some C code). The
widget “is” the collection of sub-widgets (remember that now lines and
so forth are widgets also) specified by the resource file.
In the resource file, some sub-widgets would be essential or
mandatory, and others would really be a “default theme.” For a button
widget, it might be required that the resource file have an
“input-only window” widget and a “text string” widget. The code for
the button would refer to those to get events and display the button
text, respectively. However, everything else in the resource file
might be purely cosmetic (not used by the code). So for example there
might be a beveled rectangle widget and themes could remove it or
change its properties.
You see where this is going: the theme is just an alternate resource
file, or a resource file transformation. There are some tough
complexities, because you want to bind the graphics in the theme to
certain widget states or events. You can imagine doing this with
full-blown scripting, but it may be possible to do it with some
relatively simple approach. For example in the metacity theme file you
have to specify the resources and their properties for each possible
state. Another option would be some kind of extremely limited
“scripting”, maybe only allow relating attributes to each other like this:
when button.STATE = PRESSED, bevel.STYLE = IN
If you had attributes for the major widget states, widget visibility,
etc. you could do a lot there. Add simple math as in metacity themes
and you could do even more. So e.g.:
line.X1 = button.X1 + 2
These simple constructs may well be good enough to do all kinds of
great themes.
The important thing here is that entire application windows are
themselves just widgets, and the layout of a window is just the
default “theme” for that window. So say you have a control panel, you
could recurse down into it:
ControlPanel -> Frame -> Button -> Rectangle -> Line
A GUI builder is a widget editor, which can edit everything from the
lines inside a GtkButton, to the entire toplevel window. To do a new
theme, graphic designers would just load up the original resource file
in the GUI builder and start changing around the “inessential” aspects
of the widget tree while leaving the “mandatory” widgets/attributes
unmodified.
Some of the more complex widgets essentially have to be primitives
(“atoms”) rather than composed. We know from long experience that a
tree widget can’t be defined as a bunch of tree item subwidgets and
still perform adequately. However, some of the visuals (such as column
headers) are GtkWidgets today and could be composed widgets in this
new system.
Fifth, and this is maybe true whether or not we do all this
blue-sky stuff, the GTK+ layout system is showing its age. It’s pretty
unpleasant to use with Glade as it is, but if you start to think about
changing the layout of a window in a theme for that window, it seems
pretty clear that we can do better. In a next-generation widget
system, the layout would almost always be in the “inessential”
properties of a widget, rather than the “essential”
properties. i.e. we would not expect the layout to be referenced from
inside the code.
Summarizing so far: the idea here is to define a widget as a
primitive widget (such as a line or input-only region) or a recursive
group of sub-widgets. To create a new app window, you just design a
new widget in the GUI builder. The resulting widget tree has
“essential” nodes and “inessential” nodes, where “inessential” nodes
can be replaced by alternate “inessential” nodes. The inessential
nodes are the “theme.”
Themes are suddenly able to do far more than they do today: 1) they
can rearrange the internals of a button, because the basic layout
isn’t hardcoded in the expose or coordinate event handlers 2) they can
rearrange buttons with respect to one another, because the widget
containing the buttons is just another widget which is themeable as a
whole. A control panel widget is to button widgets as button widgets
are to line and rectangle widgets. And of course themes can insert new
widgets (of the drawing primitive variety) anywhere in the widget
tree.
(I freely admit to handwaving a bunch of details here.)
Sixth, we still have the problem that we can’t have graphical
elements that span the window manager frame and the client contents.
A hugely hard solution is to somehow have a global widget tree (the
widget tree for each app is visible to the compositing manager as a
subtree of the WM frame). I think some simpler hack would turn out to
be better, though in a resource-file-based widget system pushing the
widgets to the display server could be fundamentally saner than it
would be today. I’m not sure it would be saner, but it seems plausible
that you could do some clever things.
To make this seem less far-fetched, imagine that most widgets still
had an X window associated with them; now imagine that some hints were
set on those X windows about the kind of widget and how to render it;
the compositing manager could then be aware of the entire global
widget tree, and render graphical elements mixed in with the widgets
inside an app.
Anyway, that’s what I’ve been thinking on this topic. To me this would
be a really interesting line of thought to prototype in the context of
a canvas widget. By doing a new widget system inside a canvas widget,
you could let GTK+ provide all the basics and just worry about the new
ideas. You could also allow apps to try out the new stuff in limited
contexts without porting to a wacky new widget system.
I know people will send me mail about HTML, XUL, and Longhorn and how
they already do much of this – to answer the question, I don’t
disagree. There are several analogies I’ve had in my head here,
including HTML/CSS/DOM; scene graphs (and 2D “scene graphs”); and
GnomeCanvas.
I tried sketching out what the “resource file” and code would contain
for GtkButton and GtkEntry; it gets complicated fast and there are
unquestionably some issues to think about. In the worst case these
basic widgets would have to be relatively hardcoded: they could have
numerous “essential” aspects and assumptions about the visual details
in the code portion of the widgets. However, even if these basic
widgets are largely hardcoded, their complexities look specific to
basic widgets. Higher-level widgets such as entire application
windows are a lot easier in some ways (especially if we avoid the
container-widgets approach to layout).
(This post was originally found at http://log.ometer.com/2005-02.html#18)