Skip to content

Fix text truncation, hover-scroll, and layout overlap across the app - #71

Merged
XeldarAlz merged 4 commits into
XeldarAlz:masterfrom
BluntEXE:fix/text-overlap-marquee
Jul 29, 2026
Merged

XeldarAlz merged 4 commits into
XeldarAlz:masterfrom
BluntEXE:fix/text-overlap-marquee

Conversation

@BluntEXE

Copy link
Copy Markdown
Contributor

Summary

Fixes a batch of text truncation, hover-scroll, and layout overlap bugs reported across the app (Photos, Notes, Timers, Dailies, Activity, Fishing, Market, Clock, Aethergram, Velvet, Music, Health, Yellow Pages, Muster, Direct Messages, Linkshells, Collections, and the three arcade mini-games), plus a couple of framework-level bugs found along the way that were causing some of them.

Root causes found

Most of these bugs traced back to a small number of repeating patterns rather than being independent one-offs:

  • Shared hover state on stacked text. A single hover boolean, computed for a whole row, was passed into two separate Marquee calls (title + subtitle). Hovering either line scrolled both. Fixed by giving each line its own hover check, using Marquee.DrawLeftAuto/DrawCenteredAuto where possible.
  • Unclamped sibling eating shared width. A row split between two elements (e.g. a name and a value, or two pills) where one side was measured and clamped but the other was drawn at full natural width. When the unclamped side grew, it pushed into or past its sibling. Fixed by giving both sides a computed floor and letting them shrink or scroll together, following the same pattern used in MarketApp.Detail.cs's hero card fix.
  • Typography.DrawCentered's auto-wrap heuristic. The no-maxWidth overload estimates a safe wrap width from the current ImGui window's edges relative to the text's center point. That assumption breaks for anything not roughly centered in a full-size window: a small host window (the minimized-phone icon), or an element sitting near the edge of a multi-column layout. Found three independent instances of this (Yellow Pages intent-tile category count, the minimized-phone notification badge, hover tooltips in general). Fixed each call site by bypassing the auto-wrap path with an explicit-width Marquee call or a manual measure-and-draw, rather than patching the heuristic itself (too many other call sites depend on its current behavior to change it globally without a full audit).
  • Padding equal to, not greater than, corner radius. A pill button's text padding must exceed its corner radius (height * 0.5), not just be some positive number, or the text visually crowds the curve.

Per-area changes

  • Photos, Notes, Timers, Dailies, Activity, Fishing, Market, Clock, Aethergram, Velvet, Health, Yellow Pages, Muster, Direct Messages: converted static Typography.FitText truncation to Marquee hover-scroll wherever text could realistically overflow, applying the shared-hover and unclamped-sibling fixes above where relevant. This includes Muster's host name/status/meta text (MusterApp.Detail.cs, .Directory.cs, .Manage.cs, MusterCard.cs), the DM/Aethergram thread header name and subtitle (MessageApp.Thread.cs, AethergramApp.Thread.cs), and a Velvet comment timestamp that could overlap a review badge.
  • Aethergram: the multi-photo page-indicator dots on both the feed card and detail page now only render at phone size M and up; below that there isn't room to show them without colliding with the share button, so they're hidden entirely instead of squeezed in.
  • Music: Recently Played chip title/author text is hidden below phone size M for the same reason; the cover art expands to fill the freed space instead of leaving a gap.
  • Yellow Pages: DrawIntent's header (the screen you land on from Go Somewhere/Hire Someone/Join Something) now reserves space for the scope pill instead of using a plain centered title. DrawRailCard (the "Open Now" rail) converted to Marquee. The intent tile's category count no longer goes through Typography.DrawCentered's auto-wrap path.
  • Linkshells: LinkshellRow's title width reservation only accounted for the mute-bell icon, never the timestamp drawn on the same row; now reserves the timestamp's measured width first.
  • Collections: the per-tile completion-percentage ring is slightly larger and better-proportioned relative to its icon (radius 23 to 25px, icon gap 18 to 8px).
  • Games (Tetris, Watersort, Breakout): added GameHud.PillWidth (and a sizeScale parameter on Pill/ScorePill) so HUD layout can be computed from a pill's real measured width instead of guessed fixed offsets, which is what was causing Score/Level/Best to overlap the Hold/Next preview slots and each other. Watersort and Breakout's HUD rows now solve their layout as one group (fixed anchors define a middle zone, flexible elements fit inside it) rather than two independent one-sided clamps that could fight each other. Tetris's HUD went through several rounds of layout iteration; final shape is Hold/Next in the corners (fixed size, since their piece-preview rendering assumes a fixed slot size), Restart in the gap between them, and Level/Score/Best as their own row below, spanning the full board width for maximum room against large scores. Breakout's top bar was changed to match Beat's shape (Restart pinned to a corner independent of the score pills, lives shown as a vertical 3-slot indicator instead of a horizontal row) per direct request.
  • HoverTooltip: wrap-width calculation now uses the viewport's bounds instead of the current ImGui window's content region, since tooltips render on the foreground draw list and can extend past a small host window.
  • StepperField: chevron button width now clamps to a fraction of the actual rect width instead of a fixed constant, so it can't exceed the available space on a narrow rect.
  • AppHeader.DrawTitleWithReserve: added an optional TextStyle parameter so callers with a non-default title size (Photos) can use the shared helper instead of duplicating its logic inline.

Test plan

  • dotnet build clean after every change in this batch
  • Each fix confirmed in-game via full plugin reload (not hot-reload) before moving to the next, including several rounds of screenshot-driven iteration on the Tetris HUD and the Yellow Pages venue-ad detail page
  • No changes to game logic, only rendering/layout

Converts static FitText truncation to Marquee hover-scroll across Photos,
Notes, Timers, Dailies, Activity, Fishing, Market, Clock, Aethergram,
Velvet, Health, and Yellow Pages, and fixes the HUD layout in Tetris,
Watersort, and Breakout so Score/Level/Best pills no longer overlap the
Hold/Next preview slots or each other.

Most of the individual bugs traced back to a handful of repeating root
causes: shared hover state feeding two stacked Marquee texts at once,
one side of a row being unclamped while its sibling was, and
Typography.DrawCentered's window-relative auto-wrap heuristic
misfiring on small host windows (minimized-phone icon) and edge-of-row
elements. Fixed each occurrence and, where the pattern was structural
(GameHud pill sizing, HoverTooltip's wrap bounds), fixed the shared
component instead of patching every call site by hand.

Full breakdown and rationale in the PR description.

@XeldarAlz XeldarAlz left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Verified on 39e06bc in a throwaway worktree: dotnet build clean (0 errors, 4 pre-existing NU1902 warnings), dotnet test 139/139 passing. Not run in-game here, so the screenshot iteration is taken on trust.

Approach is right: fixing GameHud.PillWidth, HoverTooltip and AppHeader.DrawTitleWithReserve at the component level instead of patching every call site is the correct call. Five inline notes, one blocking.

Comment thread src/Aetherphone/Windows/Widgets/WidgetChrome.cs Outdated
Comment thread src/Aetherphone/Apps/YellowPages/YellowPagesApp.Detail.cs Outdated
Comment thread src/Aetherphone/Apps/YellowPages/YellowPagesApp.Browse.cs
Comment thread src/Aetherphone/Apps/YellowPages/YellowPagesApp.Browse.cs Outdated
Comment thread src/Aetherphone/Windows/Components/HoverTooltip.cs
- WidgetChrome: MeasureTrackedWidth added trailing tracking that
  EyebrowWidth omits, causing the marquee threshold to disagree with
  the layout width callers pass as maxWidth.
- YellowPagesApp.Detail: restore the accent stroke on tag pills that
  the old inline pill draw included.
- YellowPagesApp.Browse: cache the per-label word split used by
  FitLabelStyle instead of re-splitting every frame per intent tile.
- HoverTooltip: cap wrap width so long labels don't stretch across
  the full viewport.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@XeldarAlz XeldarAlz left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed all 41 files. Verified: built ed685cc5 clean in a detached worktree (0 errors, only the pre-existing AngleSharp NU1902 warnings).

The three root causes in the description hold up, and the fixes are the right shape: per-line hover instead of one shared boolean, both sides of a row given a floor, and the Typography.DrawCentered auto-wrap path bypassed at the call site rather than patched globally. Checked and happy with: the GameHud sizeScale plumbing (measure and draw both land on the same FontService.NearestSize bucket, so PillWidth tracks the rendered text), the HoverTooltip viewport switch, the Market hero card reflow, and the Linkshell timestamp reserve.

2 blocking (both in WidgetChrome.cs, both small), 6 non-blocking. Details inline.

One housekeeping note: ed685cc5 carries a Co-Authored-By: Claude Sonnet 5 trailer. I keep those out of this repo's history, so please drop it before merge (or I'll strip it on squash).

Comment thread src/Aetherphone/Windows/Widgets/WidgetChrome.cs Outdated
Comment thread src/Aetherphone/Windows/Widgets/WidgetChrome.cs Outdated
Comment thread src/Aetherphone/Apps/Notes/NotesApp.cs
Comment thread src/Aetherphone/Apps/Photos/PhotosApp.Grid.cs Outdated
Comment thread src/Aetherphone/Apps/Music/MusicApp.Home.cs Outdated
Comment thread src/Aetherphone/Apps/Games/Breakout/BreakoutApp.cs Outdated
Comment thread src/Aetherphone/Apps/Velvet/VelvetShell.Profile.cs Outdated
Comment thread src/Aetherphone/Apps/YellowPages/YellowPagesApp.Browse.cs Outdated
- WidgetChrome.EyebrowMarquee now measures fullWidth with EyebrowWidth
  instead of a second, disagreeing per-glyph formula, so labels that
  fit stop taking the clip/marquee path on hover. Deletes the dead
  MeasureTrackedWidth and its per-char allocation.
- Brace the single-line if per repo style.
- Marquee's three *Auto helpers now hover-test through UiInteract.Hover
  instead of raw ImGui.IsMouseHoveringRect, so gating (InputBlocked,
  MouseOverOverlay) applies at every *Auto call site instead of only
  where callers remembered to gate manually.
- Extract AppSkin.HeaderActionWidth so PhotosApp's nav bar reserve
  can't drift from the button's actual geometry.
- Recent Play chips now show a title-only line at Caption1 below M
  instead of dropping all text, so XS/S chips aren't unlabelled art.
- Breakout's life-pip loop reads BreakoutBoard.StartingLives instead
  of a duplicated literal.
- Velvet profile name marquee measures the untruncated display name
  for its box width instead of the already-ellipsised string.
- Move YellowPages' IntentLabelWords cache field up with the type's
  other statics.
XeldarAlz pushed a commit that referenced this pull request Jul 29, 2026
* fix(jobs): reserve header space and marquee row text

Jobs was the one app left off PR #71's sweep: its header title used
plain Typography.DrawCentered (no reserve for the palette/categories
buttons, no marquee), and gearset rows used static FitText for name
and level/ilvl instead of scrolling on hover. Brings it in line with
the AppHeader.DrawTitleWithReserve + Marquee.DrawLeftAuto pattern
used everywhere else.

* fix(jobs): correct header centering, marquee hover gating, per-row string churn

- AppHeader.DrawTitleWithReserve takes an explicit leftReserve
  (default 44f for the seven callers with a back button); Jobs has no
  back button and now passes 0, so the title centers in the true
  content width instead of shifting left.
- Marquee's *Auto helpers hover-test through UiInteract.Hover instead
  of a raw ImGui.IsMouseHoveringRect, so rows stop scrolling under the
  color picker, category editor, and dropdowns that call
  UiInteract.BlockThisFrame.
- JobEntry now builds its row ids once in the constructor instead of
  JobsApp concatenating them every row, every frame.
Resolves the conflicts XeldarAlz#73 and XeldarAlz#74 created.

- AppHeader.DrawTitleWithReserve now carries both new optional parameters
  (style from this branch, leftReserve from XeldarAlz#73).
- LinkshellRow keeps this branch's timeReserve subtraction on top of
  master's UiInteract.Hover gate.
- Dailies, Timers and Message drop master's row hover locals: the rows
  moved to Marquee.DrawLeftAuto, which hit-tests internally, so the
  locals were dead (and Message's shadowed an inner declaration).
- Tetris keeps the outerMargin extraction with master's gated hover.
- Music keeps the showAuthor branch with master's gated hover.

Also converts the 15 raw ImGui.IsMouseHoveringRect hit tests this branch
reintroduced to UiInteract.Hover. Master holds the invariant that the
call only appears inside UiInteract; leaving them raw would regress the
occlusion fix for every text row this branch touches.
@XeldarAlz

Copy link
Copy Markdown
Owner

Pushed a merge of master onto this branch (5a116ac7) since merging #73 put it in conflict. Verified: Release build clean (0 errors), dotnet test 158/158.

Conflict resolutions, all "keep both":

  • AppHeader.DrawTitleWithReserve carries both new optional parameters: style from here, leftReserve from fix(jobs): reserve header space and marquee row text #73. Order is style then leftReserve, so this branch's positional callers are unaffected.
  • LinkshellRow.cs:75 keeps the - timeReserve subtraction on top of master's UiInteract.Hover.
  • DailiesApp.cs, TimersApp.cs, MessageApp.Thread.cs: dropped master's row hover locals. Those rows moved to Marquee.DrawLeftAuto, which hit-tests internally, so the locals were dead, and Message's outer one shadowed an inner declaration.
  • TetrisApp.cs:117 keeps the outerMargin extraction with master's gated hover.
  • MusicApp.Home.cs:151 keeps the showAuthor branch with master's gated hover.

One change beyond the conflicts: this branch predates #74, so it reintroduced 15 raw ImGui.IsMouseHoveringRect hit tests (Aethergram/Message thread headers, Music chips, Velvet profile, WidgetChrome, LinkshellRow, Tetris). Master now holds the invariant that the call only appears inside UiInteract.cs; leaving them raw would have regressed the occlusion fix for every text row this branch touches, so they are all UiInteract.Hover now.

Threads are all resolved and it is mergeable again. Pull before you push anything further.

@XeldarAlz
XeldarAlz merged commit bfed669 into XeldarAlz:master Jul 29, 2026
2 checks passed
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.

2 participants