f04f1b9af06c823602c5e0ecd3b8c2059d7704d3
jcasper
Thu Aug 27 14:42:31 2026 -0700
Faceted composites now have a max display mode setting instead of a full override
of child track visibilities, with accompanying support for the onlyVisibility setting. We also
now include a filter block (applied to appropriate child tracks) when those settings are in place.
refs #37662
diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c
index 32bafce637f..90dc760923a 100644
--- src/hg/hgTrackUi/hgTrackUi.c
+++ src/hg/hgTrackUi/hgTrackUi.c
@@ -3100,31 +3100,54 @@
const char *primaryKey = trackDbSetting(tdb, "primaryKey");
struct slPair *dataTypes = parseDataTypes(tdb);
boolean hasDataTypes = (dataTypes != NULL);
// optional
const char *colorSettingsUrl = (const char *)hashFindVal(tdb->settingsHash, "colorSettingsUrl");
const char *maxCheckboxes = (const char *)hashFindVal(tdb->settingsHash, "maxCheckboxes");
const char *subtrackUrls = trackDbSetting(tdb, "subtrackUrls");
// --- done parsing values from trackDb.settings ---
const char *metaDataId = tdb->track;
const int metaDataIdLen = strlen(metaDataId);
printf(pageStyle); // css
-printf(placeholderDiv); // placholder
+
+// --- Composite-level filters ---
+// Faceted composites skip hCompositeUi(), so we're doing our own filters setup.
+// The section is named "bedFilters" rather than "filters" because the JS builds
+// its own "#filters" div for the metadata facets.
+if (bedHasFilters(tdb))
+ {
+ puts("
");
+ puts("");
+ puts("
"); // required by jsBeginCollapsibleSection*, which emits a
+ jsBeginCollapsibleSectionFontSize(cart, tdb->track, "bedFilters", "Data filters",
+ FALSE, "medium");
+ printf("These filters apply to every "
+ "subtrack in this container. ");
+ printInfoIcon("A filter set on an individual subtrack's own configuration page "
+ "overrides the value here, for that subtrack only.");
+ puts("
");
+ scoreCfgUi(database, cart, tdb, tdb->track, NULL, 1000, /*boxed=*/FALSE);
+ jsEndCollapsibleSection();
+ puts("
");
+ }
+
+printf(placeholderDiv);
// start by figuring out what's on by default and hasn't been overridden
struct hash *defaultOn = hashNew(0);
for (struct trackDb *st = tdb->subtracks; st != NULL; st = st->next)
{
char *setting = NULL;
char *words[2];
boolean enabled = TRUE;
if ((setting = trackDbLocalSetting(st, "parent")) != NULL)
{
char *clone = NULL;
if (chopLine(clone = cloneString(setting), words) >= 2)
if (sameString(words[1], "off"))
enabled = FALSE;
freeMem(clone);
@@ -3408,31 +3431,31 @@
if (liftDb != NULL)
db = liftDb;
// Ideally check cfgTypeFromTdb()/cfgByCfgType() first, but with all these special cases already in
// place, lets be cautious at this time.
// NOTE: Developer, please try to use cfgTypeFromTdb()/cfgByCfgType().
boolean boxed = trackDbSettingClosestToHomeOn(tdb, "boxedCfg");
boolean isGencode3 = trackDbSettingOn(tdb, "isGencode3");
// UI precedence:
// 1) supers to get them out of the way: they have no controls
// 2) special cases based upon track name (developer please avoid)
// 3) cfgTypeFromTdb()/cfgByCfgType() <== prefered method
// 4) special cases falling through the cracks but based upon type
if (tdbIsSuperTrack(tdb))
superTrackUi(tdb, tdbList);
-else if (tdbIsComposite(tdb) && sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted"))
+else if (tdbIsFacetedComposite(tdb))
facetedCompositeUi(tdb);
else if (sameString(track, "stsMap"))
stsMapUi(tdb);
else if (sameString(track, "affyTxnPhase2"))
affyTxnPhase2Ui(tdb);
else if (sameString(track, "cgapSage"))
cgapSageUi(tdb);
else if (sameString(track, "stsMapMouseNew"))
stsMapMouseUi(tdb);
else if (sameString(track, "stsMapRat"))
stsMapRatUi(tdb);
else if (sameString(track, "snpMap"))
snpMapUi(tdb);
else if (sameString(track, "snp"))
snpUi(tdb);
@@ -3609,31 +3632,31 @@
/* myVariants own track: render inline share management. Skip shared tracks
* (myVariants_shared_*) - you can't re-share someone else's data. */
if (cfgOptionBooleanDefault("doMyVariants", FALSE)
&& isMyVariantsTrack(tdb->track)
&& !isMyVariantsSharedTrack(tdb->track))
myVariantsShareUi(tdb);
if (!ajax) // ajax asks for a simple cfg dialog for right-click popup or hgTrackUi subtrack cfg
{
// Composites *might* have had their top level controls just printed,
// but almost certainly have additional controls
boolean isLogo = (trackDbSetting(tdb, "logo") != NULL);
// It'd be nice to handle faceted composites as a separate container type, but practically so much
// of the display features we want are identical to composites - it's easier to special case the UI.
if (tdbIsComposite(tdb) && !isLogo) // for the moment generalizing this to include other containers...
- if (!sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted")) // but not faceted containers ...
+ if (!tdbIsFacetedComposite(tdb)) // but not faceted containers ...
hCompositeUi(db, cart, tdb, NULL, NULL, MAIN_FORM);
// Additional special case navigation links may be added
extraUiLinks(db, tdb, cart);
}
}
#ifdef UNUSED
static void findSuperChildrenAndSettings(struct trackDb *tdbList, struct trackDb *super)
/* Find the tracks that have super as a parent and stuff references to them on
* super's children list. Also do some visibility and parentName futzing. */
{
struct trackDb *tdb;
for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
{
@@ -3673,31 +3696,31 @@
// A bit of context when we're in hierarchy: parent description and sibling track list
if (!tdb->parent)
return;
// show super-track info
struct trackDb *tdbParent = tdb->parent;
printf("Configure track container: "
"
"
"%s ",
hgTrackUiName(), cartSessionVarName(), cartSessionId(cart),
database, chromosome, cgiEncode(tdbParent->track), tdbParent->longLabel);
printf("");
-if (tdbIsComposite(tdb) && sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted"))
+if (tdbIsFacetedComposite(tdb))
return;
if (tdbParent->html)
{
// collapsed panel for Description
printf("
"); // required by jsCollapsible
jsBeginCollapsibleSectionFontSize(cart, tdb->track, "superDescription", "Description", FALSE,
"medium");
// TODO: better done with regex
char *html = replaceChars(tdbParent->html, "Description", "");
html = replaceChars(html, "Description
", "");
@@ -3901,31 +3924,31 @@
safef(longLabel, sizeof(longLabel), "%s", tdb->longLabel);
}
printf("%s%s\n", longLabel, tdbIsSuper(tdb) ? " Tracks" : "");
}
else
{
if (trackDbSetting(tdb, "wgEncode"))
printf("
");
// set large title font size, but less so for long labels to minimize wrap
// longLabel can come from a track hub (user-supplied), escape before echoing
printf("%s%s\n", strlen(tdb->longLabel) > 30 ? 133 : 200,
htmlEncode(tdb->longLabel), tdbIsSuper(tdb) ? " tracks" : "");
// Add a description link if there is one. Only for faceted composites for now.
- if (isNotEmpty(tdb->html) && (tdbIsComposite(tdb) && sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted")))
+ if (isNotEmpty(tdb->html) && tdbIsFacetedComposite(tdb))
{
char *downArrow = "⇓";
enum browserType browser = cgiBrowser();
if (browser == btIE || browser == btFF)
downArrow = "↓";
printf(" ("
"Description%s)", downArrow);
}
}
/* Print link for parent track */
if (!ajax)
{
@@ -3972,32 +3995,40 @@
// special case wikiTrack (there's no trackDb entry); fixes redmine 2395
tdb->canPack = TRUE;
else if (sameString(tdb->type, "halSnake"))
tdb->canPack = TRUE;
else if (!startsWith("bigWig", tdb->type) && startsWith("big", tdb->type))
tdb->canPack = TRUE;
else if (sameString(tdb->type, "bigNarrowPeak"))
tdb->canPack = TRUE;
else if (sameString(tdb->type, "hic"))
tdb->canPack = TRUE;
// Don't bother with vis controls for downloadsOnly
if (!tdbIsDownloadsOnly(tdb))
{
/* Display visibility menu */
- if (tdbIsComposite(tdb) && multViewCount(tdb) > 0)
+ boolean isFaceted = tdbIsFacetedComposite(tdb);
+ if (tdbIsComposite(tdb) && (multViewCount(tdb) > 0 || isFaceted))
+ {
printf("Maximum display mode: ");
+ if (isFaceted)
+ printInfoIcon("This is the most detailed display mode any track in this "
+ "container may use. Tracks that ask for a less detailed mode of "
+ "their own will keep it, and changing this maximum does not "
+ "discard those settings.");
+ }
else if (tdbIsSuper(tdb))
{
printf("Show or hide this container and all tracks: ");
printInfoIcon("Hiding the entire container here will preserve the track visibility settings below, and they can be restored by selecting 'Show'.");
}
else
printf("Display mode: ");
if (tdbIsSuper(tdb))
{
superTrackDropDown(cart, tdb, 1);
}
else
{
/* normal visibility control dropdown */
@@ -4014,30 +4045,43 @@
else // But hgTrackUi page should show local vis
vis = hTvFromString(cartUsualString(cart,tdb->track, hStringFromTv(vis)));
if (tdbIsSuperTrackChild(tdb))
{
char javascript[1024];
safef(javascript, sizeof(javascript), "visTriggersHiddenSelect(this);");
struct slPair *event = slPairNew("change", cloneString(javascript));
hTvDropDownClassVisOnlyAndExtra(tdb->track,vis,canPack,"normalText superChild visDD",
trackDbSetting(tdb, "onlyVisibility"), event);
}
else
hTvDropDownClassVisOnlyAndExtra(tdb->track,vis,canPack,"normalText visDD",
trackDbSetting(tdb, "onlyVisibility"),NULL);
+
+ // A faceted composite caps its children, so say so rather than quietly clamping.
+ // NOTE: no shortLabel in the mouseover - printInfoIcon doesn't escape its text.
+ if (tdbIsContainerChild(tdb) && tdbIsFacetedComposite(tdb->parent))
+ {
+ enum trackVisibility maxVis = tdbVisLimitedByAncestors(cart, tdb->parent,
+ FALSE, TRUE);
+ char note[512];
+ safef(note, sizeof(note), "The parent container is currently set to a maximum "
+ "display mode of '%s', so this track will display at no more than that.",
+ hStringFromTv(maxVis));
+ printInfoIcon(note);
+ }
}
if (!ajax)
{
printf(" ");
cgiMakeButton("Submit", "Submit");
// Offer cancel button always? // composites and multiTracks (not standAlones or supers)
if (tdbIsContainer(tdb))
{
printf(" ");
cgiMakeOnClickButton("htui_cancel", "window.history.back();","Cancel");
}
if (tdbIsComposite(tdb) || tdbIsSuperTrack(tdb))
{