Skip to content

Biggest UI refresh since Oriath's End - #10290

Open
canuysal wants to merge 5 commits into
PathOfBuildingCommunity:devfrom
canuysal:big_ui_refresh
Open

Biggest UI refresh since Oriath's End#10290
canuysal wants to merge 5 commits into
PathOfBuildingCommunity:devfrom
canuysal:big_ui_refresh

Conversation

@canuysal

Copy link
Copy Markdown
Contributor

Fixes # .

Excuse the PR title, just wanted to hype a bit.

Description of the problem being solved:

PoB currently has a look reminiscent of a 90's advanced calculator. That reflects the soul of
the app, but it also adds to the complexity. This PR tries to solve that by refreshing the UI
components with modern looking variants, and by doing so also makes the app more appealing to
newer players.

UI is subjective, which is why I went with ShadCN style components, the style ~90% of the web
uses nowadays, so it's something we're all already used to. One thing I'm not sure about is
the contrast, so let me know if anything is hard to read.

The important part is that this is not a pile of one-off tweaks. Every base control used to
hardcode its own draw colours, repeating the same 1px light grey outline over a black fill in
about forty places. This adds src/Modules/UI.lua, a single module holding the colour tokens
plus rounded rectangle helpers, and points the controls at it. All the visual tuning now lives
in one token table
, so if the contrast or the rounding needs adjusting, it's a one-file change
rather than a hunt through every control.

Two small assets come with it (908 bytes total). SimpleGraphic can only draw axis aligned quads,
so ui_round.png is an antialiased disc whose four quadrants act as rounded corners, and
ui_check.png is the check mark. Untextured geometry gets no antialiasing, which was very
visible on a diagonal shape once the tick sat on a filled box.

What changed, control by control:

  • Buttons, drop downs, edits, check boxes: rounded corners, subtle borders, hover and
    pressed states that lift the fill instead of flashing a white outline. A ticked check box is
    now a filled box with a dark tick, like ShadCN's.
  • Scroll bars: rebuilt. The stepper arrow buttons are gone, so the knob runs the full length
    of the bar. It's a thin rail at rest, fattens while you're using it, and hides entirely when
    there's nothing to scroll. Clicking the track pages toward the cursor.
  • Side bar stat list: label and value are pinned to opposite edges instead of meeting in the
    middle, each run of stats between spacers is grouped into a card with hairlines between its
    rows, and rows have breathing room. Long labels are clipped so they can't run underneath their
    own value (they used to clip off the left edge of the panel instead). No stats were added,
    removed or reordered. Only how the existing list is drawn changed.
  • Main skill section: it was built from 18px controls with 2px gaps, and a drop down draws
    its text at height minus four, so that meant 14px text sitting between the 16px label above
    and the 16px stat rows below. Everything in the section is now 20px, and drop down / edit text
    is inset from the left border rather than jammed against it.
  • Top bar: the point display and build name are read-only, so they're framed like disabled
    buttons to match the rest of the bar. The 4px near-white rule under the top bar is now a
    hairline, and the vertical tick at the centre is gone. It marked where the bar's two anchor
    groups meet, which isn't information a user needs.
  • Sections, popups, lists: same rounding and border treatment, filled selection rows instead
    of white-outlined ones.

Worth noting on contrast: the first pass lifted control interiors to around 10% grey and it
flattened the game's own text palette, since item rarity colours and the dimmer greys are all
mixed for contrast against black. Anything holding text now sits near black again and leans on
its border for definition. That's the part I'd most like a second opinion on.

Steps taken to verify a working solution:

  • Ran the app and went through every tab, checking the controls in each: Tree, Skills, Items,
    Calcs, Configuration, Notes, Import/Export, Party, Compare.
  • Checked every base control in each state: hover, pressed, disabled, focused, and drop downs in
    both dropped directions (up and down), with and without a scroll bar.
  • Verified the side bar stat list against the previous build: same stats, same order, same
    values, including minion stats and the multi-line skill DPS entries.
  • Confirmed scroll bars still work for both axes: knob drag, click-to-page, hold to repeat,
    mouse wheel, and the auto-hide when content fits.
  • luajit HeadlessWrapper.lua loads clean.

Link to a build that showcases this PR:

Before screenshot:

image image image

After screenshot:

image image image image image image image image image image image

Buttons, drop downs, edits, check boxes, sliders, scroll bars, lists,
sections and popups each hardcoded their own draw colours, repeating the
same 1px light grey outline over a black fill throughout.

Add Modules/UI, which holds the colour tokens they now share along with
rounded rectangle primitives. SimpleGraphic can only draw axis aligned
quads, so rounding uses Assets/ui_round.png, an antialiased disc whose
four quadrants serve as corners.
The first pass lifted every control body to around ten percent grey,
which flattened the game's own text palette; item rarity colours and the
dimmer notes are mixed for contrast against black. Anything holding text
now sits near black again and leans on its border for definition, with
the lift spent on the hover and pressed states instead.

Draw the check mark from a supersampled sprite as well. Untextured
geometry has no antialiasing, which barely showed on the old low
contrast tick but was obvious once the box became a filled shape.

Replace the scroll bar's stepper buttons with a full length track and a
pill knob that fattens while the bar is in use, and hide the bar
entirely when there is nothing to scroll.

Frame the point display and the build name like disabled buttons, since
both are read-only, and close the top bar off with a hairline rather
than a four pixel rule. Drop the vertical tick at the centre of the top
bar, which marked where its two anchor groups meet and meant nothing to
the reader.
Drop down and edit text was drawn hard against the border at two pixels
in. Inset it, and grow the dropped panel and auto sized box widths by
the same amount so long labels do not clip by exactly what was added.
The edit control works out its text origin separately in the draw path
and in the click to place caret path, so both move together; changing
only the former would leave the caret landing about a character off.

The main skill section was built from eighteen pixel controls with two
pixel gaps, and one sixteen pixel outlier. A drop down draws its text at
its height less four, so that meant fourteen pixel text against the
sixteen pixel label above it and the sixteen pixel stat rows below.
Every control in the section is now twenty pixels with wider gaps, which
puts its text at the same size as its surroundings. The stat box takes
its height from the screen, so it absorbs the difference.
Not everyone wants rounded corners, so ui.rounded lets the whole refresh
be viewed with sharp edges without touching anything else: same colours,
same spacing, same borders, only the corners change.

It gates the drawing primitives rather than the radius tokens, because
the slider knob, the scroll bar thumb and the list drag marker work out
their own radius instead of reading a token, and zeroing the tokens
would leave those three still round.

Defaults to rounded, and is marked to come out before this reaches
upstream.
Review raised the risk of building tables for RGB values on the draw
path. The token table itself is built once at load, but three controls
were creating a fresh {r, g, b} every frame: the check box, drop down
and edit each wrapped whatever their borderFunc returned. Those run for
every such control on every frame, and the config tab alone has a lot of
them, so the collector had a steady trickle of short lived tables to
trace.

Store each token as a packed 0xRRGGBB number and unpack it in SetColor,
and add PackColor for colours that only exist at draw time. Numbers are
values in Lua, so nothing on the draw path allocates now, and a token
costs one hash lookup rather than a lookup plus three array reads.

Colours are unchanged to within half a step out of 255, which is not
visible.
@canuysal

Copy link
Copy Markdown
Contributor Author

Re: the GC pressure concern raised on Discord, the colour tokens are packed 0xRRGGBB numbers
rather than {r, g, b} tables, and nothing on the draw path allocates. The token table was
already built once at load, but three controls really were creating a fresh table every frame
to wrap what their borderFunc returned; that's gone.

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.

1 participant