Who is this Dan you are talking to and how does he know so much about dt and gtk?
Anyway, your suggestion is a really good one, but not necessarily trivial to implement. There’s two possible approaches:
- temporarily move (reparent) the header of the topmost module into the top of the panel. The problem with this is that this would take it outside of the scrollable window, so it doesn’t fit within the vertical scrollbar anymore (and therefore would be slightly wider, so you’d see the size jump back and forth). Also keeping track of which module is the topmost one is not so easy. See also the problem with 2.
- Add a GtkOverlay widget to each module. Put the body in there as the main widget, with an added margin at the top the height of the header. Put the header as an overlay widget. Now you can move the overlay across the body by adjusting its top margin. Every time you draw the widget, you check if it is partially off the top of the screen and push the header down if needed.
So in dtgtk_expander_new
instead of
gtk_box_pack_start(GTK_BOX(expander), expander->header_evb, TRUE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(expander), expander->frame, TRUE, FALSE, 0);
do
GtkWidget *overlay = gtk_overlay_new();
gtk_container_add(GTK_CONTAINER(overlay), expander->frame);
gtk_overlay_add_overlay(GTK_OVERLAY(overlay), expander->header_evb);
gtk_widget_set_valign(expander->header_evb, GTK_ALIGN_START);
gtk_container_add(GTK_CONTAINER(expander), overlay);
g_signal_connect(expander, "draw", G_CALLBACK(_adjust_header_position), header);
with
static gboolean _adjust_header_position(GtkWidget *widget, cairo_t *cr, GtkWidget *header)
{
GtkWidget *sw = gtk_widget_get_ancestor(widget, GTK_TYPE_SCROLLED_WINDOW);
if(!sw) return FALSE;
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
GtkAdjustment *adjustment = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw));
gdouble value = gtk_adjustment_get_value(adjustment);
gtk_widget_set_margin_top(header, MAX(value - allocation.y, 0)); // TODO don't push header off the bottom
return FALSE;
}
and initialise the spacing for the header at the top as soon as we know how tall the header is, so let’s say, for now, at the start of _expander_resize
GtkDarktableExpander *expander = DTGTK_EXPANDER(widget);
gtk_widget_set_margin_top(expander->frame, gtk_widget_get_allocated_height(expander->header));
OK, all good and it sort of works (if you add background-color: @bg_color;
to #module-header
in darktable.css
otherwise the body shines through the header.
_Except the GtkScrolledWindow buffers the drawing of the widgets it contains. So the module does not necessarily get re"draw"n each time it moves so we don’t get a chance to move the header. (Generally, if you drag the scrollbar it will update the header in jumps a few times and if it is then not at the top you can make it update again by moving the mouse over the module).
OK, we could monitor the scrollbar and check all modules (even the ones that are not being drawn because they are completely off the screen) but that is a major hassle that I don’t want to get into.
Maybe there’s another signal that fires conveniently? Suggestions welcome.