Skip to content

Commit

Permalink
Adding DisplayedTrailsProperty, DisplayedProperty => TypeScript, addi…
Browse files Browse the repository at this point in the history
…ng Node rootedDisplayChangedEmitter, pdomVisibleProperty, see #1621
  • Loading branch information
jonathanolson committed Apr 2, 2024
1 parent df9ab12 commit fd2db6b
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 145 deletions.
2 changes: 2 additions & 0 deletions js/accessibility/pdom/PDOMTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ const PDOMTree = {
if ( removedItemToRemove ) {
PDOMTree.removeTree( node, removedItemToRemove, pdomTrails );
removedItemToRemove._pdomParent = null;
removedItemToRemove.pdomParentChangedEmitter.emit();
}
}

Expand All @@ -148,6 +149,7 @@ const PDOMTree = {
PDOMTree.removeTree( removedParents[ j ], addedItemToRemove );
}
addedItemToRemove._pdomParent = node;
addedItemToRemove.pdomParentChangedEmitter.emit();
}
}

Expand Down
70 changes: 57 additions & 13 deletions js/accessibility/pdom/ParallelDOM.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ import TReadOnlyProperty, { isTReadOnlyProperty } from '../../../../axon/js/TRea
import ReadOnlyProperty from '../../../../axon/js/ReadOnlyProperty.js';
import isSettingPhetioStateProperty from '../../../../tandem/js/isSettingPhetioStateProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import TinyForwardingProperty from '../../../../axon/js/TinyForwardingProperty.js';
import TProperty from '../../../../axon/js/TProperty.js';

const INPUT_TAG = PDOMUtils.TAGS.INPUT;
const P_TAG = PDOMUtils.TAGS.P;
Expand Down Expand Up @@ -221,6 +223,7 @@ const ACCESSIBILITY_OPTION_KEYS = [
'focusHighlight',
'focusHighlightLayerable',
'groupFocusHighlight',
'pdomVisibleProperty',
'pdomVisible',
'pdomOrder',

Expand Down Expand Up @@ -278,6 +281,7 @@ export type ParallelDOMOptions = {
focusHighlight?: Highlight; // Sets the focus highlight for the node
focusHighlightLayerable?: boolean; //lag to determine if the focus highlight node can be layered in the scene graph
groupFocusHighlight?: Node | boolean; // Sets the outer focus highlight for this node when a descendant has focus
pdomVisibleProperty?: TReadOnlyProperty<boolean> | null;
pdomVisible?: boolean; // Sets whether or not the node's DOM element is visible in the parallel DOM
pdomOrder?: ( Node | null )[] | null; // Modifies the order of accessible navigation

Expand Down Expand Up @@ -492,7 +496,7 @@ export default class ParallelDOM extends PhetioObject {
// be found by the assistive technology virtual cursor. For more information on how assistive technologies
// read with the virtual cursor see
// http://www.ssbbartgroup.com/blog/how-windows-screen-readers-work-on-the-web/
private _pdomVisible: boolean;
private readonly _pdomVisibleProperty: TinyForwardingProperty<boolean>;

// If provided, it will override the focus order between children
// (and optionally arbitrary subtrees). If not provided, the focus order will default to the rendering order
Expand Down Expand Up @@ -559,10 +563,13 @@ export default class ParallelDOM extends PhetioObject {
private _pdomHeadingBehavior: PDOMBehaviorFunction;

// Emits an event when the focus highlight is changed.
public readonly focusHighlightChangedEmitter: TEmitter;
public readonly focusHighlightChangedEmitter: TEmitter = new TinyEmitter();

// Emits an event when the pdom parent of this Node has changed
public readonly pdomParentChangedEmitter: TEmitter = new TinyEmitter();

// Fired when the PDOM Displays for this Node have changed (see PDOMInstance)
public readonly pdomDisplaysEmitter: TEmitter;
public readonly pdomDisplaysEmitter: TEmitter = new TinyEmitter();

// PDOM specific enabled listener
protected pdomBoundInputEnabledListener: ( enabled: boolean ) => void;
Expand Down Expand Up @@ -611,7 +618,6 @@ export default class ParallelDOM extends PhetioObject {
this._focusHighlight = null;
this._focusHighlightLayerable = false;
this._groupFocusHighlight = false;
this._pdomVisible = true;
this._pdomOrder = null;
this._pdomParent = null;
this._pdomTransformSourceNode = null;
Expand All @@ -622,14 +628,14 @@ export default class ParallelDOM extends PhetioObject {
this._positionInPDOM = false;
this.excludeLabelSiblingFromInput = false;

this._pdomVisibleProperty = new TinyForwardingProperty<boolean>( true, false, this.onPdomVisiblePropertyChange.bind( this ) );

// HIGHER LEVEL API INITIALIZATION

this._accessibleNameBehavior = ParallelDOM.BASIC_ACCESSIBLE_NAME_BEHAVIOR;
this._helpTextBehavior = ParallelDOM.HELP_TEXT_AFTER_CONTENT;
this._headingLevel = null;
this._pdomHeadingBehavior = DEFAULT_PDOM_HEADING_BEHAVIOR;
this.focusHighlightChangedEmitter = new TinyEmitter();
this.pdomDisplaysEmitter = new TinyEmitter();
this.pdomBoundInputEnabledListener = this.pdomInputEnabledListener.bind( this );
}

Expand Down Expand Up @@ -700,6 +706,8 @@ export default class ParallelDOM extends PhetioObject {

// PDOM attributes can potentially have listeners, so we will clear those out.
this.removePDOMAttributes();

this._pdomVisibleProperty.dispose();
}

private pdomInputEnabledListener( enabled: boolean ): void {
Expand Down Expand Up @@ -745,7 +753,7 @@ export default class ParallelDOM extends PhetioObject {
// when accessibility is widely used, this assertion can be added back in
// assert && assert( this._pdomInstances.length > 0, 'there must be pdom content for the node to receive focus' );
assert && assert( this.focusable, 'trying to set focus on a node that is not focusable' );
assert && assert( this._pdomVisible, 'trying to set focus on a node with invisible pdom content' );
assert && assert( this.pdomVisible, 'trying to set focus on a node with invisible pdom content' );
assert && assert( this._pdomInstances.length === 1, 'focus() unsupported for Nodes using DAG, pdom content is not unique' );

const peer = this._pdomInstances[ 0 ].peer!;
Expand Down Expand Up @@ -2316,18 +2324,54 @@ export default class ParallelDOM extends PhetioObject {
}
}

/**
* Called when our pdomVisible Property changes values.
*/
private onPdomVisiblePropertyChange( visible: boolean ): void {
this._pdomDisplaysInfo.onPDOMVisibilityChange( visible );
}

/**
* Sets what Property our pdomVisibleProperty is backed by, so that changes to this provided Property will change this
* Node's pdom visibility, and vice versa. This does not change this._pdomVisibleProperty. See TinyForwardingProperty.setTargetProperty()
* for more info.
*/
public setPdomVisibleProperty( newTarget: TReadOnlyProperty<boolean> | null ): this {
this._pdomVisibleProperty.setTargetProperty( newTarget );

return this;
}

/**
* See setPdomVisibleProperty() for more information
*/
public set pdomVisibleProperty( property: TReadOnlyProperty<boolean> | null ) {
this.setPdomVisibleProperty( property );
}

/**
* See getPdomVisibleProperty() for more information
*/
public get pdomVisibleProperty(): TProperty<boolean> {
return this.getPdomVisibleProperty();
}


/**
* Get this Node's pdomVisibleProperty. See Node.getVisibleProperty for more information
*/
public getPdomVisibleProperty(): TProperty<boolean> {
return this._pdomVisibleProperty;
}

/**
* Hide completely from a screen reader and the browser by setting the hidden attribute on the node's
* representative DOM element. If the sibling DOM Elements have a container parent, the container
* should be hidden so that all PDOM elements are hidden as well. Hiding the element will remove it from the focus
* order.
*/
public setPDOMVisible( visible: boolean ): void {
if ( this._pdomVisible !== visible ) {
this._pdomVisible = visible;

this._pdomDisplaysInfo.onPDOMVisibilityChange( visible );
}
this.pdomVisibleProperty.value = visible;
}

public set pdomVisible( visible: boolean ) { this.setPDOMVisible( visible ); }
Expand All @@ -2338,7 +2382,7 @@ export default class ParallelDOM extends PhetioObject {
* Get whether or not this node's representative DOM element is visible.
*/
public isPDOMVisible(): boolean {
return this._pdomVisible;
return this.pdomVisibleProperty.value;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions js/imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ export { default as SceneryStyle } from './util/SceneryStyle.js';
export { default as CanvasContextWrapper } from './util/CanvasContextWrapper.js';
export { default as FullScreen } from './util/FullScreen.js';
export { default as CountMap } from './util/CountMap.js';
export { default as DisplayedTrailsProperty } from './util/DisplayedTrailsProperty.js';
export type { DisplayedTrailsPropertyOptions } from './util/DisplayedTrailsProperty.js';
export { default as DisplayedProperty } from './util/DisplayedProperty.js';
export type { DisplayedPropertyOptions } from './util/DisplayedProperty.js';
export { default as SceneImage } from './util/SceneImage.js';
export { default as allowLinksProperty } from './util/allowLinksProperty.js';
export { default as openPopup } from './util/openPopup.js';
Expand Down
42 changes: 21 additions & 21 deletions js/nodes/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,16 +609,16 @@ class Node extends ParallelDOM {
// This is fired only once for any single operation that may change the children of a Node.
// For example, if a Node's children are [ a, b ] and setChildren( [ a, x, y, z ] ) is called on it, the
// childrenChanged event will only be fired once after the entire operation of changing the children is completed.
public readonly childrenChangedEmitter: TEmitter;
public readonly childrenChangedEmitter: TEmitter = new TinyEmitter();

// For every single added child Node, emits with {Node} Node, {number} indexOfChild
public readonly childInsertedEmitter: TEmitter<[ node: Node, indexOfChild: number ]>;
public readonly childInsertedEmitter: TEmitter<[ node: Node, indexOfChild: number ]> = new TinyEmitter();

// For every single removed child Node, emits with {Node} Node, {number} indexOfChild
public readonly childRemovedEmitter: TEmitter<[ node: Node, indexOfChild: number ]>;
public readonly childRemovedEmitter: TEmitter<[ node: Node, indexOfChild: number ]> = new TinyEmitter();

// Provides a given range that may be affected by the reordering
public readonly childrenReorderedEmitter: TEmitter<[ minChangedIndex: number, maxChangedIndex: number ]>;
public readonly childrenReorderedEmitter: TEmitter<[ minChangedIndex: number, maxChangedIndex: number ]> = new TinyEmitter();

// Fired whenever a parent is added
public readonly parentAddedEmitter: TEmitter<[ node: Node ]> = new TinyEmitter();
Expand All @@ -628,29 +628,33 @@ class Node extends ParallelDOM {

// Fired synchronously when the transform (transformation matrix) of a Node is changed. Any
// change to a Node's translation/rotation/scale/etc. will trigger this event.
public readonly transformEmitter: TEmitter;
public readonly transformEmitter: TEmitter = new TinyEmitter();

// Should be emitted when we need to check full metadata updates directly on Instances,
// to see if we need to change drawable types, etc.
public readonly instanceRefreshEmitter: TEmitter;
public readonly instanceRefreshEmitter: TEmitter = new TinyEmitter();

// Emitted to when we need to potentially recompute our renderer summary (bitmask flags, or
// things that could affect descendants)
public readonly rendererSummaryRefreshEmitter: TEmitter;
public readonly rendererSummaryRefreshEmitter: TEmitter = new TinyEmitter();

// Emitted to when we change filters (either opacity or generalized filters)
public readonly filterChangeEmitter: TEmitter;
public readonly filterChangeEmitter: TEmitter = new TinyEmitter();

// Fired when an instance is changed (added/removed). CAREFUL!! This is potentially a very dangerous thing to listen
// to. Instances are updated in an asynchronous batch during `updateDisplay()`, and it is very important that display
// updates do not cause changes the scene graph. Thus, this emitter should NEVER trigger a Node's state to change.
// Currently, all usages of this cause into updates to the audio view, or updates to a separate display (used as an
// overlay). Please proceed with caution, and see https://github.com/phetsims/scenery/issues/1615 and
// https://github.com/phetsims/scenery/issues/1620 for details.
public readonly changedInstanceEmitter: TEmitter<[ instance: Instance, added: boolean ]>;
public readonly changedInstanceEmitter: TEmitter<[ instance: Instance, added: boolean ]> = new TinyEmitter();

// Fired whenever this node is added as a root to a Display OR when it is removed as a root from a Display (i.e.
// the Display is disposed).
public readonly rootedDisplayChangedEmitter: TEmitter<[ display: Display ]> = new TinyEmitter();

// Fired when layoutOptions changes
public readonly layoutOptionsChangedEmitter: TEmitter;
public readonly layoutOptionsChangedEmitter: TEmitter = new TinyEmitter();

// A bitmask which specifies which renderers this Node (and only this Node, not its subtree) supports.
// (scenery-internal)
Expand Down Expand Up @@ -836,17 +840,6 @@ class Node extends ParallelDOM {

this._filters = [];

this.childrenChangedEmitter = new TinyEmitter();
this.childInsertedEmitter = new TinyEmitter();
this.childRemovedEmitter = new TinyEmitter();
this.childrenReorderedEmitter = new TinyEmitter();
this.transformEmitter = new TinyEmitter();
this.instanceRefreshEmitter = new TinyEmitter();
this.rendererSummaryRefreshEmitter = new TinyEmitter();
this.filterChangeEmitter = new TinyEmitter();
this.changedInstanceEmitter = new TinyEmitter();
this.layoutOptionsChangedEmitter = new TinyEmitter();

this._rendererBitmask = Renderer.bitmaskNodeDefault;
this._rendererSummary = new RendererSummary( this );

Expand Down Expand Up @@ -5960,6 +5953,8 @@ class Node extends ParallelDOM {

// Defined in ParallelDOM.js
this._pdomDisplaysInfo.onAddedRootedDisplay( display );

this.rootedDisplayChangedEmitter.emit( display );
}

/**
Expand All @@ -5972,6 +5967,8 @@ class Node extends ParallelDOM {

// Defined in ParallelDOM.js
this._pdomDisplaysInfo.onRemovedRootedDisplay( display );

this.rootedDisplayChangedEmitter.emit( display );
}

private getRecursiveConnectedDisplays( displays: Display[] ): Display[] {
Expand Down Expand Up @@ -6342,6 +6339,9 @@ class Node extends ParallelDOM {
if ( assert && options.hasOwnProperty( 'visible' ) && options.hasOwnProperty( 'visibleProperty' ) ) {
assert && assert( options.visibleProperty!.value === options.visible, 'If both visible and visibleProperty are provided, then values should match' );
}
if ( assert && options.hasOwnProperty( 'pdomVisible' ) && options.hasOwnProperty( 'pdomVisibleProperty' ) ) {
assert && assert( options.pdomVisibleProperty!.value === options.pdomVisible, 'If both pdomVisible and pdomVisibleProperty are provided, then values should match' );
}
if ( assert && options.hasOwnProperty( 'pickable' ) && options.hasOwnProperty( 'pickableProperty' ) ) {
assert && assert( options.pickableProperty!.value === options.pickable, 'If both pickable and pickableProperty are provided, then values should match' );
}
Expand Down
Loading

0 comments on commit fd2db6b

Please sign in to comment.