Skip to content

HUD system - #150

Open
EternalQ wants to merge 6 commits into
GTNewHorizons:masterfrom
cubeofint:master
Open

HUD system#150
EternalQ wants to merge 6 commits into
GTNewHorizons:masterfrom
cubeofint:master

Conversation

@EternalQ

@EternalQ EternalQ commented Jul 16, 2026

Copy link
Copy Markdown

HUD system summary

com.cleanroommc.modularui.hud renders ModularUI panels directly over the game world (or over/under
an open GuiScreen), reusing the normal widget-tree/theme/layout pipeline. A HUD element is
display-only: no mouse/keyboard input, no hover/focus/drag - only draw() sees an updated mouse
position.

How it works

A HudElement owns a private ModularScreen built from a HudContext (no-op onFrameUpdate(), so
no hover/focus/drag logic runs) and a throwaway HudWrapper GuiScreen used only to satisfy
ModularScreen#constructOverlay's contract - neither is public API.

HudManager (started once via HudManager.init(), already wired in ClientProxy) hooks:

  • RenderGameOverlayEvent.Post - draws while no GuiScreen is open (visibleInWorld).
  • GuiScreenEvent.DrawScreenEvent.Pre/.Post - draws under/over an open GuiScreen
    (visibleInGui(visible, isOverGui)).
  • ClientTickEvent - ticks registered elements.
HudElement hud = new HudElement("mymod", ctx -> {
    ModularPanel panel = new ModularPanel("status").size(100, 20).pos(5, 5);
    panel.child(new TextWidget().text(IKey.dynamic(() -> "Current time: " + System.currentTimeMillis())));
    return panel;
});
HudManager.register(hud);
// HudManager.unregister(hud) / .clear() to stop drawing+ticking, e.g. on log-out

enabled(boolean) toggles an element entirely (skips ticking too); renderPriority(int) orders
overlapping elements (higher draws on top).

Changes (including fixes for my chat mod)

  • HUD system added (HudElement, HudManager, HudContext, HudScreen, HudWrapper).
  • Text field scroll/cursor fix: the cursor and just-typed text were clipped out of view once
    typing past a field's visible width. Fixed by measuring scroll in the same coordinate space as the
    cursor (plus a scrollEdgeMargin) and widening the cursor quad so it still rasterizes at a
    scrolled/clipped edge.
  • Platform.setupDrawFont() now enables GL_BLEND: previously FontRenderer wrote fully opaque
    pixels regardless of a color's alpha channel, so fade-out/ghost-text effects had no visible effect.
  • InternalWidgetTree.drawTreeForeground now skips disabled widgets: brings it in line with
    drawTree/IWidget#drawForeground's own documented contract - a disabled widget's foreground/tooltip
    should not render, but it still did.
  • Lighting/darkening fix: having a HUD element visible under/over an open GuiScreen (the default)
    made the whole game - world and every GUI - render visibly darker, permanently, until restart. Cause:
    RenderHelper.enableStandardItemLighting() bakes GL_LIGHT0/GL_LIGHT1 positions relative to
    whatever matrix is active when it's called; called from a HUD overlay's own transform stack (as
    ModularScreen.drawScreen()/drawForeground() always did, for every screen), those positions came
    out wrong, corrupting lighting for everything drawn afterward. Fix: both methods now skip
    RenderHelper.enableStandardItemLighting()/GlStateManager.enableLighting() entirely when
    isOverlay() is true (HUD screens never render lit 3D items, so nothing is lost). Do not remove this
    guard or call those methods from HUD-drawn code.

Checklist

  • I have tested this PR in DevEnv
  • I have tested this PR in Fullpack - woks fine in 2.9-beta2
  • This PR is in compliance with the GTNH AI Policy - I'm not sure. A lot of the code was indeed generated (especially the comments and documentation, which I'm too lazy to write myself), but I supervised and guided every step.
  • This PR requires another PR in order to merge

@FourIsTheNumber FourIsTheNumber added the AI This PR was made with heavy AI assistance and needs more thorough review. label Jul 16, 2026
@EternalQ

Copy link
Copy Markdown
Author

* @param panelCreator function which creates the main panel of this screen
* @param contextFactory factory that creates the context for this screen
*/
protected ModularScreen(@NotNull String owner, @NotNull Function<ModularGuiContext, ModularPanel> panelCreator,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Add the param to the existing private ctor and add overloads for the public ctors which just call this(...)

}

boolean resized = scaledWidth != lastWidth || scaledHeight != lastHeight;
if (resized) { lastWidth = scaledWidth; lastHeight = scaledHeight; }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Dont inline code blocks

* Defaults to matching the field's own default horizontal padding (see BaseTextFieldWidget's
* constructor), since that's already the amount of breathing room the field visually reserves.
*/
private int scrollEdgeMargin = 4;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is this needed? Why is widget padding not enough?

@brachy84

Copy link
Copy Markdown
Collaborator

Also either write SHORT code comments yourself or dont write them at all. NOBODY is reading those books.

@EternalQ
EternalQ requested a review from brachy84 July 19, 2026 20:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI This PR was made with heavy AI assistance and needs more thorough review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants