// Copyright (c) 2026 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT var Menu = require('./Menu.js'); var Button = require('./Button.js'); // A Yes/No dialog: dim full-screen backdrop + a flat-color panel (see // Fullbox.js/plan notes on why there's no real 9-slice Frame yet) + // text + a 2-item Menu. Confirm.open(text, callback) lazily creates and // reuses ONE shared instance rather than allocating fresh every time -- // the Menu stack already guarantees at most one confirm is ever // meaningfully active, so there's nothing to gain from a fresh instance // per call, and it sidesteps ever needing to dispose one (see Fullbox.js // for why disposing from inside your own callback chain is risky). class Confirm extends UIElement { init() { this.backdrop = new Rectangle(); this.backdrop.color = Confirm.COLOR_BACKDROP; this.add(this.backdrop); this.panel = new Rectangle(); this.panel.width = Confirm.PANEL_WIDTH; this.panel.height = Confirm.PANEL_HEIGHT; this.panel.color = Confirm.COLOR_PANEL; this.add(this.panel); this.label = new Label(); this.add(this.label); this.menu = new Menu(); this.menu.cols = 2; this.add(this.menu); this.confirmButton = new Button(); this.confirmButton.text = 'Confirm'; this.menu.addItem(this.confirmButton); this.cancelButton = new Button(); this.cancelButton.text = 'Cancel'; this.menu.addItem(this.cancelButton); this.menu.onSelected = (index) => this._finish(index === 0); // CANCEL (the bind, not the button) also closes the menu without // firing onSelected -- treat that the same as picking Cancel. this.menu.onClosed = () => this._finish(false); this._callback = null; this._finishing = false; } open(text, callback) { this.label.text = text; this._callback = callback; this._finishing = false; this._layout(); UI.add(this); this.menu.open(); } _finish(result) { // menu.onClosed calling back into _finish (via close() below) would // otherwise recurse into itself. if(this._finishing) return; this._finishing = true; this.menu.close(); UI.remove(this); const callback = this._callback; this._callback = null; if(callback) callback(result); } _layout() { this.backdrop.width = Screen.width; this.backdrop.height = Screen.height; const panelX = (Screen.width - Confirm.PANEL_WIDTH) / 2; const panelY = (Screen.height - Confirm.PANEL_HEIGHT) / 2; this.panel.x = panelX; this.panel.y = panelY; this.label.x = panelX + Confirm.PADDING; this.label.y = panelY + Confirm.PADDING; this.confirmButton.x = panelX + Confirm.PADDING; this.confirmButton.y = panelY + Confirm.PANEL_HEIGHT - Confirm.PADDING - 32; this.cancelButton.x = this.confirmButton.x + 130; this.cancelButton.y = this.confirmButton.y; } } Confirm.PANEL_WIDTH = 300; Confirm.PANEL_HEIGHT = 120; Confirm.PADDING = 16; Confirm.COLOR_BACKDROP = { r: 0, g: 0, b: 0, a: 160 }; Confirm.COLOR_PANEL = { r: 40, g: 40, b: 40, a: 255 }; Confirm.open = function(text, callback) { if(!Confirm._shared) Confirm._shared = new Confirm(); Confirm._shared.open(text, callback); return Confirm._shared; }; module.exports = Confirm;