Skip to content

PyUI: fit list titles to their room by width, not 35 chars - #1900

Merged
chrisj951 merged 1 commit into
spruceUI:Developmentfrom
abalakh:descriptive_list_title_cap
Oct 21, 2026
Merged

chrisj951 merged 1 commit into
spruceUI:Developmentfrom
abalakh:descriptive_list_title_cap

Conversation

@abalakh

@abalakh abalakh commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to #1871, as discussed. Instead of a 35-char cap, the descriptive list now works out each title's room in pixels (row minus icon column, offsets and the value on the right):

  • titles that fit are left alone
  • longer ones end with "..." right before the value
  • the selected one still scrolls after a second, clipped before the value

Widths are measured per letter. Each character's advance is cached per font in Display, and a title's width is just a sum. It ignores kerning, so it comes out 0–4 px wider than TTF_SizeUTF8, never narrower. That means a cut title never runs into the hours. The same estimate lets is_text_too_long skip measuring strings that are clearly short enough, and value widths are cached per list.

The activity tracker was the reason for the cap, and at 480×800 the 35 chars still overlapped the hours there. Now names end right before them at every resolution.

Measured on a Miyoo Mini+ (Spruce Nightly, 640×480, RAOfflineProxy running) with my real activity log (23 games) and Cheevos (20 games):

Development This PR
Activity tracker, render per frame 7.6–8.0 ms 6.0 ms
Activity tracker, TTF_SizeUTF8 calls per frame 8 0
Activity tracker, CPU while a long name scrolls 50% 45%
Activity tracker, CPU with a short name selected 5.8% 1.7%
Cheevos list, CPU with a long title selected 66% (scrolls forever) 7% (goes idle)
Cheevos list, render per frame 8.2 ms 7.1 ms
MainUI RSS in the tracker 32.0–32.4 MB 31.6–32.0 MB
Cheevos RSS in its games list 35.2 MB 33.9–34.8 MB
Per title, on the Mini+ TTF_SizeUTF8 Per letter
Width ~105 µs ~30–40 µs
Cut to fit ~0.45 ms (binary search) ~0.07 ms

Render is the median DescriptiveListView._render time. CPU is the share of one core. A new character costs ~0.1–1 ms the first time a font draws it, then it's cached. The caches stay small: about 5 KB per font, and at most ~160 KB for a list scrolled through 1000+ rows.

Also tested on desktop at all 8 resolutions in SPRUCE, MINIMAL, Pico-8 and ART_BOOK_NEXT. Settings and the other menus look pixel for pixel the same as on Development. Descriptions (70-char scroll) and values (25-char cap) are untouched.

cmp-480x800 Before/After/After+2s

@ryanmsartor

Copy link
Copy Markdown
Collaborator

This should not block merging, but just a note to self to explicitly check behavior on the A30 after merge but before a stable release since in the past it has shown some quirks regarding asset size calculation.

@abalakh
abalakh force-pushed the descriptive_list_title_cap branch from 87d4fba to 3b9d61b Compare October 21, 2026 17:58
@abalakh abalakh changed the title PyUI: Descriptive list view: let views turn off or set the title cap PyUI: fit list titles to their room by width, not 35 chars Oct 21, 2026
@chrisj951

Copy link
Copy Markdown
Contributor

This should not block merging, but just a note to self to explicitly check behavior on the A30 after merge but before a stable release since in the past it has shown some quirks regarding asset size calculation.

I wonder if glyphmetrics have the same issue

# up to a few pixels wider than TTF_SizeUTF8's; use get_text_dimensions where exact
# widths matter.
@classmethod
def get_text_width_from_glyphs(cls, purpose, text):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI for What @ryanmsartor is mentioning we have this as a helper method in device:

fix_device_font_height_calc_issues()

We will potentially need to use it alongside a width version.

The one being used here get_text_width_measurement_multiplier() functions the same but is set to 1 for each device, might need to set it for a30.

The difference is a multiplier vs passing the value to the device to decide. (I think passing it to the device is better if we end up needing it)

@chrisj951
chrisj951 merged commit 9e0e9d4 into spruceUI:Development Oct 21, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants