//////////////////////////////////////// // Programming project code // UniBw M, 2022, 2023, 2024 // www.unibw.de/inf2 // (c) Mark Minas (mark.minas@unibw.de) //////////////////////////////////////// package pp.dialog; import com.jme3.app.SimpleApplication; import com.jme3.math.Vector3f; import com.jme3.scene.Spatial; import com.jme3.system.AppSettings; import com.simsilica.lemur.Panel; import com.simsilica.lemur.focus.FocusManagerState; import java.util.ArrayDeque; import java.util.Deque; import static java.lang.Math.max; /** * Manages dialog boxes within the application, handling their display, positioning, and focus. */ public class DialogManager { /** * The application instance. */ private final SimpleApplication app; /** * A stack to keep track of the dialogs. */ private final Deque dialogStack = new ArrayDeque<>(); /** * Constructs a DialogManager for the specified application. * * @param app the SimpleApplication instance */ public DialogManager(SimpleApplication app) { this.app = app; } /** * Checks if any dialog is currently displayed. * * @return true if any dialog is currently shown, false otherwise */ public boolean showsDialog() { return !dialogStack.isEmpty(); } /** * Retrieves the stack of dialogs. * * @return the dialog stack */ public Deque getDialogStack() { return dialogStack; } /** * Calculates the depth for the next dialog to be displayed. * * @return the next depth value */ public int nextDepth() { return dialogStack.isEmpty() ? 10 : dialogStack.peek().depth + 10; } /** * Positions the specified panel in the center of the screen, with a specified z coordinate. * * @param panel the panel to center * @param z the z coordinate */ public void centering(Panel panel, float z) { final Vector3f size = panel.getPreferredSize(); centering(panel, size.getX(), size.getY(), z); } /** * Positions the specified spatial in the center of the screen, with specified width, height, and z coordinate. * * @param spatial the spatial to center * @param width the width reserved for the spatial * @param height the height reserved for the spatial * @param z the z coordinate */ public void centering(Spatial spatial, float width, float height, float z) { final AppSettings settings = app.getContext().getSettings(); spatial.setLocalTranslation(max(0f, 0.5f * (settings.getWidth() - width)), max(0f, 0.5f * (settings.getHeight() + height)), z); } /** * Arranges for the specified spatial to receive the focus. * * @param spatial the spatial to focus */ public void setFocus(Spatial spatial) { final var focusManager = app.getStateManager().getState(FocusManagerState.class); if (focusManager != null) focusManager.setFocus(spatial); } /** * Opens the specified dialog and adds it to the dialog stack. * * @param dialog the dialog to open */ public void open(Dialog dialog) { dialogStack.push(dialog); dialog.update(); app.getGuiNode().attachChild(dialog); } /** * Checks if the specified dialog is the topmost dialog in the dialog stack. * * @param dialog a dialog. * @return true if the dialog is the top dialog, false otherwise. */ boolean isTop(Dialog dialog) { return !dialogStack.isEmpty() && dialogStack.peek() == dialog; } /** * Closes the specified dialog, removing it from the dialog stack. * * @param dialog the dialog to close * @throws IllegalArgumentException if the specified dialog is not the top dialog */ public void close(Dialog dialog) { if (!isTop(dialog)) throw new IllegalArgumentException(dialog + " is not the top dialog"); dialogStack.pop(); if (!dialogStack.isEmpty()) dialogStack.peek().update(); app.getGuiNode().detachChild(dialog); } /** * Calls the escape action of the top dialog, if a dialog is shown. */ public void escape() { if (dialogStack.isEmpty()) return; dialogStack.peek().escape(); } public void update(float delta) { for (Dialog dialog : dialogStack) dialog.update(delta); } }