Hi everyone,
While developing a GNOME Shell extension layout (a vertical St.BoxLayout containing an St.ScrollView and a custom child container), we encountered a layout geometry issue where St.ScrollView unexpectedly collapsed to 1px height.
We tracked down the diagnostic behavior and would like to share our empirical observations, as well as ask the maintainers about sentinel handling in St.BoxLayout / Clutter.Actor.
Observed Symptom
In GNOME Shell 42.9 (Ubuntu 22.04 LTS):
- An actor hierarchy constructed with a vertical
St.BoxLayoutcontains:St.ScrollView(withy_expand = true).- A child widget container inside or adjacent to the ScrollView.
- The
St.ScrollViewallocated geometry collapses toheight = 1px, rendering all items inside invisible.
Diagnostic log output:
# Anomalous allocation status observed during early execution pass:
appsScroll=1
appsBox=1
status=4294967040
Diagnostic Trace & Empirical Findings
By recursively traversing the actor tree and querying get_preferred_height(-1):
let [minH, natH] = actor.get_preferred_height(-1);
console.log(`${actor.constructor.name}: minH=${minH}, natH=${natH}`);
We observed the following return values:
ChildGridWidget:minH = 0,natH = 4294967296StScrollView:minH = 1,natH = 4294967296,allocH = 1
Analysis of the 4294967296 Sentinel
- Confirmed API Contract: In Clutter C layout code, passing a negative value like
-1toget_preferred_height()indicates an unconstrained height query (see Clutter.Actor docs). - Observed Behavior: When a custom child widget with filled alignment (
y_align: FILL) receives an unconstrained query (for_width = -1) and does not sanitize the input insidevfunc_get_preferred_height, the returned preferred height manifests in GJS asnatH = 4294967296($2^{32}$). - Unconfirmed Pipeline Mechanism (Hypothesis): Official signatures use
gfloat*rather thanguint, so the exact internal C/GJS pipeline stage converting the negative sentinel to $2^{32}$ is unconfirmed from source. - Layout Consequence: During vertical space allocation in
St.BoxLayout, summing sibling natural heights containing $4.29 \times 10^9\text{ px}$ causes container allocation math to saturate, forcing the siblingSt.ScrollViewdown to its minimum required allocation of1px.
Minimal Reproduction Code
const { St, Clutter } = imports.gi;
const box = new St.BoxLayout({ vertical: true, x_expand: true, y_expand: true });
const scrollView = new St.ScrollView({
hscrollbar_policy: St.PolicyType.NEVER,
vscrollbar_policy: St.PolicyType.AUTOMATIC,
x_expand: true,
y_expand: true,
});
// Child widget without explicit min-size and filled alignment
const childGrid = new St.Widget({
x_align: Clutter.ActorAlign.FILL,
y_align: Clutter.ActorAlign.FILL,
});
scrollView.add_actor(childGrid);
box.add_child(scrollView);
// Query preferred height
let [minH, natH] = childGrid.get_preferred_height(-1);
console.log(`minH=${minH}, natH=${natH}`); // natH outputs 4294967296
Workaround for Extension Developers
- Defensive
vfunc_get_preferred_heightHandling: Ensure to check ifforWidth < 0(unconstrained query) and return explicit fallback dimensions. - Explicit Minimum Sizing: Provide explicit minimum size constraints (
set_size(w, h)or CSSmin-height) on custom containers. - Consistent Expansion Flags: Set
x_expand: trueandy_expand: trueexplicitly across layout containers.
Questions for GNOME / St Maintainers
- Has anyone traced the exact internal C/GJS pipeline stage where an unconstrained negative preferred size sentinel registers as $2^{32}$ (
4294967296)? - Should
St.BoxLayoutclamp or sanitize negative/sentinel preferred sizes before calculating sibling layout space distribution?
Full Case Investigation: README.md on GitHub
Reproduction Code & Verification: reproduction/extension.js | verify.sh