PR-L3 — Layer Controls UI#120
Merged
Merged
Conversation
Add a new Layer Stack side panel that surfaces the resolved S-98
layer stack grouped by display plane (top-of-paint-stack first) and
lets the user toggle each dataset's in-memory Active flag.
Implementation
- Promote IDatasetLoaderService.CurrentStackedLayers and
LayerStackChanged from default to required members. Add a richer
CurrentStackEntries snapshot (one LayerStackEntry per stack slot)
plus GetActive/SetActive/ActiveChanged for the new in-memory
Active flag.
- DatasetLoaderService stores Active per dataset id (defaults to
true), and FlattenLayerOrder / BuildLoadedDatasetInfos honour it
so R-101-102-B and any future Active-aware rules see the toggle.
Active is process-local; persistence deferred to PR-L4
(TODO marker on _activeFlags).
- LayerStackViewModel groups CurrentStackEntries by S98DisplayPlane
in the canonical top-first order (EcdisAlerts → ... →
BaseChartUnder), hides empty planes by default with a 'Show empty
planes' toggle, preserves expansion state per plane across
rebuilds, and bridges the per-entry Active checkbox to
IDatasetLoaderService.SetActive.
- LayerStackView.axaml mirrors the CatalogPanelView pattern; PR-L4
TBDs (PdC picker, drag-reorder unification) are marked in code.
- MainWindow adds an activity-bar toggle button for the new pane
(sibling to Catalogs in the activity bar; the panel lives in the
shared activity-pane column rather than its own sidebar column,
which would conflict with the existing single-pane architecture).
- App.axaml.cs registers LayerStackViewModel in DI.
Strings & tooltips (Resources/Strings.{resx,cs})
- Pane_LayerStack, Tooltip_LayerStack, plus 14 LayerStack_* keys
(panel title, ShowEmptyPlanes, Active tooltip and Active vs
Visibility help text, plane-name labels for all nine S-98
display planes, child-count and priority format strings, and
empty-plane placeholder).
LoadedDatasetInfo
- XML doc on Active now explains Active vs DatasetEntry.IsVisible.
Tests (viewer 280 → 290, +10)
- LayerStackViewModelTests: plane grouping, top-first plane order,
within-plane priority descending, empty-plane hide/show toggle,
LayerStackChanged triggers rebuild, expansion state preserved
across rebuilds, IsActive round-trips through the loader.
- ActiveFlagTests: LoadedDatasetInfo.Active round-trip, loader
GetActive default, SetActive raises ActiveChanged. R-101-102-B
Active=false suppression is already covered in
S98DefaultRulesTests (PR-L2).
- Updated all 10 existing IDatasetLoaderService stubs in
tests/EncDotNet.S100.Viewer.Tests to implement the promoted
interface members.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Performance Gate✅ PASSED — no regressions. Threshold: 10.0%, MAD multiplier (k): 3.0, retry-zone mult: 2.0× Scenario summary
exchange-set-openIteration statistics
Spans (sum of all iterations)
Metrics
s101-portray-coldIteration statistics
Spans (sum of all iterations)
Metrics
s101-portray-warmIteration statistics
Spans (sum of all iterations)
Metrics
s101-render-warmIteration statistics
Spans (sum of all iterations)
Metrics
s102-coverageIteration statistics
Spans (sum of all iterations)
Metrics
s102-coverage-openIteration statistics
Spans (sum of all iterations)
Metrics
s102-coverage-render-largeIteration statistics
Spans (sum of all iterations)
Metrics
s124-vectorIteration statistics
Spans (sum of all iterations)
Metrics
s201-vectorIteration statistics
Spans (sum of all iterations)
Metrics
Generated by EncDotNet.S100.PerfReport gate command |
ReorderDatasetLayers previously only reordered layers in the input list and silently ignored layers it had never seen. Two consequences: 1. Toggling a dataset's Active flag off filtered it out of the reorder list, but its layer remained in map.Layers and kept painting. 2. R-101-102-B builds a *new* filtered MemoryLayer to replace the S-101 area layer when S-102 is active. That replica was not in _datasetLayers, so the reorder skipped it entirely — meaning the DepArea/DepCnt suppression has been an invisible no-op since PR-L2. Treat the supplied list as the authoritative dataset-layer slice: remove every previously-tracked dataset layer from map.Layers, then insert each input layer in order and track it as a dataset layer. Rule-filtered replicas now actually replace their originals, and inactive datasets disappear from the map. Updated IMapHost XML doc to document the new contract. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DockPanel.LastChildFill only stretches the last child, so when the empty-state Border and populated ScrollViewer were both non-dock siblings only the ScrollViewer filled. The empty Border took its content size at the top of the panel instead of centring. Wrap both in a single Grid that takes the fill slot so the empty state can centre horizontally and vertically. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a new Layer Stack side panel that surfaces the resolved S-98 layer stack grouped by display plane (top-of-paint-stack first) and lets the user toggle each dataset's in-memory Active flag.
What's in
IDatasetLoaderService.CurrentStackedLayersandLayerStackChangedare now required members. AddedCurrentStackEntries(richerLayerStackEntrysnapshot) plusGetActive/SetActive/ActiveChanged.DatasetLoaderServicestores Active per dataset id (default true).FlattenLayerOrderandBuildLoadedDatasetInfoshonour it, so R-101-102-B and any future Active-aware rules see the toggle. Process-local; persistence deferred to PR-L4 (TODO marker on_activeFlags). Distinct fromDatasetEntry.IsVisible— documented onLoadedDatasetInfo.Active.CurrentStackEntriesbyS98DisplayPlanein the canonical top-first order, hides empty planes by default with a 'Show empty planes' toggle, preserves per-plane expansion across rebuilds, and routes per-entryIsActivetoIDatasetLoaderService.SetActive.CatalogPanelView. PR-L4 TBDs (PdC picker / drag-reorder unification) are marked in code.LayerStackViewModelregistered as a singleton.Strings.resxentries. All checkboxes / toggle buttons carryToolTip.Tip.Placement assumption
The brief asked for "own column in the left sidebar, sibling to Catalogs". The existing
MainWindow.axamluses a single activity-pane column whose contents are selected byMainViewModel.SelectedActivity(driven by the activity bar). Adding a parallel column would double the sidebar chrome and break the established pattern, so I implemented Layer Stack as a new activity tab (sibling to Catalogs in the activity bar). That still satisfies "sibling to Catalogs, not a sub-tab of dataset list" — happy to refactor to a literal second column if you'd prefer.Tests
EncDotNet.S100.Viewer.TestsEncDotNet.S100.Pipelines.TestsLayerStackViewModelTests(6) — plane grouping, top-first order, within-plane priority desc, empty-plane hide/show,LayerStackChangedtriggers rebuild, expansion preserved across rebuilds, IsActive round-trips through the loader.ActiveFlagTests(4) —LoadedDatasetInfo.Activeround-trip, loaderGetActivedefault,SetActiveraisesActiveChanged. R-101-102-B Active=false suppression is already covered inS98DefaultRulesTests(PR-L2).IDatasetLoaderServicestubs to implement the promoted members.dotnet build EncDotNet.S100.slnx -c Releaseanddotnet test -c Releaseboth green.Out of scope (deferred to PR-L4)
// TODO PR-L4 (TBD-7): PdC picker UX— empty-state area of the panel.// TODO PR-L4 (TBD-11): unify ordering UX with DatasetsViewModel drag-reorder— drag-reorder stays as-is on the Datasets panel.// TODO PR-L4: persist Active in ViewerSettings— Active is in-memory only this PR.Visual regression
Panel is additive (new activity tab; no MainWindow column changes). No snapshot rebaseline needed.
Manual smoke (described, not enforced)
Load S-101 + S-102 → two planes visible (BaseChartOver/Bathymetry); toggle S-102 Active off → S-101 DepthArea reappears in render + pick.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com