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("<div class='bedFiltersWrap'>");
+    puts("<style>#bedFilters-1 > td > br:first-of-type, "
+         "#bedFilters-1 > td > p:empty { display:none; }</style>");
+    puts("<table>");  // required by jsBeginCollapsibleSection*, which emits a <TR>
+    jsBeginCollapsibleSectionFontSize(cart, tdb->track, "bedFilters", "Data filters",
+                                      FALSE, "medium");
+    printf("<p class='smallText' style='margin-top:0'>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("</p>");
+    scoreCfgUi(database, cart, tdb, tdb->track, NULL, 1000, /*boxed=*/FALSE);
+    jsEndCollapsibleSection();
+    puts("</table></div>");
+    }
+
+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("<b>Configure track container: "
            "<img height=12 src='../images/ab_up.gif'>"
             "<a href='%s?%s=%s&db=%s&c=%s&g=%s'>%s </a></b>",
             hgTrackUiName(), cartSessionVarName(), cartSessionId(cart),
             database, chromosome, cgiEncode(tdbParent->track), tdbParent->longLabel);
 printf("<p>");
 
-if (tdbIsComposite(tdb) && sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted"))
+if (tdbIsFacetedComposite(tdb))
     return;
 
 if (tdbParent->html)
     {
     // collapsed panel for Description
     printf("<p><table>");  // required by jsCollapsible
     jsBeginCollapsibleSectionFontSize(cart, tdb->track, "superDescription", "Description", FALSE,
                                             "medium");
     // TODO: better done with regex
     char *html = replaceChars(tdbParent->html, "<H", "<h");
     html = replaceChars(html, "</H", "</h");
 
     // remove Description header
     html = replaceChars(html, "<h2>Description</h2>", "");
     html = replaceChars(html, "<h3>Description</h3>", "");
@@ -3901,31 +3924,31 @@
 	    safef(longLabel, sizeof(longLabel), "%s", tdb->longLabel);
 	}
     printf("<B style='font-size:200%%;'>%s%s</B>\n", longLabel, tdbIsSuper(tdb) ? " Tracks" : "");
     }
 else
     {
     if (trackDbSetting(tdb, "wgEncode"))
         printf("<A HREF='/ENCODE/index.html'><IMG style='vertical-align:middle;' "
                "width=100 src='/images/ENCODE_scaleup_logo.png'><A>");
     // 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("<B style='font-size:%d%%;'>%s%s</B>\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 = "&dArr;";
         enum browserType browser = cgiBrowser();
         if (browser == btIE || browser == btFF)
             downArrow = "&darr;";
         printf("&nbsp;&nbsp;(<A HREF='#TRACK_HTML' TITLE='Jump to description section of page'>"
                "Description%s</A>)", 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("<B>Maximum&nbsp;display&nbsp;mode:&nbsp;</B>");
+        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("<B>Show or hide this container and all tracks:&nbsp;</B>");
         printInfoIcon("Hiding the entire container here will preserve the track visibility settings below, and they can be restored by selecting 'Show'.");
         }
     else
         printf("<B>Display&nbsp;mode:&nbsp;</B>");
 
     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("&nbsp;");
         cgiMakeButton("Submit", "Submit");
         // Offer cancel button always?    // composites and multiTracks (not standAlones or supers)
         if (tdbIsContainer(tdb))
             {
             printf("&nbsp;");
             cgiMakeOnClickButton("htui_cancel", "window.history.back();","Cancel");
             }
 
         if (tdbIsComposite(tdb) || tdbIsSuperTrack(tdb))
 	    {