Stack based drawing

This is an insane idea on how to implement the drawing. In this case there is no GtkStyle Object. Instead the styles are matched and merged implicitely when needed.


The idea is that the drawing information kept around as a stack which is modified on the fly while the widgets are drawn. So a button expose, could look something like this:

/* Draw a normal button. This is of course just a mockup, and would probably need some larger changes. */
gtk_button_paint(GtkWidget *widget, GtkPaintContext *context)
  GtkButton *button = GTK_BUTTON (widget);

  /* would add the GObject class names, and anything set on the widget with
   * gtk_widget_add_class() or similar. */
  gtk_widget_add_classes_to_paint_context(widget, context);

  /* Do not add "button" if the relief is eg. NONE and the state is NORMAL. */
  if (button->relief == GTK_RELIEF_NORMAL ||
      gtk_button_get_mouse_over (button) ||
      gtk_button_get_pressed (button) ||
      gtk_button_get_active (button))
      gtk_paint_context_add_classes ("button", "box", NULL);
      gtk_paint_context_add_classes ("flat_box", NULL);

  /* add_states would add the state if the variable passed in afterwards is TRUE. */
  gtk_paint_context_add_states ("active",    gtk_button_get_active(button),
                                "pressed",   gtk_button_get_pressed(button),
                                "sensitive", gtk_widget_get_sensitive(widget),
                                "prelight",  gtk_button_get_mouse_over(button),

  /* Variables for the paint function. */
  gtk_paint_context_set_variables ("x", G_TYPE_INT, widget->allocation.x,
                                   "y", G_TYPE_INT, widget->allocation.y,
                                   "width", G_TYPE_INT, widget->allocation.width,
                                   "height", G_TYPE_INT, widget->allocation.height,

  if (button->relief == GTK_RELIEF_NORMAL ||
      gtk_button_get_mouse_over (button) ||
      gtk_button_get_pressed (button) ||
      gtk_button_get_active (button))
      /* Call the paint function. */
      gtk_paint_context_paint(context, "box");
      gtk_paint_context_paint(context, "flat_box");

  /* Call the parents class expose event, which will propagate the expose
   * to the children. The children will use all the information that is
   * already on the stack for the drawing. */
  parent_class->paint (button, context);


The _save() and _restore() functions could be called implicitely by the container expose handler or some helper function to expose child widets (maybe something like gtk_container_propagate_expose). This function could also implicitely add the class name (and all parent classes) to list of classes. Also added automatically would be any classes from the application (eg. "gnome-preferences-dialog" or whatever else).

Selecting the Style

The interesting part about this idea is that the style is selected implicitely when the drawing function is called. So when gtk_paint_context_paint is called the following needs to happen.

  1. The context information is read, and a list of styles that "match" the current context is created
  2. These styles are merged on the fly (optimize by merging on top of the old style)
  3. Then the rendering function of the style is called
  4. The rendering function can (if it wants) query the style context.
    • It could be interesting to investigate if it should be able to modify the style context.

  5. The merged style is kept around until the next _restore happens or the context is modified.

Style properties vs. modifying the context in the theme

In the above example, the button draws a flat box if the relief is set to NONE. The interesting problem here is that some themes will not want to draw any flat box, but others will. However, the knowledge if something *was* drawn is important to select the correct text color of the contained labels.

This may be especially relevant for radio/checkbox prelight. GTK+ currently draws a flat box in this case.

Possibility 1.

The theme/engine could modify the context on the fly, and remove the flat_box class again. Then any flat_box style is not used, and the font color is not modified by the (radio)buttons classes.

Actually, this version makes more sense the other way around. "flat_box" and "box" would be added by the engine/theme if (and only if) they filled the background for following drawing operations.

Possibility 2.

We introduce style properties to modify the widgets drawing, and insert/remove the flat box. However this seems a bit awkward because we need add the different cases to GTK+ itself.

Possibility 3.


Possible Issues


One will need to think about subwindows in some way, but that should be solveable if the toplevel window is exposed first one can attach a copy of the GtkDrawContext. This copy is then retrieved when the subwindow is exposed.

Projects/GTK/NewThemeApi/Proposals/StackedDrawing (last edited 2018-12-05 15:45:53 by EmmanueleBassi)