Skip to content

Commit 193cfa6

Browse files
chore(docs): update code docs
1 parent f6beae3 commit 193cfa6

3 files changed

Lines changed: 36 additions & 116 deletions

File tree

docs/components_ui_dropdown_TrackDropdown.bs.html

Lines changed: 34 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,6 @@
4242
' widths plus this gutter so an open menu never spans more than two columns of
4343
' horizontal real estate.
4444
const TRACK_DROPDOWN_INTER_SLOT_GUTTER = 30
45-
' Multiplier applied to the TextSizeTask measurement when computing menu width.
46-
' GetOneLineWidth returns the font-metric width which can come up a few pixels
47-
' short of how the actual ScrollingLabel renders. The gap varies per string —
48-
' caps-heavy strings with numbers ("ENGLISH TRUEHD 7.1") drift only ~2-3px while
49-
' mixed-case strings with punctuation ("Spanish; Castilian SUBRIP") can drift
50-
' more — so a flat fudge constant is the wrong shape: it either over-pads short
51-
' strings or under-pads long ones. Scaling proportionally to the measured width
52-
' tracks the actual risk: short strings get a small buffer (no wasted space),
53-
' long strings get a larger buffer (no clipping). 3% is the lowest stable value
54-
' on tested hardware; bump to 1.04 / 1.05 if any title ever clips again.
55-
const TRACK_DROPDOWN_TEXT_WIDTH_MULTIPLIER = 1.03
5645

5746
sub init()
5847
m.log = new log.Logger("TrackDropdown")
@@ -71,12 +60,12 @@
7160
m.visibleRows = 5
7261
m.rowHeight = 0 ' computed in onItemsChanged
7362

74-
' TextSizeTask is required because roFontRegistry / Font.GetOneLineWidth are
75-
' MAIN|TASK-only — they cannot be created on the render thread where component
76-
' field observers run. The task receives titles + font size, returns a width
77-
' array we observe on the "width" field. See JRButtons.bs for the same pattern.
78-
m.textSizeTask = CreateObject("roSGNode", "TextSizeTask")
79-
m.textSizeTask.observeField("width", "onTextSizeTaskComplete")
63+
' Hidden Label used to measure each item's natural pixel width before we size the
64+
' menu. We use a themed LabelPrimarySmaller — same theme as the rendered row's
65+
' ScrollingLabel — so font URI + fontSizeSmaller + uiFontFallback scaling come
66+
' for free via LabelSmaller.init / JRLabel.init. Same trick FontScalingTask uses
67+
' against scene-level defaultFont/fallbackFont nodes (see FontScalingTask.bs).
68+
m.measureLabel = m.top.findNode("measureLabel")
8069

8170
m.top.observeField("focusedChild", "onFocusChanged")
8271

@@ -165,7 +154,6 @@
165154
' duplication read as "your current value, persistent above and inside the menu"
166155
' rather than competing with the alternatives. See TrackDropdownRow.onFocusStateChanged.
167156
sub onItemsChanged()
168-
' Clear existing items
169157
m.menuItems.removeChildren(m.menuItems.getChildren(-1, 0))
170158
m.menuItems.translation = [0, 0]
171159
m.focusedItemIndex = 0
@@ -178,129 +166,66 @@
178166
end if
179167

180168
constants = m.global.constants
181-
slotWidth = m.top.slotWidth
169+
slotWidth = int(m.top.slotWidth)
182170

183171
' Row height matches trigger font (fontSizeSmaller 25) with breathing padding so
184172
' focus border doesn't crowd the text. Mirrors JRDropdown's formula structure
185173
' (line height + padding) scaled for the smaller font.
186174
m.rowHeight = int(constants.fontSizeSmaller * 1.0) + 20
187175

188-
' Initial menu width = slotWidth. The async TextSizeTask measures each title and
189-
' may widen the menu up to 2 slot widths + the inter-slot gutter if titles are
190-
' long. In normal usage the task completes well before the user opens the menu,
191-
' so the user sees the auto-sized width — not the slotWidth fallback. If the
192-
' task hasn't completed (or doesn't run, e.g. in unit-test environment), the
193-
' menu still renders correctly at slotWidth, just without the long-title fit.
194-
initialWidth = slotWidth
176+
menuWidth = computeMenuWidth(items, slotWidth)
195177

196178
for each item in items
197179
menuItem = CreateObject("roSGNode", "TrackDropdownRow")
198180
menuItem.text = item.title
199181
menuItem.itemId = item.id
200-
menuItem.itemWidth = initialWidth
182+
menuItem.itemWidth = menuWidth
201183
menuItem.itemHeight = m.rowHeight
202184
m.menuItems.appendChild(menuItem)
203185
end for
204186

205187
visibleRows = m.visibleRows
206188
if items.count() < visibleRows then visibleRows = items.count()
207-
208189
viewportHeight = m.rowHeight * visibleRows
190+
209191
' clippingRect is in the viewport's local coords: [x, y, width, height]. Anything the
210192
' menuItems LayoutGroup draws outside this rect is invisible, enabling the scroll UX.
211-
m.menuViewport.clippingRect = [0, 0, initialWidth, viewportHeight]
212-
213-
m.menuBackground.width = initialWidth
193+
m.menuViewport.clippingRect = [0, 0, menuWidth, viewportHeight]
194+
m.menuBackground.width = menuWidth
214195
m.menuBackground.height = viewportHeight
215196

216197
if isValidAndNotEmpty(m.top.selectedItemId)
217198
onSelectedItemChanged()
218199
end if
219-
220-
' Kick off async measurement to refine width once the task returns.
221-
measureMenuWidthAsync(items)
222-
end sub
223-
224-
' measureMenuWidthAsync: Pushes the item titles through TextSizeTask so the task
225-
' can call roFontRegistry / GetOneLineWidth on a non-render thread (the registry
226-
' is MAIN|TASK-only). Result is delivered via the "width" observer below.
227-
sub measureMenuWidthAsync(items as object)
228-
if not isValid(m.textSizeTask) then return
229-
' Cancel any in-flight measurement so an older items[] response can't overwrite
230-
' a newer one.
231-
m.textSizeTask.control = "STOP"
232-
233-
titles = []
234-
for each item in items
235-
if isValid(item.title) then titles.push(item.title)
236-
end for
237-
if titles.count() = 0 then return
238-
239-
m.textSizeTask.fontsize = m.global.constants.fontSizeSmaller
240-
' Leave maxWidth at its XML default (1920 = full screen width). 1920 is wider
241-
' than any realistic track title at fontSizeSmaller so GetOneLineWidth returns
242-
' natural unwrapped widths, and we avoid passing an unusually large value that
243-
' could perturb the font-metric calculation.
244-
m.textSizeTask.text = titles
245-
m.textSizeTask.control = "RUN"
246200
end sub
247201

248-
' onTextSizeTaskComplete: Reads the per-title widths produced by TextSizeTask,
249-
' picks the maximum, adds row padding, and clamps to [slotWidth, slotWidth*2 +
250-
' interSlotGutter]. The lower clamp keeps the menu anchored under its trigger
251-
' column; the upper clamp prevents an open menu from spanning more than two
252-
' columns of horizontal real estate. Then resizes menuBackground, the clipping
253-
' viewport, and every row in lockstep.
254-
sub onTextSizeTaskComplete()
255-
widths = m.textSizeTask.width
256-
if not isValid(widths) or widths.count() = 0 then return
257-
258-
' Reject stale results: when the user changes the video source we STOP+RUN the
259-
' task with new titles, but a result from the previous run can still land
260-
' (control=STOP doesn't interrupt a synchronous getTextSize() in progress). If
261-
' the menu's current row count no longer matches the measured count, the items
262-
' array changed mid-flight — drop this result and let the in-flight task's
263-
' completion apply the up-to-date measurements.
264-
if widths.count() <> m.menuItems.getChildCount() then return
265-
202+
' Returns the menu width that fits the longest item title, clamped to
203+
' [slotWidth, slotWidth*2 + interSlotGutter]. The lower clamp keeps the menu
204+
' anchored under its trigger column; the upper clamp stops an open menu from
205+
' spanning more than two columns of horizontal real estate.
206+
'
207+
' Measurement is pixel-exact: m.measureLabel is a themed LabelPrimarySmaller, so
208+
' setting its text and reading boundingRect().width returns the same width the
209+
' rendered row's ScrollingLabel will use (same font, same fontSizeSmaller, same
210+
' uiFontFallback scale factor). No fudge multiplier needed. Same render-thread
211+
' boundingRect technique FontScalingTask uses against the scene's defaultFont /
212+
' fallbackFont labels.
213+
function computeMenuWidth(items as object, slotWidth as integer) as integer
266214
maxTextWidth = 0
267-
for each w in widths
268-
iw = int(w)
269-
if iw > maxTextWidth then maxTextWidth = iw
215+
for each item in items
216+
if isValid(item.title)
217+
m.measureLabel.text = item.title
218+
w = int(m.measureLabel.boundingRect().width)
219+
if w > maxTextWidth then maxTextWidth = w
220+
end if
270221
end for
271222

272-
' Scale the measured width by the multiplier to compensate for the per-string
273-
' render-vs-metric gap (see TRACK_DROPDOWN_TEXT_WIDTH_MULTIPLIER comment for
274-
' rationale), then add row padding for the menu's inner edges.
275-
desired = int(maxTextWidth * TRACK_DROPDOWN_TEXT_WIDTH_MULTIPLIER) + (TRACK_DROPDOWN_ROW_H_PADDING * 2)
276-
' m.top.slotWidth is dynamic (node field); coerce to int so 'desired' stays
277-
' integer-typed across the clamp comparisons.
278-
slotWidth = int(m.top.slotWidth)
223+
desired = maxTextWidth + (TRACK_DROPDOWN_ROW_H_PADDING * 2)
279224
if desired < slotWidth then desired = slotWidth
280225
upperBound = (slotWidth * 2) + TRACK_DROPDOWN_INTER_SLOT_GUTTER
281226
if desired > upperBound then desired = upperBound
282-
283-
applyMenuWidth(desired)
284-
end sub
285-
286-
' applyMenuWidth: Resizes the menu in lockstep — background + clipping viewport +
287-
' every row's itemWidth. Called from onTextSizeTaskComplete; no-op if the menu
288-
' has been torn down between task launch and task completion.
289-
sub applyMenuWidth(width as integer)
290-
if not isValid(m.menuItems) then return
291-
rowCount = m.menuItems.getChildCount()
292-
if rowCount = 0 then return
293-
294-
visibleRows = m.visibleRows
295-
if rowCount < visibleRows then visibleRows = rowCount
296-
viewportHeight = m.rowHeight * visibleRows
297-
298-
m.menuViewport.clippingRect = [0, 0, width, viewportHeight]
299-
m.menuBackground.width = width
300-
for i = 0 to rowCount - 1
301-
m.menuItems.getChild(i).itemWidth = width
302-
end for
303-
end sub
227+
return desired
228+
end function
304229

305230
sub onSelectedItemChanged()
306231
selectedId = m.top.selectedItemId
@@ -606,16 +531,11 @@
606531

607532
m.menuItems.removeChildren(m.menuItems.getChildren(-1, 0))
608533

609-
if isValid(m.textSizeTask)
610-
m.textSizeTask.unobserveField("width")
611-
m.textSizeTask.control = "STOP"
612-
m.textSizeTask = invalid
613-
end if
614-
615534
m.buttonBackground = invalid
616535
m.buttonBorder = invalid
617536
m.triggerLabel = invalid
618537
m.triggerChevron = invalid
538+
m.measureLabel = invalid
619539
m.menuContainer = invalid
620540
m.menuBackground = invalid
621541
m.menuViewport = invalid

docs/data/search.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/module-TrackDropdown.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)