import { FocusType } from '../core/FocusType.js';
import type { Widget } from '../widgets/Widget.js';
import type { Driver } from '../core/Driver.js';
import type { Root } from '../core/Root.js';
import type { CaptureList } from '../core/CaptureList.js';
/**
 * A group of Roots. When a {@link TabSelectEvent} is not captured by a
 * {@link Root} in a group, the TabSelectEvent is carried over to the next (or
 * previous, depending on the direction) root in the group. Although
 * TabSelectEvent events are carried over between Roots in the same group, they
 * are **not** automatically carried over **between different groups**.
 *
 * This behaviour is useful for binding Roots to DOM elements. For example,
 * {@link DOMRoot | DOMRoots} should be placed in groups with a single DOMRoot,
 * since the DOMRoot owns its own DOM element, but Roots used for an external 3D
 * engine, where the roots share a single DOM element such as a canvas used as
 * the output for rendering, should all be in the same group.
 *
 * @category Driver
 */
export interface KeyboardDriverGroup {
    /**
     * The list of {@link Root | Roots} assigned to this group, in the order
     * they were added to this group. Not to be confused with
     * {@link KeyboardDriver#accessList}.
     */
    roots: Array<Root>;
    /**
     * Similar to {@link KeyboardDriverGroup#roots}, but only contains enabled
     * {@link Root | Roots}.
     */
    enabledRoots: Array<Root>;
    /**
     * Similar to {@link KeyboardDriverGroup#enabledRoots}, but only contains
     * enabled {@link Root | Roots} that are {@link Root#tabFocusable}.
     */
    tabbableRoots: Array<Root>;
    /**
     * Should {@link TabSelectEvent} events wrap-around to the other end of the
     * group if not captured by the last (or first) {@link Root}? If this is
     * true, the navigation will be trapped to this group for keyboard-only
     * users, since that will be the only way to change keyboard focus. Useful
     * for 3D engines where all Roots share the same canvas.
     */
    wrapsAround: boolean;
}
/**
 * Options used for creating a new {@link KeyboardDriverGroup}.
 *
 * @category Driver
 */
export interface KeyboardDriverGroupOptions {
    /** See {@link KeyboardDriverGroup#wrapsAround}. */
    wrapsAround: boolean;
}
/**
 * A generic keyboard {@link Driver | driver}.
 *
 * Does nothing on its own, but provides an API for sending keyboard events to
 * registered roots.
 *
 * @category Driver
 */
export declare class KeyboardDriver<G extends KeyboardDriverGroup = KeyboardDriverGroup, O extends KeyboardDriverGroupOptions = KeyboardDriverGroupOptions> implements Driver {
    /**
     * Groups belonging to this driver. Roots in the same group transfer tab
     * selections between themselves. Do not modify from a child class.
     */
    protected readonly groups: G[];
    /**
     * A map from a Root to a group. Used only for optimisation purposes. Do not
     * modify from a child class.
     */
    protected readonly groupMap: Map<Root, G>;
    /**
     * The list of {@link Root | Roots} that are using this driver, in the order
     * of access; the last focused (with any focus type) Root is moved to the
     * beginning of the list.
     *
     * Used as a fallback for {@link KeyboardDriver#focus}.
     */
    private accessList;
    /** A set containing the keys currently down. */
    private keysDown;
    /** The currently focused root. New keyboard events will go to this root */
    private focus;
    /**
     * Changes the current {@link KeyboardDriver#focus | root focus}.
     *
     * If there was a previous root focus, that root's {@link Root#clearFocus}
     * is called with {@link FocusType#Keyboard}.
     *
     * {@link KeyboardDriver#keysDown} is cleared.
     */
    protected changeFocusedRoot(root: Root | null): void;
    /**
     * Get the current {@link KeyboardDriver#focus | root focus}.
     *
     * @returns Returns {@link KeyboardDriver#focus}
     */
    getFocusedRoot(): Root | null;
    /**
     * Similar to {@link KeyboardDriver#getFocusedRoot}, but can fall back to
     * the first root of {@link KeyboardDriver#accessList} if
     * {@link KeyboardDriver#focus} is null.
     */
    getEffectiveFocusedRoot(): Root | null;
    /**
     * Clear the current {@link KeyboardDriver#focus | root focus}. Calls
     * {@link KeyboardDriver#changeFocusedRoot} with null.
     */
    clearFocus(): void;
    /**
     * Dispatch a new {@link KeyPressEvent} event to the
     * {@link KeyboardDriver#getEffectiveFocusedRoot | effective focused Root}.
     *
     * @param key - Must follow the {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values | KeyboardEvent.key} Web API.
     * @param shift - Is shift being pressed?
     * @param ctrl - Is control being pressed?
     * @param alt - Is alt being pressed?
     * @param virtual - Is the key down originating from a virtual keyboard? False by default
     * @returns Returns a list of dispatched events and whether they were captured.
     */
    keyDown(key: string, shift: boolean, ctrl: boolean, alt: boolean, virtual?: boolean): CaptureList;
    /**
     * Dispatch a new {@link KeyReleaseEvent} event to the
     * {@link KeyboardDriver#getEffectiveFocusedRoot | effective focused Root}.
     *
     * @param key - Must follow the {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values | KeyboardEvent.key} Web API.
     * @param shift - Is shift being pressed?
     * @param ctrl - Is control being pressed?
     * @param alt - Is alt being pressed?
     * @param virtual - Is the key up originating from a virtual keyboard? False by default
     * @returns Returns a list of dispatched events and whether they were captured.
     */
    keyUp(key: string, shift: boolean, ctrl: boolean, alt: boolean, virtual?: boolean): CaptureList;
    /**
     * Calls {@link KeyboardDriver#keyDown} followed by
     * {@link KeyboardDriver#keyUp}. If the key was already down before calling
     * ({@link KeyboardDriver#isKeyDown}), keyUp is not called.
     *
     * @param key - Must follow the {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values | KeyboardEvent.key} Web API.
     * @param shift - Is shift being pressed?
     * @param ctrl - Is control being pressed?
     * @param alt - Is alt being pressed?
     * @param virtual - Is the key press originating from a virtual keyboard? False by default
     * @returns Returns a list of dispatched events and whether they were captured.
     */
    keyPress(key: string, shift: boolean, ctrl: boolean, alt: boolean, virtual?: boolean): CaptureList;
    /**
     * Check if a key is pressed.
     *
     * @param key - Must follow the {@link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values | KeyboardEvent.key} Web API.
     *
     * @returns Returns true if key was in {@link KeyboardDriver#keysDown}
     */
    isKeyDown(key: string): boolean;
    /**
     * Adds enabled root to {@link KeyboardDriver#accessList} and its respective
     * group.
     */
    onEnable(root: Root): void;
    /**
     * Removes disabled root from {@link KeyboardDriver#accessList} and its
     * respective group. If the root was the {@link KeyboardDriver#focus}, then
     * {@link KeyboardDriver#clearFocus | the focus is cleared }.
     */
    onDisable(root: Root): void;
    update(_root: Root): void;
    /**
     * Does nothing if the new focus type is not a {@link FocusType.Keyboard}.
     * If the focus comes from a root which is not the
     * {@link KeyboardDriver#focus | root focus}, then the root focus is
     * {@link KeyboardDriver#changeFocusedRoot | changed to the new root}. If
     * there is no new focused widget (the root's keyboard focus was cleared),
     * then nothing happens.
     *
     * If a {@link Root} becomes focused (with any focus type, not just keyboard
     * focus), it is moved to the beginning of the
     * {@link KeyboardDriver#accessList} list.
     *
     * This behaviour is confusing, however, it's required so that the keyboard
     * focus "lingers" for future tab key presses; this way, pressing tab can do
     * tab selection even when there is no widget that wants keyboard input.
     * When a focus is lingering, then it means that key events are still being
     * dispatched to the last focused root, but they don't have a target. This
     * way, most events get dropped, but tab key events are used for tab
     * selection.
     */
    onFocusChanged(root: Root, focusType: FocusType, newFocus: Widget | null): void;
    onFocusCapturerChanged(_root: Root, _focusType: FocusType, _oldCapturer: Widget | null, _newCapturer: Widget | null): void;
    /**
     * Check if the currently focused root needs keyboard input. Virtual
     * keyboard should query this property to know when to show themselves.
     */
    get needsInput(): boolean;
    /**
     * Dispatches an event to the currently focused root (or a fallback).
     * Handles wrap-around for tab selection. Internal use only.
     *
     * @param event - The event to dispatch
     */
    private dispatchEvent;
    /** Get the index of a group in the groups list. For internal use only */
    private getGroupIndex;
    /**
     * Get the group that a {@link Root} is assigned to. Throws an error if the
     * Root is not assigned to any group in this driver.
     *
     * @returns Returns the group assigned to this Root. The group is live, do not modify it directly.
     */
    getGroup(root: Root): G;
    /**
     * Get a new group, with no {@link Root | Roots}.
     *
     * @returns Returns the created group. The group is live, do not modify it directly.
     */
    createGroup(options: O): G;
    /**
     * Delete a group that is assigned to this keyboard. Throws an error if the
     * group is still in use (has assigned {@link Root | Roots}).
     */
    deleteGroup(group: G): void;
    /**
     * Bind a {@link Root} to a group that is assigned to this keyboard. Throws
     * an error if the Root is already assigned to a group in this driver.
     */
    bindRoot(root: Root, group: G): void;
    /**
     * Unbind a {@link Root} from its assigned group. Throws an error if the
     * Root is not assigned to any group in this driver.
     */
    unbindGroup(root: Root): void;
    /**
     * Bind a {@link Root} to this keyboard, in a new group dedicated to the
     * Root. Equivalent to creating a new group and binding a Root to it. Useful
     * if you are using lazy-widgets directly in the DOM, where each Root has a
     * dedicated DOM element.
     *
     * @returns Returns the created group. The group is live, do not modify it directly.
     */
    bindSingletRoot(root: Root, options: O): G;
}
