Skip to content

fix(ui): scale auto-icons by min(w,h) to prevent overflow in narrow panels#10376

Open
RafaelHGOliveira wants to merge 4 commits intoCard-Forge:masterfrom
RafaelHGOliveira:pr/flabel-icon-min-dim
Open

fix(ui): scale auto-icons by min(w,h) to prevent overflow in narrow panels#10376
RafaelHGOliveira wants to merge 4 commits intoCard-Forge:masterfrom
RafaelHGOliveira:pr/flabel-icon-min-dim

Conversation

@RafaelHGOliveira
Copy link
Copy Markdown
Contributor

@RafaelHGOliveira RafaelHGOliveira commented Apr 12, 2026

Problem

`FLabel.resetIcon()` scaled non-background icons only by the panel's height, so icons overflowed horizontally when a panel became narrow without becoming short. This is visible when resizing the match window or in layouts with multiple narrow player fields side by side.

Fix

Use `Math.min(getHeight(), getWidth())` as the scaling basis so icons always fit within the smaller dimension.

```java
// Before
final int h = (int) (getHeight() * iconScaleFactor);

// After
final int basis = Math.min(getHeight(), getWidth());
final int h = (int) (basis * iconScaleFactor);
```

Also: PlayerDetailsPanel improvements

  • `baseFontSize` tracking — the font auto-sizing loop was starting from a hardcoded `14` even when a subclass configured a different size. Now uses the field.
  • Parameterized constructors — `DetailLabel` and `DetailLabelNumeric` accept `fontSize` and `iconScaleFactor`, enabling per-label config without changing existing defaults.
  • Mana labels — `DetailLabelMana` now uses 16pt font and 0.70 scale factor for better visibility.

Notes

This is not a perfect solution — icons may still appear small in very narrow panels — but it is already a noticeable improvement for 4-player Commander layouts where each player field is roughly one quarter of the screen width.

Screenshot

icon scaling in narrow panels

…t/icon config

FLabel.resetIcon() previously scaled only by panel height, causing icons to
overflow horizontally in narrow panels (e.g. resized match windows). Change
the basis to min(width, height) so icons always fit within the available area.

Also improve PlayerDetailsPanel:
- Track baseFontSize per label so the font auto-sizing loop starts from the
  configured size, not a hardcoded 14
- Add parameterized constructors to allow per-label font and icon scale config
- Use 16pt font and 0.70 scale for mana symbols for better visibility
@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented Apr 12, 2026

This is not a perfect solution — icons may still appear small in very narrow panels — but it is already a noticeable improvement for 4-player Commander layouts where each player field is roughly one quarter of the screen width.

I thought of a potential fix for root cause of this scaling issue:

When resizing a player field window it currently resizes both the battlefield itself and the player details panel on left hand side, I assume based on some proportional logic.

Instead, set a minimum width for the player details panel which ensures labels are always a visible font size. Once rescaling causes the panel to hit that minimum, further window resizing only affects the battlefield area while leaving the details panel at the fixed minimum.

…text length

Address review feedback from @MostCromulent: instead of relying only on
min(w,h) for icon scaling, set a minimum width for the avatar and player
details panels via MigLayout constraints. When the window is resized
narrow, the details panel stops shrinking at 60px and the battlefield
area absorbs the remaining width change.

Additionally, PlayerDetailsPanel labels now dynamically reduce their
icon scale factor as text grows (e.g. multi-digit life totals). Each
extra character beyond 1 reduces the icon by ~15%, leaving room for
the text to render without overlap.
@RafaelHGOliveira
Copy link
Copy Markdown
Contributor Author

RafaelHGOliveira commented Apr 14, 2026

@MostCromulent I applied your suggestion in the PR:

VField.java — avatar and details panels now use MigLayout constraints w 60px:10%:10% (min 60px, preferred and max at 10%), and the scroller (battlefield) uses w 0:85%, growx so it absorbs remaining space. Once the window is narrow enough that 10% would drop below 60px, the panels hold steady and the battlefield keeps shrinking.

Honest note: at normal window sizes I didn't notice a big visual difference from the original min(w,h) approach — the benefit is mostly a safety floor for extreme narrow layouts (4-player Commander on a small window), where the details panel would otherwise degrade below legibility. Left it applied regardless since it's a cleaner root-cause fix.

Also added: dynamic icon scaling for multi-digit text. Multi-digit life totals (e.g. 120) or high radiation counters still needed to coexist with the icon in the same cell. I added getEffectiveIconScaleFactor() as a protected hook in FLabel that subclasses can override, and PlayerDetailsPanel.DetailLabel reduces the icon scale by ~15% per character beyond the first. Single-digit values keep the original icon size; larger numbers get progressive room without any cell resizing.

Let me know if the approach looks good.

@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented Apr 15, 2026

Just tested and doesn't seem to have that behavior?

Details panel continues to shrink into nothing rather than holding at fixed minimum.

Screenshot 2026-04-15 182530

DragCell now exposes a per-cell minW (default SResizingUtil.W_MIN);
SResizingUtil.resizeX consults t.getMinW() instead of the global
constant so user drag is locked at each cell's own minimum.

VField.populate sets parentCell.setMinW(240) and uses fixed pixel
widths for avatar (90), phase indicator (22) and details panel (90),
letting the scroller absorb extra horizontal space via growx. The
phase indicator strip is wide enough to render its icons cleanly.

PlayerDetailsPanel.DetailLabel scales the icon down progressively
when text grows past one digit (0.55x / 0.40x / 0.30x) so multi-digit
counters share the cell with the icon without truncation.

setSmoothW intentionally does NOT clamp to minW: clamping there breaks
SRearrangingUtil.fillGap, which relies on exact tiled coordinates and
throws "Gap was not filled" when starting a match with stale dynamic
cells. The drag-time check is enough for the user-facing case.
@RafaelHGOliveira
Copy link
Copy Markdown
Contributor Author

RafaelHGOliveira commented Apr 15, 2026

@MostCromulent you're right — didn't test on a window that narrow, sorry. The 60px:10%:10% MigLayout constraint silently breaks when 10% drops below 60px (max < min), so my fix did nothing in your case.

Pushed a different approach: per-cell minW on DragCell (default SResizingUtil.W_MIN), and SResizingUtil.resizeX consults t.getMinW() instead of the global. VField.populate sets parentCell.setMinW(240) and uses fixed pixel widths for avatar/phase/details, scroller absorbs the rest via growx. As an accidental bonus I ended up liking the fixed-width look better.

Caveat: I do not clamp inside setSmoothW/setBounds — tried it first and it breaks SRearrangingUtil.fillGap (Gap was not filled on match start with stale dynamic cells). So minW is only enforced on user drag; window-resize can still go below it.

Also tuned the icon-scaling: 2 digits → 0.55x, 3 → 0.40x, 4+ → 0.30x. The previous pow(0.85, n-1) was way too gentle.

narrow window screenshot

@MostCromulent
Copy link
Copy Markdown
Contributor

Tested again - seems to work fine and I think the fixed width panel is a solid improvement. 👍

Will leave code review to the pros from here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants