@@ -233,6 +233,23 @@ TRIGGERTIMESLIDERSCHANGEDFORTEST Test-only hook to invoke the slider
233233 (Hidden, not the narrower Access = {?matlab.unittest.TestCase},
234234 so Octave parsing survives — Octave has no matlab.unittest.)
235235
236+ #### ` broadcastTimeRangeNow(obj, tStart, tEnd) `
237+
238+ BROADCASTTIMERANGENOW Test-only synchronous broadcast bypassing the
239+ SliderDebounceTimer. Stock Octave 7 batch mode has unreliable
240+ timer scheduling; tests should use this entry point to drive
241+ the broadcast deterministically. Also updates the time labels
242+ (skipping the debounced onRangeSelectorChanged path).
243+
244+ #### ` env = computePreviewEnvelopeForTest(obj, nBuckets) `
245+
246+ COMPUTEPREVIEWENVELOPEFORTEST Test-only wrapper around the
247+ private computePreviewEnvelopeReturning_ . Runs the real
248+ aggregation and returns the envelope struct so tests can
249+ assert shape/monotonicity without scraping the selector's
250+ patch handles. When nBuckets is omitted, uses the method's
251+ own width-derived default.
252+
236253#### ` str = formatTimeVal(~, t) `
237254
238255FORMATTIMEVAL Format a numeric time value as a human-readable string.
@@ -383,6 +400,14 @@ Override in subclasses to respond to global time changes.
383400
384401Override in subclasses to report data time range.
385402
403+ #### ` series = getPreviewSeries(~, ~) `
404+
405+ GETPREVIEWSERIES Optional preview data for the time-range envelope.
406+ series = getPreviewSeries(obj, nBuckets) returns a struct with
407+ fields xCenters, yMin, yMax — each a 1xnBuckets row vector;
408+ yMin/yMax MUST be normalized to [ 0,1] within the widget's own
409+ y-range. Base returns [ ] to opt out of the preview envelope.
410+
386411#### ` lines = asciiRender(obj, width, height) `
387412
388413ASCIIRENDER Return ASCII representation of this widget.
@@ -483,6 +508,15 @@ detach this widget from global time.
483508Return cached min/max in O(1). Cache is kept up to date by
484509updateTimeRangeCache() which is called from render/refresh/update.
485510
511+ #### ` series = getPreviewSeries(obj, nBuckets) `
512+
513+ GETPREVIEWSERIES Per-bucket min/max preview for the dashboard envelope.
514+ series = getPreviewSeries(obj, nBuckets) returns a struct with
515+ fields xCenters, yMin, yMax — each a 1xnBuckets row vector; yMin
516+ and yMax are normalized into [ 0,1] across the widget's own
517+ current y-range. Returns [ ] when no data is bound or when the
518+ sample count is too low to downsample meaningfully.
519+
486520#### ` t = getType(~) `
487521
488522#### ` lines = asciiRender(obj, width, height) `
@@ -903,9 +937,9 @@ obj = DashboardLayout(varargin)
903937| Columns | ` 24 ` | |
904938| TotalRows | ` 4 ` | |
905939| ContentArea | ` [0 0 1 1] ` | |
906- | Padding | ` [0.02 0.02 0.02 0.02 ] ` | |
907- | GapH | ` 0.008 ` | |
908- | GapV | ` 0.015 ` | |
940+ | Padding | ` [0 0 0 0 ] ` | |
941+ | GapH | ` 0 ` | |
942+ | GapV | ` 0 ` | |
909943| RowHeight | ` 0.22 ` | |
910944| ScrollbarWidth | ` 0.015 ` | |
911945| OnScrollCallback | ` [] ` | function handle: @(topRow, bottomRow) |
@@ -1761,3 +1795,116 @@ TOSTRUCT Serialize widget to a struct for JSON export.
17611795
17621796FROMSTRUCT Deserialize a SparklineCardWidget from a struct.
17631797
1798+ ---
1799+
1800+ ## ` TimeRangeSelector ` --- Single-window time-range selector with data-preview envelope.
1801+
1802+ > Inherits from: ` handle `
1803+
1804+ selector = TimeRangeSelector(hPanel) attaches a time-range selector to a
1805+ uipanel. The selector owns its own axes inside the panel and draws:
1806+
1807+ * an (optional) aggregate min/max envelope patch behind the selection,
1808+ * a semi-transparent selection rectangle that can be panned by dragging
1809+ its middle and resized by dragging either of its two edge handles,
1810+ * two line handles at the left and right edges of the selection window.
1811+
1812+ Interaction uses figure-level WindowButton{Down,Motion,Up}Fcn. Any previously
1813+ installed callbacks are saved on construction and restored on delete().
1814+
1815+ Usage (the contract plan 03 uses to wire this into DashboardEngine):
1816+
1817+ selector = TimeRangeSelector(hPanel, ...
1818+ 'OnRangeChanged', @(tStart, tEnd) onRangeChanged(tStart, tEnd), ...
1819+ 'Theme', themeStruct);
1820+ selector.setDataRange(tMin, tMax); % full extent user can scrub
1821+ selector.setSelection(tStart, tEnd); % fires OnRangeChanged
1822+ selector.setEnvelope(xC, yMin, yMax); % optional preview
1823+ [tS, tE] = selector.getSelection();
1824+ delete(selector); % restores figure callbacks
1825+
1826+ Properties (public, configurable):
1827+ OnRangeChanged Function handle @(tStart, tEnd). May be [ ] .
1828+ Theme Theme struct (or [ ] ).
1829+ MinWidthFrac Minimum selection width as fraction of DataRange span.
1830+ EdgeTolPx Pixel tolerance for edge hit-testing.
1831+
1832+ Properties (read-only, set internally):
1833+ hPanel, hFigure, hAxes, hEnvelope, hSelection, hEdgeLeft, hEdgeRight
1834+ DataRange 1x2 [ tMin tMax] .
1835+ Selection 1x2 [ tStart tEnd] .
1836+ DragState 'idle' | 'panning' | 'resizeLeft' | 'resizeRight'.
1837+
1838+ Methods:
1839+ setDataRange(tMin, tMax) Set full extent; rescales selection.
1840+ setSelection(tStart, tEnd) Set/clamp/reorder selection; fires callback.
1841+ getSelection() Return [ tStart, tEnd] .
1842+ setEnvelope(xC, yMin, yMax) Update or hide aggregate envelope.
1843+ delete() Restore saved figure callbacks.
1844+
1845+ Compatible with MATLAB R2020b+ and Octave 7+ (D-11): uses only axes, patch,
1846+ line, uipanel primitives and WindowButton{Down,Motion,Up}Fcn — no
1847+ matlab.graphics.* , no uifigure/uiaxes, no addlistener on primitive properties.
1848+
1849+ ### Constructor
1850+
1851+ ``` matlab
1852+ obj = TimeRangeSelector(hPanel, varargin)
1853+ ```
1854+
1855+ TimeRangeSelector Construct a selector attached to a uipanel.
1856+
1857+ ### Properties
1858+
1859+ | Property | Default | Description |
1860+ | ----------| ---------| -------------|
1861+ | OnRangeChanged | ` [] ` | function handle @(tStart, tEnd) |
1862+ | Theme | ` [] ` | struct from DashboardTheme, or [ ] |
1863+ | MinWidthFrac | ` 0.005 ` | minimum selection width as fraction of DataRange span |
1864+ | EdgeTolPx | ` 10 ` | pixel tolerance for edge hit-test |
1865+
1866+ ### Methods
1867+
1868+ #### ` setDataRange(obj, tMin, tMax) `
1869+
1870+ setDataRange Set the full extent the user can scrub over.
1871+ The current selection is rescaled proportionally so that a
1872+ 50%-selected window remains 50% wide after the change.
1873+ Programmatic — does NOT fire OnRangeChanged; only user
1874+ drag interactions do.
1875+
1876+ #### ` setSelection(obj, tStart, tEnd) `
1877+
1878+ setSelection Update the selection window, clamping and reordering.
1879+ Swapped inputs (tStart > tEnd) are reordered. Values outside
1880+ DataRange are clamped. Widths smaller than MinWidthFrac * span
1881+ are widened around the requested midpoint. Fires OnRangeChanged
1882+ with the final [ tStart, tEnd] (if the callback is set).
1883+ Reorder swapped bounds (tStart < tEnd).
1884+
1885+ #### ` [tStart, tEnd] = getSelection(obj) `
1886+
1887+ getSelection Return the current selection as [ tStart, tEnd] .
1888+
1889+ #### ` setLabels(obj, leftText, rightText) `
1890+
1891+ setLabels Update the inline edge labels that track the selection.
1892+ Pass empty strings to hide a side's label. The text sits at the
1893+ mid-height of the selector, inside each edge handle.
1894+
1895+ #### ` setEnvelope(obj, xC, yMin, yMax) `
1896+
1897+ setEnvelope (Legacy) Draw the aggregate min/max preview envelope.
1898+ Kept for backward compat with tests. New code should prefer
1899+ setPreviewLines for per-widget line previews.
1900+
1901+ #### ` setPreviewLines(obj, lines) `
1902+
1903+ setPreviewLines Draw one downsampled line per widget preview.
1904+ lines is a cell array of structs, each with fields x and y
1905+ (equal-length row vectors; y already normalized to [ 0,1] ).
1906+ Each line is rendered with a distinct color from a fixed
1907+ palette, placed behind the selection rectangle so drag
1908+ interactions remain unaffected.
1909+ Clear previous preview lines.
1910+
0 commit comments