Phase 1039: per-graph current-view boxes in the dashboard time slider#172
Open
HanSur94 wants to merge 13 commits into
Open
Phase 1039: per-graph current-view boxes in the dashboard time slider#172HanSur94 wants to merge 13 commits into
HanSur94 wants to merge 13 commits into
Conversation
- dark: amber [0.95 0.62 0.20], contrasts bluish-gray Selection - light: dark amber [0.85 0.45 0.05], contrasts dark-blue Selection - per-preset (mirrors MarkerPlantLog) so light/dark can differ
…im listener slot - getCurrentXLim() returns the LIVE wrapped-FastSense axes XLim ([min max]) read via get(ax,'XLim'), or [] when not rendered — distinct from the getTimeRange() data-extent cache - CurrentViewXLimListener_ engine-owned listener slot + Hidden setCurrentViewXLimListenerForEngine_ setter (mirrors PlantLogXLimListener_) - delete() releases the listener before FastSenseObj teardown (try/catch)
- hCurrentViewBox patch + hCurrentViewLeft/Right dashed edge lines - distinct style: FaceAlpha 0.12, LineStyle '--' LineWidth 1, theme CurrentViewBoxColor with amber fallback; PickableParts/HitTest off - setCurrentView(tStart,tEnd): validate finite, reorder, clamp to DataRange, store CurrentView, redraw, make visible (no-throw guarded) - hideCurrentView(): clear CurrentView, NaN data, Visible off - redraw_ refreshes box geometry when CurrentView is non-empty - header doc updated (Properties/Methods/CurrentView)
- testEmptyBeforeRender: [] before render - testReturnsLiveXLimAfterRender: 1x2 == live axes XLim - testReflectsZoom: tracks programmatic xlim([2 5]) (reads LIVE axes) - testNotEqualToDataExtentWhenZoomed: live view differs from getTimeRange cache - testListenerSlotSetterAndDeleteNoThrow: engine slot setter + delete() are safe
- 7 class-based tests: visible-box, geometry, hide, clamp, reorder, distinct-style/non-interactive, no-throw-after-delete - off-screen figure factory mirrors TestTimeRangeSelectorEventMarkers - geometry asserts are orientation-agnostic (MATLAB/Octave parity)
testNoThrowAfterDelete asserted that calling setCurrentView/hideCurrentView after delete(selector) does not throw — impossible in MATLAB, which throws "Invalid or deleted object" at method dispatch on a deleted handle before any in-method guard runs. Replaced with testNoThrowAfterGraphicsDestroyed: destroy the underlying figure (killing all graphics handles) while the selector object stays alive, then call the API — exercises the real ishandle guards (redraw_ + each box handle). Wave 1 now: TimeRangeSelector 7/7, FastSenseWidget 5/5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- updateCurrentViewIndicator_ (private): collects out-of-sync widget XLims via getCurrentXLim, unions them, shows/hides the slider current-view box per the epsilon-vs-Selection rule (0.005*span) - attachCurrentViewXLimListener_ (Hidden): engine-owned XLim PostSet listener mirroring attachPlantLogXLimListener_ (idempotent, Octave-skip, try/catch, namespaced DashboardEngine:currentViewIndicatorFailed) - updateCurrentViewIndicatorForTest_ (Hidden seam): routes to the private method so Wave 3 integration tests drive the decision on Octave too Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- render() tail: attach a current-view XLim listener to every FastSenseWidget across all pages (SITE 1; Octave-skipped in the helper) - broadcastTimeRange tail: recompute indicator so a re-sync hides the box (SITE 2) - onLiveTick tail: recompute each tick so drift in/out of sync updates without user interaction (SITE 3) - switchPage tail: recompute for the newly active page (SITE 4) All calls appended + try/catch wrapped; sync/Selection semantics of broadcastTimeRange, onLiveTick, and switchPage are unchanged (additive only). mh_lint/style/metric clean; Octave render+seam smoke passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- tests/suite/TestDashboardCurrentViewIndicator.m (6 tests): all-synced hidden, one-zoomed visible at window, two-zoomed union, re-sync hides, sub-epsilon stays hidden, no-selector guard no-throw. Drives the engine via updateCurrentViewIndicatorForTest_. - examples/demo_current_view_box.m: 3-widget dashboard, top plot pre-zoomed so the amber current-view box shows on load; interaction instructions. - FastSenseWidget: add CurrentXLimOverrideForTest_ Hidden seam — getCurrentXLim returns it verbatim when set. FastSense rebuilds its axes during zoom re-resolve, so a raw programmatic xlim() poke is not durable under the unittest runner's event flushing; the override makes the integration test deterministic. The real axes<->getCurrentXLim path stays covered by TestFastSenseWidgetCurrentXLim. Empty default => zero production impact. Verified (matlab-MCP authoritative): TimeRangeSelector 7/7, FastSenseWidget 5/5, Dashboard integration 6/6 = 18/18. Real interactive zoom confirmed live: getCurrentXLim tracks the zoomed window, box shows it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Change from a single union box to ONE box per visible, out-of-sync graph, each coloured to match that graph's slider preview line (per user request). - TimeRangeSelector: replace single hCurrentViewBox with a reused handle POOL (hCurrentViewBoxes/EdgesL/EdgesR). New setCurrentViews(ranges, colorIdxs) draws N boxes; setCurrentView is a back-compat single-box wrapper; hideCurrentView clears the pool. Palette extracted to shared previewPalette_ used by BOTH preview lines and current-view boxes, so box k matches preview line k exactly. redraw_ refreshes all boxes. - DashboardEngine.updateCurrentViewIndicator_: collect per-out-of-sync-widget (range, preview-line index) instead of a union; show each box only when that graph's view differs from the Selection (epsilon 0.005*span); colour index = the graph's position among valid-preview widgets (mirrors linesList order). - Tests: TimeRangeSelector 9/9 (added multi-box + pool-shrink + colour tests), Dashboard integration 6/6 (union test -> two-separate-boxes-with-distinct- colours). FastSenseWidget 5/5 unchanged. 20 tests total, MISS_HIT clean. - Demo: pre-zooms two plots to different windows -> two differently-coloured boxes on load; instructions updated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update CONTEXT decision (union -> one coloured box per out-of-sync graph, matching each graph's preview-line colour), HUMAN-UAT items, and the integration test header/coverage doc to the per-graph design. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- TestDashboardCurrentViewIndicator: add testPageSwitchScopesBoxesToActivePage
— builds a 2-page dashboard, zooms a graph on each page, and asserts boxes
follow the ACTIVE page across REAL switchPage calls (P1 box clears on switch
to P2, P2 box shows, P1 box restored on switch back). Verifies the indicator
scopes to activePageWidgets() and the switchPage wiring (Plan 03 SITE 4)
auto-refreshes — no manual seam call after the switches. 7/7 integration.
- examples/demo_current_view_box_multitab.m: live 2-page ("Process"/"Utilities")
demo, one plot pre-zoomed per page; boxes track the active tab.
Phase 1039 totals: TimeRangeSelector 9 + FastSenseWidget 5 + Dashboard 7 = 21.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two bugs made the boxes never appear on a second tab: 1. Missing listener on later-realized pages. The current-view XLim listener was attached only during render(), but widgets on pages other than page 1 aren't realized then (no axes) so the attach silently skipped them, and nothing re-attached when the tab opened. Fix: re-attach the active page's (now-realized) FastSenseWidgets in switchPage, and also attach in onScrollRealize for below-the-fold widgets. attachCurrentViewXLimListener_ is idempotent + skips unrealized widgets, so repeats are safe. 2. Listener fired mid-rebuild. FastSense re-resolves/rebuilds its axes on a zoom, firing the XLim listener multiple times; the box could be left hidden on a transient read even though the final view is zoomed. Fix: debounce — the listener now restarts a 0.15s one-shot timer (CurrentViewDebounceTimer_, mirrors SliderDebounceTimer) so the box refreshes ONCE after FastSense settles, reading the final getCurrentXLim. Timer cleaned up alongside the other debounce timers; updateCurrentViewIndicator_ guards isObjValid_ for the post-delete fire. Verified: 2-page dashboard, live-zoom (listener+debounce, no manual seam) on BOTH tabs surfaces the correct box. TimeRangeSelector 9/9, Dashboard 7/7 (incl. multi-tab), FastSenseWidget 5/5. MISS_HIT clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
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.
Summary
Adds a current-view indicator to the dashboard's lower preview slider (
TimeRangeSelector). When a plot's x-limits are not synchronized with the slider selection (i.e. you've zoomed/panned that plot directly), a small box appears inside the slider marking that plot's current view — one box per out-of-sync graph, each drawn in the same colour as that graph's slider preview line. Boxes scope to the active page and update on tab switch. The slider's selection rectangle and its drag behaviour are unchanged.What's included
TimeRangeSelector—setCurrentViews(ranges, colorIdxs)/hideCurrentView()draw a reused pool of per-graph boxes (patch + dashed edges) in absolute data-time space. The preview-line palette is now a sharedpreviewPalette_()used by both preview lines and boxes, so a box matches its line's colour by index.setCurrentView(a,b)kept as a single-box back-compat wrapper.FastSenseWidget.getCurrentXLim()— returns the live axes XLim (not the data-extent cache), plus an engine-owned XLim-listener slot.DashboardEngine.updateCurrentViewIndicator_— collects each out-of-sync, visible widget's view + preview-index, shows a box only when it differs from the selection (epsilon0.005·span), hides when synced. Wired to the per-widget XLim listener, live tick, post-broadcast, and page switch. A 0.15 s debounce (CurrentViewDebounceTimer_) coalesces FastSense's zoom re-resolve so the box reflects the settled view. Listeners are (re)attached for widgets realized later (other tabs / scroll).DashboardTheme.CurrentViewBoxColortoken (light + dark).TestTimeRangeSelectorCurrentView(9),TestFastSenseWidgetCurrentXLim(5),TestDashboardCurrentViewIndicator(7, incl. multi-tab). MISS_HIT clean.examples/demo_current_view_box.m,examples/demo_current_view_box_multitab.m.Notes
try/catchguarded; Octave skips the listener and refreshes via the tick/switch/broadcast hooks)..planning/artifacts,FastSense.muntouched).🤖 Generated with Claude Code