GUI¶
The Frame¶
GuiPage (src/main/java/dev/universaladmin/gui/GuiPage.java)
is still the only interface a page is opened through:
public interface GuiPage {
GuiPageId id();
void open(Player viewer);
}
GuiRegistry manages registered pages under their GuiPageId. Only the
GUI framework's own classes (GuiView, GuiListener, AbstractGuiPage)
work directly with Bukkit inventory APIs/click events today - a feature
page no longer implements GuiPage by hand, it extends
AbstractGuiPage/AbstractListGuiPage and gets navigation, permission
filtering, pagination, and async loading for free. The complete framework
(building blocks, an example, design decisions like choosing the Paper
dialog API for text input) is in
docs/development/gui-framework.md.
Every page has the same frame - the header in the module's colour with back, main menu, the page's icon, refresh and close, the tools and paging at the bottom - and glass colours with one meaning on every page; a page switch redraws the open window instead of opening a new one. The main menu and a player's profile are hubs: grouped rows behind coloured label panes. See ADR 0027.
The One Rule¶
A click handler in a GuiPage implementation calls a service or an
Action - it contains no logic itself. Concretely that means: no direct
database access, no computation, no authorization check with its own logic
(that belongs in the service/action, not duplicated in the GUI). Violating
this is the most common way an admin plugin turns into an unmaintainable
pile of click handlers - see the
development rules.
Dependency Injection Instead of a Global Context¶
A page gets the services/actions it needs through its constructor - not by
accessing UniversalAdmin at open time. That keeps a page testable (the
services can be faked) and makes a page's actual dependencies explicit
instead of hiding them behind a "has access to everything" object.
GuiFramework (sessions/icons, see gui-framework.md) is the one accepted
exception - a narrow, GUI-framework-scoped bundle, not access to the whole
platform.
public final class PlayerListPage extends AbstractListGuiPage<PlayerProfile> {
private final PlayerService playerService; // not: UniversalAdmin platform
public PlayerListPage(
GuiFramework framework, MessageService messages, TaskScheduler scheduler, PlayerService playerService) {
super(GuiPageId.core("players.home"), framework, messages, scheduler);
this.playerService = playerService;
}
// ...
}
A complete, runnable example (including rendering, navigation, registration in the module) is in docs/development/gui-framework.md.
Current State¶
The framework exists (its own, minimal menu system - no external inventory
GUI library, see
gui-framework.md) and is used by the
main menu (MainMenuPage), a placeholder page per not-yet-built-out
module, and the players feature GUI (player browser, profile, actions,
editable inventory/ender chest - see
docs/user/modules/players.md). The remaining
seven modules are ROADMAP.md Phase 1/2.
Relationship to the Web App¶
A GuiPage and a future web page for the same feature (see
web-future.md) ideally call the same service/action and
only differ in presentation. That's the reason GUI click handlers may not
contain logic: any logic in the GUI is logic the web app can't reuse.