asciitorium
Version:
an ASCII CLUI framework
121 lines (120 loc) • 4.19 kB
JavaScript
import { Component } from '../core/Component.js';
import { State } from '../core/State.js';
export class MobileController extends Component {
constructor(props) {
super({
...props,
width: 0, // Non-visual component
height: 0,
border: false,
visible: new State(false), // Explicitly invisible - not a visual component
});
this.registrationAttempted = false;
this.destroyed = false;
this.priority = props.priority ?? 0;
// Handle enabled/disabled state (reactive or static)
if (props.enabled instanceof State) {
// Store the State directly, we'll invert in the disabled getter
this.disabledState = props.enabled;
this.staticDisabled = false;
}
else {
// Static enabled: invert to disabled
this.staticDisabled = !(props.enabled ?? true); // Default enabled = not disabled
}
// Build action map from props
this.actionMap = new Map();
if (props.dpad) {
if (props.dpad.up)
this.actionMap.set('btn-up', props.dpad.up);
if (props.dpad.down)
this.actionMap.set('btn-down', props.dpad.down);
if (props.dpad.left)
this.actionMap.set('btn-left', props.dpad.left);
if (props.dpad.right)
this.actionMap.set('btn-right', props.dpad.right);
}
if (props.buttons) {
if (props.buttons.a)
this.actionMap.set('btn-a', props.buttons.a);
if (props.buttons.b)
this.actionMap.set('btn-b', props.buttons.b);
if (props.buttons.x)
this.actionMap.set('btn-x', props.buttons.x);
if (props.buttons.y)
this.actionMap.set('btn-y', props.buttons.y);
}
if (props.menu) {
this.actionMap.set('btn-menu', props.menu);
}
}
get disabled() {
// If using reactive State, invert enabled to disabled
if (this.disabledState) {
return !this.disabledState.value; // enabled State -> invert to disabled
}
return this.staticDisabled;
}
/**
* Handle a button press
* @returns true if this controller handled the button, false otherwise
*/
handleButton(buttonId) {
const action = this.actionMap.get(buttonId);
if (action) {
action();
return true;
}
return false;
}
// Override setParent to register with App when added
setParent(parent) {
super.setParent(parent);
this.registerWithApp();
}
registerWithApp() {
if (this.registrationAttempted || this.destroyed) {
return; // Prevent infinite loops and registration after destruction
}
// Walk up parent chain to find App
let current = this.parent;
while (current && !current.isApp) {
current = current.parent;
}
if (current && current.registerMobileController) {
current.registerMobileController(this);
this.registrationAttempted = true;
}
else {
// Try again after a short delay in case the parent tree isn't fully built yet
this.timeoutId = setTimeout(() => {
if (!this.destroyed) {
this.registerWithApp();
}
}, 0);
}
}
// Called when component is removed
destroy() {
this.destroyed = true;
// Clear any pending registration timeout
if (this.timeoutId !== undefined) {
clearTimeout(this.timeoutId);
this.timeoutId = undefined;
}
this.unregisterWithApp();
super.destroy();
}
unregisterWithApp() {
let current = this.parent;
while (current && !current.isApp) {
current = current.parent;
}
if (current && current.unregisterMobileController) {
current.unregisterMobileController(this);
}
}
draw() {
return []; // Never renders
}
}