8e279300b1726f355767b467bc699685e90d487b max Tue Apr 21 06:27:16 2026 -0700 composite hgTrackUi: fix btn_minus_all to hide parent, remember last vis, refs #37182 Two fixes around composite visibility on hgTrackUi: 1) The global [-] button (btn_minus_all) unchecked all subtracks but left the composite visibility dropdown unchanged. The earlier fix in 814472876ea covered only the matrix-corner [-] (_matSetMatrixCheckBoxes); the global button goes through matSubCBsCheck, which is now updated to the same pattern. 2) [-] / [+] and individual subCB clicks now remember the prior visibility via a data-last-viz attribute on the dropdown. Hiding stashes the current value; re-showing restores it (falling back to pack/dense if nothing saved) instead of always jumping to pack. Factored into two helpers in hui.js: hideCompositeSaveVis() and an extended exposeAll(). subCfg.onUserCbChange (individual subCB/matCB path) delegates to the same helpers so behavior is uniform. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> diff --git src/hg/js/hui.js src/hg/js/hui.js index 4b3ce46764b..c028d2240a1 100644 --- src/hg/js/hui.js +++ src/hg/js/hui.js @@ -77,49 +77,66 @@ } else { $( matCBs ).each( function (i) { matChkBoxNormalize( this, classesHidden ); }); } } matSubCBsSelected(); obj.lastIndex = obj.selectedIndex; } function matSelectViewForSubTracks(obj,view) { waitOnFunction( _matSelectViewForSubTracks, obj,view); } function exposeAll() { - // Make main display dropdown show pack if currently hide + // Unhide composite vis. Prefers data-last-viz (stashed by hideCompositeSaveVis() + // when [-] or the last subtrack were unchecked), then pack, dense, else last option. var visDD = normed($("select.visDD")); if (visDD) { if ($(visDD).prop('selectedIndex') === 0) { - if ($(visDD).children('option[value="pack"]').length) + var saved = $(visDD).attr("data-last-viz"); + if (saved && $(visDD).children('option[value="'+saved+'"]').length) + $(visDD).val(saved); + else if ($(visDD).children('option[value="pack"]').length) $(visDD).val("pack"); else if ($(visDD).children('option[value="dense"]').length) $(visDD).val("dense"); else $(visDD).prop('selectedIndex',$(visDD).children('option').length - 1); $(visDD).trigger("change");// trigger on change code, which may trigger supertrack reshaping } // and effecting inherited subtrack vis // If superChild and hidden by supertrack, wierd things go on unless we trigger reshape if ($(visDD).hasClass('superChild')) visTriggersHiddenSelect(visDD); } } +function hideCompositeSaveVis() +{ + // Set composite vis dropdown to hide, stashing the current value in data-last-viz + // so exposeAll() can restore it. We set selectedIndex directly without triggering + // 'change', because propagateVis re-checks all subCBs when checkedCount===0. + var visDD = normed($("select.visDD")); + if (visDD && $(visDD).prop('selectedIndex') !== 0) { + $(visDD).attr("data-last-viz", $(visDD).val()); + $(visDD).prop('selectedIndex', 0); + $(visDD).addClass('changed'); + } +} + function matSubCbClick(subCB) { // subCB:onclick When a subtrack checkbox is clicked, it may result in // Clicking/unclicking the corresponding matrix CB. Also the // subtrack may be hidden as a result. // NOTE: if "subCfg" then 'change' event will update it if (isFauxDisabled(subCB,false)) { // disabled subCB is still clickable when "subCfg" subCB.checked = true; fauxDisable(subCB,false,""); // enable and get rid of message } matSubCBsetShadow(subCB,false); hideOrShowSubtrack(subCB); // When subCBs are clicked, 3-state matCBs may need to be set var classes = matViewClasses('hidden'); @@ -194,39 +211,32 @@ subCbs = $( subCbs ).filter("."+arguments[sIx]); // Successively limit list } if (state) { // If clicking [+], further limit to only checked ABCs var classes = matAbcCBclasses(false); subCbs = objsFilterByClasses(subCbs,"not",classes); // remove unchecked abcCB classes } $( subCbs ).each( function (i) { if (this.checked !== state) { this.checked = state; matSubCBsetShadow(this,false); $(this).trigger("change"); // NOTE: if "subCfg" then 'change' event will update it } }); if (state) exposeAll(); // Unhide composite vis? - else if ($("input.subCB:checked:visible").length === 0) { - // Set composite to hide directly, without triggering propagateVis - // (which would re-check all subCBs when checkedCount===0). - var visDD = normed($("select.visDD")); - if (visDD && $(visDD).prop('selectedIndex') !== 0) { - $(visDD).prop('selectedIndex', 0); - $(visDD).addClass('changed'); - } - } + else if ($("input.subCB:checked:visible").length === 0) + hideCompositeSaveVis(); showOrHideSelectedSubtracks(); matSubCBsSelected(); var tbody = normed($('tbody.sortable')); if (tbody) $(tbody).removeClass('sorting'); return true; } function matSetMatrixCheckBoxes(state) { // Called exclusively by matrix [+][-] buttons on click var tbody = normed($('tbody.sortable')); if (tbody) $(tbody).addClass('sorting'); @@ -281,30 +291,35 @@ matXY = $( matXY ).filter(":checked"); if (matXY.length === 0) subCBs.each( function (i) { matSubCBcheckOne(this,state); }); for (var mIx=0;mIx<matXY.length;mIx++) { classes = $(matXY[mIx]).attr("class").split(' '); classes = aryRemove(classes,["matCB","changed","disabled"]); /* jshint loopfunc: true */// function inside loop works and replacement is awkward. $( subCBs ).filter('.'+classes.join(".")).each( function (i) { matSubCBcheckOne(this,state); }); } } } else // state not checked so no filtering by other matCBs needed subCBs.each( function (i) { matSubCBcheckOne(this,state); }); + if (state) + exposeAll(); // Unhide composite vis? + else if ($("input.subCB:checked:visible").length === 0) + hideCompositeSaveVis(); + return true; } function matSubCBsEnable(state) { // Enables/Disables subtracks checkboxes. // If additional arguments are passed in, the list of CBs will be narrowed by the classes var subCBs = $("input.subCB"); for (var vIx=1;vIx<arguments.length;vIx++) { if (arguments[vIx].length > 0) subCBs = subCBs.filter("."+arguments[vIx]); // Successively limit list } subCBs.each( function (i) { if (state) { fauxDisable(this,false,''); $(this).parent().attr('cursor','');