UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

267 lines (266 loc) 14.6 kB
/** This file must only contain pure code and pure imports */ /** * Physical input source that generated an interaction. */ export type InputSource = "pointer" | "wheel" | "touch" | "keyboard"; /** * Modifier key state, shared across input sources that support modifiers. */ export type InputModifiers = { /** Ctrl key pressed */ ctrl?: boolean; /** Shift key pressed */ shift?: boolean; /** Alt key pressed */ alt?: boolean; }; /** * Conditions for pointer inputs. */ export type PointerConditions = { /** Mouse button (0=left, 1=middle, 2=right). Omit to match any button. */ button?: number; /** Modifier key state. Only specified keys are checked; omitted = don't-care. */ modifiers?: InputModifiers; }; /** * Conditions for mouse wheel inputs. */ export type WheelConditions = { /** Modifier key state. Only specified keys are checked; omitted = don't-care. */ modifiers?: InputModifiers; }; /** * Conditions for touch inputs. */ export type TouchConditions = { /** Number of active touch points. Omit to match any count. */ touchCount?: number; }; /** * Conditions for keyboard inputs. */ export type KeyboardConditions = { /** Key code of the current key being resolved. Omit to match any key. */ key?: number; /** Modifier key state. Only specified keys are checked; omitted = don't-care. */ modifiers?: InputModifiers; }; /** * Mapping rule for pointer (mouse button) inputs. */ export type PointerInputMapEntry<TInteraction extends string = string> = { source: "pointer"; interaction: TInteraction; /** Multiplier applied to input deltas before passing to the handler. Default is 1. */ sensitivity?: number; /** Optional per-axis override for the X (horizontal / yaw) component. Falls back to `sensitivity` if unset. */ sensitivityX?: number; /** Optional per-axis override for the Y (vertical / pitch) component. Falls back to `sensitivity` if unset. */ sensitivityY?: number; } & PointerConditions; /** * Mapping rule for mouse wheel inputs. */ export type WheelInputMapEntry<TInteraction extends string = string> = { source: "wheel"; interaction: TInteraction; /** Multiplier applied to input deltas before passing to the handler. Default is 1. */ sensitivity?: number; } & WheelConditions; /** * Mapping rule for touch inputs. */ export type TouchInputMapEntry<TInteraction extends string = string> = { source: "touch"; interaction: TInteraction; /** Multiplier applied to input deltas before passing to the handler. Default is 1. */ sensitivity?: number; /** Optional per-axis override for the X component. Falls back to `sensitivity` if unset. */ sensitivityX?: number; /** Optional per-axis override for the Y component. Falls back to `sensitivity` if unset. */ sensitivityY?: number; } & TouchConditions; /** * Mapping rule for keyboard inputs. * The `key` field on the entry supports a single key code or an array of key codes for matching. * When resolving, the condition's `key` is checked against the entry's `key` value(s). */ export type KeyboardInputMapEntry<TInteraction extends string = string> = { /** Discriminator: keyboard input source */ source: "keyboard"; /** Interaction type to dispatch when this entry matches */ interaction: TInteraction; /** Multiplier applied to input deltas before passing to the handler. Default is 1. */ sensitivity?: number; /** Key code filter(s). Supports a single code or an array. Omit to match any key. */ key?: number | number[]; /** Modifier keys that must be active for this entry to match. Omit to match regardless of modifiers. */ modifiers?: InputModifiers; }; /** * A single mapping rule: source + optional conditions → interaction type. * The inputMap is an ordered array on the movement class; first matching entry wins. * The interaction string should match a handler property name on the camera's movement subclass. * * Discriminated union by `source` — only fields relevant to that source are available. */ export type InputMapEntry<TInteraction extends string = string> = PointerInputMapEntry<TInteraction> | WheelInputMapEntry<TInteraction> | TouchInputMapEntry<TInteraction> | KeyboardInputMapEntry<TInteraction>; /** * Flat conditions object passed to resolveInteraction(). * Only the fields relevant to the source type need to be set. * Per-source condition types (PointerConditions, KeyboardConditions, etc.) are subtypes * of this and should be used at call sites for clarity. */ export type InputConditions = { /** Mouse button (0=left, 1=middle, 2=right) */ button?: number; /** Current modifier key state */ modifiers?: InputModifiers; /** Number of active touch points */ touchCount?: number; /** Key code of the current key being resolved */ key?: number; }; /** * Extracts the string-typed interaction names from a handlers object type. * Equivalent to `keyof THandlers & string` — filters out symbol/number keys. */ export type InteractionName<THandlers> = keyof THandlers & string; /** * Generic input-to-interaction mapper that resolves physical input events to semantic interaction types * and dispatches them to typed handlers. * * `InputMapper` is not tied to cameras — any object that needs a configurable, prioritized * mapping from physical inputs (pointer, keyboard, wheel, touch) to named interactions can use it. * * The mapper holds an ordered `inputMap` array. When `resolveInteraction` is called, the first * entry whose source and conditions match the current input wins. More specific entries (with more * conditions like button, key, modifiers) should be placed before less specific ones; use `addEntry` * to auto-insert based on specificity. * * @typeParam THandlers - Object type whose keys are the valid interaction type strings and values * are the handler functions/objects for each interaction (e.g. `ArcRotateHandlers`). * Interaction types are derived as `InteractionName<THandlers>`. */ export declare class InputMapper<THandlers extends Record<string, unknown>> { /** * Ordered list of input-to-interaction mapping rules. First matching entry wins. */ inputMap: InputMapEntry<InteractionName<THandlers>>[]; /** * Interaction handlers keyed by interaction type. * Override individual handlers to customize behavior without changing input mapping. */ readonly handlers: THandlers; /** * Creates a new InputMapper. * @param handlers - The interaction handlers, keyed by interaction type. * @param createDefaultEntries - Optional factory that returns the default inputMap entries. * Called by `resetInputMap()` and during construction. When omitted, the default map is empty. */ constructor(handlers: THandlers, createDefaultEntries?: () => InputMapEntry<InteractionName<THandlers>>[]); private _createDefaultEntries?; /** * Resolves a physical input event to a matching inputMap entry. * Iterates the inputMap in order; the first entry whose source and conditions match wins. * @param source - The physical input source (e.g. "pointer", "keyboard") * @param currentConditions - Conditions to match against, specific to the source type * @returns The matched InputMapEntry, or null if no entry matches */ resolveInteraction(source: "pointer", currentConditions?: InputConditions): PointerInputMapEntry<InteractionName<THandlers>> | null; resolveInteraction(source: "wheel", currentConditions?: InputConditions): WheelInputMapEntry<InteractionName<THandlers>> | null; resolveInteraction(source: "touch", currentConditions?: InputConditions): TouchInputMapEntry<InteractionName<THandlers>> | null; resolveInteraction(source: "keyboard", currentConditions?: InputConditions): KeyboardInputMapEntry<InteractionName<THandlers>> | null; resolveInteraction(source: InputSource, currentConditions?: InputConditions): InputMapEntry<InteractionName<THandlers>> | null; /** * Restores the inputMap to the default entries provided at construction time. * If no factory was provided, resets to an empty array. */ resetInputMap(): void; /** * Finds the first inputMap entry matching the given source, interaction, and optional entry conditions. * Useful for modifying entry properties (e.g. sensitivity) without rebuilding the entire inputMap. * @param source - The physical input source to match * @param interaction - The interaction type to match * @param conditions - Optional entry conditions to match. Omitted condition fields are ignored. * @returns The matching entry, or undefined if not found */ getEntry(source: "pointer", interaction: InteractionName<THandlers>, conditions?: PointerConditions): PointerInputMapEntry<InteractionName<THandlers>> | undefined; getEntry(source: "wheel", interaction: InteractionName<THandlers>, conditions?: WheelConditions): WheelInputMapEntry<InteractionName<THandlers>> | undefined; getEntry(source: "touch", interaction: InteractionName<THandlers>, conditions?: TouchConditions): TouchInputMapEntry<InteractionName<THandlers>> | undefined; getEntry(source: "keyboard", interaction: InteractionName<THandlers>, conditions?: KeyboardConditions): KeyboardInputMapEntry<InteractionName<THandlers>> | undefined; getEntry(source: InputSource, interaction: InteractionName<THandlers>, conditions?: InputConditions): InputMapEntry<InteractionName<THandlers>> | undefined; /** * Finds all inputMap entries matching the given source, interaction, and optional entry conditions. * Useful for bulk updates when more than one physical input maps to the same interaction. * @param source - The physical input source to match * @param interaction - The interaction type to match * @param conditions - Optional entry conditions to match. Omitted condition fields are ignored. * @returns All matching entries, in inputMap order */ getEntries(source: "pointer", interaction: InteractionName<THandlers>, conditions?: PointerConditions): PointerInputMapEntry<InteractionName<THandlers>>[]; getEntries(source: "wheel", interaction: InteractionName<THandlers>, conditions?: WheelConditions): WheelInputMapEntry<InteractionName<THandlers>>[]; getEntries(source: "touch", interaction: InteractionName<THandlers>, conditions?: TouchConditions): TouchInputMapEntry<InteractionName<THandlers>>[]; getEntries(source: "keyboard", interaction: InteractionName<THandlers>, conditions?: KeyboardConditions): KeyboardInputMapEntry<InteractionName<THandlers>>[]; getEntries(source: InputSource, interaction: InteractionName<THandlers>, conditions?: InputConditions): InputMapEntry<InteractionName<THandlers>>[]; /** * Adds an entry to the inputMap at the correct position based on specificity. * More specific entries (with more conditions like button, key, modifiers) are placed * before less specific ones, ensuring they match first. Among equally specific entries, * the new entry is placed after existing ones. * @param entry - The entry to add */ addEntry(entry: InputMapEntry<InteractionName<THandlers>>): void; /** * Sets the interaction for the input combination described by `conditions`. If an * existing entry maps that exact combination, its `interaction` is updated in place; * otherwise a new entry is inserted via {@link addEntry}. * * To force an update on every matching entry use {@link setInteractions}; to address * an individual entry beyond the first, look it up via {@link getEntries} and assign * `entry.interaction` directly. * @param source - The physical input source to match * @param conditions - Conditions describing the input combination (button, modifiers, key, etc.) * @param interaction - The interaction to assign / insert * @returns true (the mapping is always made effective) */ setInteraction(source: InputSource, conditions: InputConditions | undefined, interaction: InteractionName<THandlers>): boolean; /** * Returns true when `entry` constrains every field that `conditions` specifies — i.e. * `entry` covers all of the request's conditions. Used by {@link setInteraction} as one * half of the "exactly as specific" check: combined with {@link resolveInteraction}'s * guarantee that the returned entry is no *stricter* than the request, a true result * here means `entry` and `conditions` constrain the same fields, and mutating * `entry.interaction` is safe. * * On its own this predicate does not exclude entries that are stricter than `conditions` * (those would also trivially cover every field present in `conditions`); callers must * rely on the upstream resolve step to rule them out. * * `InputConditions` is a flat type covering all source-specific fields (`button`, `key`, * `touchCount`, `modifiers`); for any given source the irrelevant fields are `undefined` * on both `entry` and `conditions`, so checking them all is harmless. * @param entry - The matched inputMap entry to test * @param conditions - The conditions the caller supplied to `setInteraction` * @returns true if `entry` covers every condition present in `conditions` */ private _entryCoversAllConditionsOf; /** * Changes the interaction for every inputMap entry matching the given source and conditions. * Useful when more than one entry maps to the same physical input (e.g. duplicate bindings, * or several keys aliased to the same action) and all of them should be remapped together. * @param source - The physical input source to match * @param conditions - Conditions to match (button, modifiers, key, etc.). Uses the same * event-resolution semantics as {@link resolveInteraction}: omitted entry * condition fields are treated as wildcards and will match. * @param interaction - The new interaction to assign to every matched entry * @returns The number of entries that were updated */ setInteractions(source: InputSource, conditions: InputConditions | undefined, interaction: InteractionName<THandlers>): number; private _entryMatches; private _entryConditionsMatch; private _entrySpecificity; private _matchModifiers; private _entryModifiersMatch; }