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)) {