diff --git a/src/framework/components/scroll-view/component.js b/src/framework/components/scroll-view/component.js index 9de3057585c..97306bf5792 100644 --- a/src/framework/components/scroll-view/component.js +++ b/src/framework/components/scroll-view/component.js @@ -20,41 +20,6 @@ const _tempScrollValue = new Vec2(); * A ScrollViewComponent enables a group of entities to behave like a masked scrolling area, with * optional horizontal and vertical scroll bars. * - * @property {boolean} horizontal Whether to enable horizontal scrolling. - * @property {boolean} vertical Whether to enable vertical scrolling. - * @property {number} scrollMode Specifies how the scroll view should behave when the user scrolls - * past the end of the content. Modes are defined as follows: - * - * - {@link SCROLL_MODE_CLAMP}: Content does not scroll any further than its bounds. - * - {@link SCROLL_MODE_BOUNCE}: Content scrolls past its bounds and then gently bounces back. - * - {@link SCROLL_MODE_INFINITE}: Content can scroll forever. - * - * @property {number} bounceAmount Controls how far the content should move before bouncing back. - * @property {number} friction Controls how freely the content should move if thrown, i.e. By - * flicking on a phone or by flinging the scroll wheel on a mouse. A value of 1 means that content - * will stop immediately; 0 means that content will continue moving forever (or until the bounds of - * the content are reached, depending on the scrollMode). - * @property {boolean} useMouseWheel Whether to use mouse wheel for scrolling (horizontally and - * vertically). - * @property {Vec2} mouseWheelSensitivity Mouse wheel horizontal and vertical sensitivity. Only - * used if useMouseWheel is set. Setting a direction to 0 will disable mouse wheel scrolling in - * that direction. 1 is a default sensitivity that is considered to feel good. The values can be - * set higher or lower than 1 to tune the sensitivity. Defaults to [1, 1]. - * @property {number} horizontalScrollbarVisibility Controls whether the horizontal scrollbar - * should be visible all the time, or only visible when the content exceeds the size of the - * viewport. - * @property {number} verticalScrollbarVisibility Controls whether the vertical scrollbar should be - * visible all the time, or only visible when the content exceeds the size of the viewport. - * @property {import('../../entity.js').Entity} viewportEntity The entity to be used as the masked - * viewport area, within which the content will scroll. This entity must have an ElementGroup - * component. - * @property {import('../../entity.js').Entity} contentEntity The entity which contains the - * scrolling content itself. This entity must have an Element component. - * @property {import('../../entity.js').Entity} horizontalScrollbarEntity The entity to be used as - * the vertical scrollbar. This entity must have a Scrollbar component. - * @property {import('../../entity.js').Entity} verticalScrollbarEntity The entity to be used as - * the vertical scrollbar. This entity must have a Scrollbar component. - * * @category User Interface */ class ScrollViewComponent extends Component { @@ -119,6 +84,244 @@ class ScrollViewComponent extends Component { this._toggleElementListeners('on'); } + // TODO: Remove this override in upgrading component + /** + * @type {import('./data.js').ScrollViewComponentData} + * @ignore + */ + get data() { + const record = this.system.store[this.entity.getGuid()]; + return record ? record.data : null; + } + + /** + * @type {boolean} + */ + set enabled(arg) { + this._setValue('enabled', arg); + } + + get enabled() { + return this.data.enabled; + } + + /** + * Whether to enable horizontal scrolling. + * + * @type {boolean} + */ + set horizontal(arg) { + this._setValue('horizontal', arg); + } + + get horizontal() { + return this.data.horizontal; + } + + /** + * Whether to enable vertical scrolling. + * + * @type {boolean} + */ + set vertical(arg) { + this._setValue('vertical', arg); + } + + get vertical() { + return this.data.vertical; + } + + /** + * Specifies how the scroll view should behave when the user scrolls past the end of the content. + * Modes are defined as follows: + * + * - {@link SCROLL_MODE_CLAMP}: Content does not scroll any further than its bounds. + * - {@link SCROLL_MODE_BOUNCE}: Content scrolls past its bounds and then gently bounces back. + * - {@link SCROLL_MODE_INFINITE}: Content can scroll forever. + * + * @type {number} + */ + set scrollMode(arg) { + this._setValue('scrollMode', arg); + } + + get scrollMode() { + return this.data.scrollMode; + } + + /** + * Controls how far the content should move before bouncing back. + * + * @type {number} + */ + set bounceAmount(arg) { + this._setValue('bounceAmount', arg); + } + + get bounceAmount() { + return this.data.bounceAmount; + } + + /** + * Controls how freely the content should move if thrown, i.e. By flicking on a phone or by + * flinging the scroll wheel on a mouse. A value of 1 means that content will stop immediately; + * 0 means that content will continue moving forever (or until the bounds of the content are + * reached, depending on the scrollMode). + * + * @type {number} + */ + set friction(arg) { + this._setValue('friction', arg); + } + + get friction() { + return this.data.friction; + } + + /** + * @type {number} + * @ignore + */ + set dragThreshold(arg) { + this._setValue('dragThreshold', arg); + } + + get dragThreshold() { + return this.data.dragThreshold; + } + + /** + * Whether to use mouse wheel for scrolling (horizontally and vertically). + * + * @type {boolean} + */ + set useMouseWheel(arg) { + this._setValue('useMouseWheel', arg); + } + + get useMouseWheel() { + return this.data.useMouseWheel; + } + + /** + * Mouse wheel horizontal and vertical sensitivity. Only used if useMouseWheel is set. Setting a + * direction to 0 will disable mouse wheel scrolling in that direction. 1 is a default + * sensitivity that is considered to feel good. The values can be set higher or lower than 1 to + * tune the sensitivity. Defaults to [1, 1]. + * + * @type {Vec2} + */ + set mouseWheelSensitivity(arg) { + this._setValue('mouseWheelSensitivity', arg); + } + + get mouseWheelSensitivity() { + return this.data.mouseWheelSensitivity; + } + + /** + * Controls whether the horizontal scrollbar should be visible all the time, or only visible + * when the content exceeds the size of the viewport. + * + * @type {number} + */ + set horizontalScrollbarVisibility(arg) { + this._setValue('horizontalScrollbarVisibility', arg); + } + + get horizontalScrollbarVisibility() { + return this.data.horizontalScrollbarVisibility; + } + + /** + * Controls whether the vertical scrollbar should be visible all the time, or only visible when + * the content exceeds the size of the viewport. + * + * @type {number} + */ + set verticalScrollbarVisibility(arg) { + this._setValue('verticalScrollbarVisibility', arg); + } + + get verticalScrollbarVisibility() { + return this.data.verticalScrollbarVisibility; + } + + /** + * The entity to be used as the masked viewport area, within which the content will scroll. + * This entity must have an ElementGroup component. + * + * @type {import('../../../framework/entity.js').Entity} + */ + set viewportEntity(arg) { + this._setValue('viewportEntity', arg); + } + + get viewportEntity() { + return this.data.viewportEntity; + } + + /** + * The entity which contains the scrolling content itself. This entity must have an Element + * component. + * + * @type {import('../../../framework/entity.js').Entity} + */ + set contentEntity(arg) { + this._setValue('contentEntity', arg); + } + + get contentEntity() { + return this.data.contentEntity; + } + + /** + * The entity to be used as the vertical scrollbar. This entity must have a Scrollbar component. + * + * @type {import('../../../framework/entity.js').Entity} + */ + set horizontalScrollbarEntity(arg) { + this._setValue('horizontalScrollbarEntity', arg); + } + + get horizontalScrollbarEntity() { + return this.data.horizontalScrollbarEntity; + } + + /** + * The entity to be used as the vertical scrollbar. This entity must have a Scrollbar component. + * + * @type {import('../../../framework/entity.js').Entity} + */ + set verticalScrollbarEntity(arg) { + this._setValue('verticalScrollbarEntity', arg); + } + + get verticalScrollbarEntity() { + return this.data.verticalScrollbarEntity; + } + + /** + * Set scroll value. + * + * @type {Vec2} + */ + set scroll(value) { + this._onSetScroll(value.x, value.y); + } + + get scroll() { + return this._scroll; + } + + /** @ignore */ + _setValue(name, value) { + const data = this.data; + const oldValue = data[name]; + data[name] = value; + this.fire('set', name, oldValue, value); + } + /** * @param {string} onOrOff - 'on' or 'off'. * @param {import('./system.js').ScrollViewComponentSystem} system - The ComponentSystem that @@ -146,7 +349,7 @@ class ScrollViewComponent extends Component { this.entity.element[onOrOff]('resize', this._onSetContentOrViewportSize, this); this.entity.element[onOrOff](EVENT_MOUSEWHEEL, this._onMouseWheel, this); - this._hasElementListeners = (onOrOff === 'on'); + this._hasElementListeners = onOrOff === 'on'; } } @@ -203,16 +406,13 @@ class ScrollViewComponent extends Component { // if we haven't already, when scrolling starts // disable input on all child elements if (!this._disabledContentInput) { - // Disable input events on content after we've moved past a threshold value - const dx = (position.x - this._dragStartPosition.x); - const dy = (position.y - this._dragStartPosition.y); + const dx = position.x - this._dragStartPosition.x; + const dy = position.y - this._dragStartPosition.y; - if (Math.abs(dx) > this.dragThreshold || - Math.abs(dy) > this.dragThreshold) { + if (Math.abs(dx) > this.dragThreshold || Math.abs(dy) > this.dragThreshold) { this._disableContentInput(); } - } } } @@ -265,7 +465,7 @@ class ScrollViewComponent extends Component { } _updateAxis(scrollValue, axis, orientation) { - const hasChanged = (scrollValue !== null && Math.abs(scrollValue - this._scroll[axis]) > 1e-5); + const hasChanged = scrollValue !== null && Math.abs(scrollValue - this._scroll[axis]) > 1e-5; // always update if dragging because drag helper directly updates the entity position // always update if scrollValue === 0 because it will be clamped to 0 @@ -330,7 +530,7 @@ class ScrollViewComponent extends Component { if (currMaxOffset === 0) { this._scroll[axis] = 1; } else { - this._scroll[axis] = math.clamp(this._scroll[axis] * prevMaxOffset / currMaxOffset, 0, 1); + this._scroll[axis] = math.clamp((this._scroll[axis] * prevMaxOffset) / currMaxOffset, 0, 1); } } @@ -529,8 +729,8 @@ class ScrollViewComponent extends Component { this._setScrollFromContentPosition(position); } - this._velocity.x *= (1 - this.friction); - this._velocity.y *= (1 - this.friction); + this._velocity.x *= 1 - this.friction; + this._velocity.y *= 1 - this.friction; } } @@ -702,14 +902,6 @@ class ScrollViewComponent extends Component { this._toggleElementListeners('off'); this._destroyDragHelper(); } - - set scroll(value) { - this._onSetScroll(value.x, value.y); - } - - get scroll() { - return this._scroll; - } } export { ScrollViewComponent }; diff --git a/src/framework/components/scroll-view/data.js b/src/framework/components/scroll-view/data.js index e872faf7f45..d546a75f25d 100644 --- a/src/framework/components/scroll-view/data.js +++ b/src/framework/components/scroll-view/data.js @@ -1,7 +1,48 @@ +import { Vec2 } from '../../../core/math/vec2.js'; + +const DEFAULT_DRAG_THRESHOLD = 10; + class ScrollViewComponentData { - constructor() { - this.enabled = true; - } + enabled = true; + + /** @type {boolean} */ + horizontal; + + /** @type {boolean} */ + vertical; + + /** @type {number} */ + scrollMode; + + /** @type {number} */ + bounceAmount; + + /** @type {number} */ + friction; + + dragThreshold = DEFAULT_DRAG_THRESHOLD; + + useMouseWheel = true; + + mouseWheelSensitivity = new Vec2(1, 1); + + /** @type {number} */ + horizontalScrollbarVisibility; + + /** @type {number} */ + verticalScrollbarVisibility; + + /** @type {import('../../../framework/entity.js').Entity} */ + viewportEntity; + + /** @type {import('../../../framework/entity.js').Entity} */ + contentEntity; + + /** @type {import('../../../framework/entity.js').Entity} */ + horizontalScrollbarEntity; + + /** @type {import('../../../framework/entity.js').Entity} */ + verticalScrollbarEntity; } export { ScrollViewComponentData }; diff --git a/src/framework/components/scroll-view/system.js b/src/framework/components/scroll-view/system.js index 3fa1ee91691..d02b5bede21 100644 --- a/src/framework/components/scroll-view/system.js +++ b/src/framework/components/scroll-view/system.js @@ -1,4 +1,3 @@ -import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; import { ScrollViewComponent } from './component.js'; @@ -91,6 +90,4 @@ class ScrollViewComponentSystem extends ComponentSystem { } } -Component._buildAccessors(ScrollViewComponent.prototype, _schema); - export { ScrollViewComponentSystem }; diff --git a/src/framework/components/scrollbar/component.js b/src/framework/components/scrollbar/component.js index 26e1265007e..cfad81ae012 100644 --- a/src/framework/components/scrollbar/component.js +++ b/src/framework/components/scrollbar/component.js @@ -11,20 +11,6 @@ import { EntityReference } from '../../utils/entity-reference.js'; /** * A ScrollbarComponent enables a group of entities to behave like a draggable scrollbar. * - * @property {number} orientation Whether the scrollbar moves horizontally or vertically. Can be: - * - * - {@link ORIENTATION_HORIZONTAL}: The scrollbar animates in the horizontal axis. - * - {@link ORIENTATION_VERTICAL}: The scrollbar animates in the vertical axis. - * - * Defaults to {@link ORIENTATION_HORIZONTAL}. - * @property {number} value The current position value of the scrollbar, in the range 0 to 1. - * Defaults to 0. - * @property {number} handleSize The size of the handle relative to the size of the track, in the - * range 0 to 1. For a vertical scrollbar, a value of 1 means that the handle will take up the full - * height of the track. - * @property {import('../../entity.js').Entity} handleEntity The entity to be used as the scrollbar - * handle. This entity must have a Scrollbar component. - * * @category User Interface */ class ScrollbarComponent extends Component { @@ -62,6 +48,93 @@ class ScrollbarComponent extends Component { this._toggleLifecycleListeners('on'); } + // TODO: Remove this override in upgrading component + /** + * @type {import('./data.js').ScrollbarComponentData} + * @ignore + */ + get data() { + const record = this.system.store[this.entity.getGuid()]; + return record ? record.data : null; + } + + /** + * @type {boolean} + */ + set enabled(arg) { + this._setValue('enabled', arg); + } + + get enabled() { + return this.data.enabled; + } + + /** + * Whether the scrollbar moves horizontally or vertically. Can be: + * + * - {@link ORIENTATION_HORIZONTAL}: The scrollbar animates in the horizontal axis. + * - {@link ORIENTATION_VERTICAL}: The scrollbar animates in the vertical axis. + * + * Defaults to {@link ORIENTATION_HORIZONTAL}. + * + * @type {number} + */ + set orientation(arg) { + this._setValue('orientation', arg); + } + + get orientation() { + return this.data.orientation; + } + + /** + * The current position value of the scrollbar, in the range 0 to 1. Defaults to 0. + * + * @type {number} + */ + set value(arg) { + this._setValue('value', arg); + } + + get value() { + return this.data.value; + } + + /** + * The size of the handle relative to the size of the track, in the range 0 to 1. For a vertical + * scrollbar, a value of 1 means that the handle will take up the full height of the track. + * + * @type {number} + */ + set handleSize(arg) { + this._setValue('handleSize', arg); + } + + get handleSize() { + return this.data.handleSize; + } + + /** + * The entity to be used as the scrollbar handle. This entity must have a Scrollbar component. + * + * @type {import('../../../framework/entity.js').Entity} + */ + set handleEntity(arg) { + this._setValue('handleEntity', arg); + } + + get handleEntity() { + return this.data.handleEntity; + } + + /** @ignore */ + _setValue(name, value) { + const data = this.data; + const oldValue = data[name]; + data[name] = value; + this.fire('set', name, oldValue, value); + } + /** * @param {string} onOrOff - 'on' or 'off'. * @private diff --git a/src/framework/components/scrollbar/data.js b/src/framework/components/scrollbar/data.js index 49914ae9e76..243f76fde4d 100644 --- a/src/framework/components/scrollbar/data.js +++ b/src/framework/components/scrollbar/data.js @@ -1,7 +1,17 @@ +import { ORIENTATION_HORIZONTAL } from '../../../scene/constants.js'; + class ScrollbarComponentData { - constructor() { - this.enabled = true; - } + enabled = true; + + orientation = ORIENTATION_HORIZONTAL; + + value = 0; + + /** @type {number} */ + handleSize; + + /** @type {import('../../../framework/entity').Entity} */ + handleEntity; } export { ScrollbarComponentData }; diff --git a/src/framework/components/scrollbar/system.js b/src/framework/components/scrollbar/system.js index 1511024fcb7..ec24224ca1d 100644 --- a/src/framework/components/scrollbar/system.js +++ b/src/framework/components/scrollbar/system.js @@ -1,4 +1,3 @@ -import { Component } from '../component.js'; import { ComponentSystem } from '../system.js'; import { ScrollbarComponent } from './component.js'; @@ -46,6 +45,4 @@ class ScrollbarComponentSystem extends ComponentSystem { } } -Component._buildAccessors(ScrollbarComponent.prototype, _schema); - export { ScrollbarComponentSystem }; diff --git a/utils/types-fixup.mjs b/utils/types-fixup.mjs index 3147fa169a7..aa627b5156c 100644 --- a/utils/types-fixup.mjs +++ b/utils/types-fixup.mjs @@ -1,8 +1,5 @@ import fs from 'fs'; -// A regex that matches a string starting with 'constructor' and ending with ');' -const regexConstructor = /constructor(.*?)\);/g; - // Generate TS declarations for getter/setter pairs const getDeclarations = (properties) => { let declarations = ''; @@ -19,46 +16,6 @@ const getDeclarations = (properties) => { let path, dts; -const scrollbarComponentProps = [ - ['handleEntity', 'Entity'], - ['handleSize', 'number'], - ['orientation', 'number'] -]; - -path = './types/framework/components/scrollbar/component.d.ts'; -dts = fs.readFileSync(path, 'utf8'); -dts = dts.replace(regexConstructor, '$&\n' + getDeclarations(scrollbarComponentProps)); -// We need to import types that are newly introduced in the property list above -dts += ` -import { Entity } from '../../../framework/entity.js'; -`; -fs.writeFileSync(path, dts); - -const scrollViewComponentProps = [ - ['bounceAmount', 'number'], - ['contentEntity', 'Entity'], - ['friction', 'number'], - ['horizontal', 'boolean'], - ['horizontalScrollbarEntity', 'Entity'], - ['horizontalScrollbarVisibility', 'number'], - ['mouseWheelSensitivity', 'Vec2'], - ['scrollMode', 'number'], - ['useMouseWheel', 'boolean'], - ['vertical', 'boolean'], - ['verticalScrollbarEntity', 'Entity'], - ['verticalScrollbarVisibility', 'number'], - ['viewportEntity', 'Entity'] -]; - -path = './types/framework/components/scroll-view/component.d.ts'; -dts = fs.readFileSync(path, 'utf8'); -dts = dts.replace(regexConstructor, '$&\n' + getDeclarations(scrollViewComponentProps)); -// We need to import types that are newly introduced in the property list above -dts += ` -import { Entity } from '../../../framework/entity.js'; -`; -fs.writeFileSync(path, dts); - const standardMaterialProps = [ ['alphaFade', 'boolean'], ['ambient', 'Color'],