import { extensions, ExtensionType } from '../extensions/Extensions';
import { autoDetectRenderer } from '../rendering/renderers/autoDetectRenderer';
import { Container } from '../scene/container/Container';
import { ApplicationInitHook } from '../utils/global/globalHooks';
import { deprecation, v8_0_0 } from '../utils/logging/deprecation';
import type { Rectangle } from '../maths/shapes/Rectangle';
import type { AutoDetectOptions } from '../rendering/renderers/autoDetectRenderer';
import type { RendererDestroyOptions } from '../rendering/renderers/shared/system/AbstractRenderer';
import type { Renderer } from '../rendering/renderers/types';
import type { DestroyOptions } from '../scene/container/destroyTypes';
/**
* The app module provides a set of classes to use as a starting point when building applications.
*
* <aside>This module has a mixin for <code>TickerPlugin</code> and <code>ResizePlugin</code>.
* These will need to be imported if you are managing your own renderer.</aside>
*
* ```js
* import { Application } from 'pixi.js';
*
* const app = new Application();
*
* await app.init();
*
* // don't forget to add the canvas to the DOM
* document.body.appendChild(app.canvas);
* ```
* @namespace app
*/
/**
* Any plugin that's usable for Application should contain these methods.
* @example
* import { ApplicationPlugin } from 'pixi.js';
*
* const plugin: ApplicationPlugin = {
* init: (options: Partial<ApplicationOptions>) =>
* {
* // handle init here, use app options if needed
* },
* destroy: () =>
* {
* // handle destruction code here
* }
* }
* @memberof app
* @see ApplicationOptions
* @ignore
*/
export interface ApplicationPlugin
{
/**
* Called when Application is constructed, scoped to Application instance.
* Passes in `options` as the only argument, which are Application `init()` options.
* @param {object} options - Application options.
*/
init(options: Partial<ApplicationOptions>): void;
/** Called when destroying Application, scoped to Application instance. */
destroy(): void;
}
/**
* Application options supplied to the init method.
* @memberof app
* @example
* import { Application } from 'pixi.js';
*
* const app = new Application();
*
* await app.init({
* autoStart: false,
* resizeTo: window,
* sharedTicker: true,
* });
*/
export interface ApplicationOptions extends AutoDetectOptions, PixiMixins.ApplicationOptions { }
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface Application extends PixiMixins.Application { }
/**
* Convenience class to create a new PixiJS application.
*
* This class automatically creates the renderer, ticker and root container.
* @example
* import { Application, Sprite } from 'pixi.js';
*
* // Create the application
* const app = new Application();
*
* await app.init({ width: 800, height: 600 });
*
* // Add the view to the DOM
* document.body.appendChild(app.canvas);
*
* // ex, add display objects
* app.stage.addChild(Sprite.from('something.png'));
* @memberof app
*/
export class Application<R extends Renderer = Renderer>
{
/**
* Collection of installed plugins.
* @alias _plugins
*/
public static _plugins: ApplicationPlugin[] = [];
/** The root display container that's rendered. */
public stage: Container = new Container();
/**
* WebGL renderer if available, otherwise CanvasRenderer.
* @member {rendering.Renderer}
*/
public renderer: R;
/** Create new Application instance */
constructor();
/** @deprecated since 8.0.0 */
constructor(options?: Partial<ApplicationOptions>);
/** @ignore */
constructor(...args: [Partial<ApplicationOptions>] | [])
{
// #if _DEBUG
if (args[0] !== undefined)
{
deprecation(v8_0_0, 'Application constructor options are deprecated, please use Application.init() instead.');
}
// #endif
}
/**
* @param options - The optional application and renderer parameters.
*/
public async init(options?: Partial<ApplicationOptions>)
{
// The default options
options = { ...options };
this.renderer = await autoDetectRenderer(options as ApplicationOptions) as R;
// install plugins here
Application._plugins.forEach((plugin) =>
{
plugin.init.call(this, options);
});
}
/** Render the current stage. */
public render(): void
{
this.renderer.render({ container: this.stage });
}
/**
* Reference to the renderer's canvas element.
* @readonly
* @member {HTMLCanvasElement}
*/
get canvas(): R['canvas']
{
return this.renderer.canvas as R['canvas'];
}
/**
* Reference to the renderer's canvas element.
* @member {HTMLCanvasElement}
* @deprecated since 8.0.0
*/
get view(): R['canvas']
{
// #if _DEBUG
deprecation(v8_0_0, 'Application.view is deprecated, please use Application.canvas instead.');
// #endif
return this.renderer.canvas as R['canvas'];
}
/**
* Reference to the renderer's screen rectangle. Its safe to use as `filterArea` or `hitArea` for the whole screen.
* @readonly
*/
get screen(): Rectangle
{
return this.renderer.screen;
}
/**
* Destroys the application and all of its resources.
* @param {object|boolean}[rendererDestroyOptions=false] - The options for destroying the renderer.
* @param {boolean}[rendererDestroyOptions.removeView=false] - Removes the Canvas element from the DOM.
* @param {object|boolean} [options=false] - The options for destroying the stage.
* @param {boolean} [options.children=false] - If set to true, all the children will have their destroy method
* called as well. `options` will be passed on to those calls.
* @param {boolean} [options.texture=false] - Only used for children with textures e.g. Sprites.
* If options.children is set to true,
* it should destroy the texture of the child sprite.
* @param {boolean} [options.textureSource=false] - Only used for children with textures e.g. Sprites.
* If options.children is set to true,
* it should destroy the texture source of the child sprite.
* @param {boolean} [options.context=false] - Only used for children with graphicsContexts e.g. Graphics.
* If options.children is set to true,
* it should destroy the context of the child graphics.
*/
public destroy(rendererDestroyOptions: RendererDestroyOptions = false, options: DestroyOptions = false): void
{
// Destroy plugins in the opposite order
// which they were constructed
const plugins = Application._plugins.slice(0);
plugins.reverse();
plugins.forEach((plugin) =>
{
plugin.destroy.call(this);
});
this.stage.destroy(options);
this.stage = null;
this.renderer.destroy(rendererDestroyOptions);
this.renderer = null;
}
}
extensions.handleByList(ExtensionType.Application, Application._plugins);
extensions.add(ApplicationInitHook);