Skip to content
Merged
8 changes: 6 additions & 2 deletions packages/@dcl/playground-assets/etc/playground-assets.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5405,10 +5405,14 @@ export type uint32 = number;

// @public (undocumented)
export type UiRendererOptions = {
virtualWidth: number;
virtualHeight: number;
virtualWidth?: number;
virtualHeight?: number;
screenInset?: UiScreenInset;
};

// @public
export type UiScreenInset = 'device' | 'interactable' | 'none';

// @public
export type UiScreenInsetAreaProps = Omit<EntityPropTypes, 'uiTransform'> & {
uiTransform?: Omit<NonNullable<EntityPropTypes['uiTransform']>, 'positionType' | 'position'>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactEcs } from '../../react-ecs'
import { UiEntity } from '../index'
import { getInteractableArea } from '../utils'
import { compensateInsetForUiScale, getInteractableArea } from '../utils'
import { UiInteractableAreaProps } from './types'

/**
Expand All @@ -25,7 +25,8 @@ import { UiInteractableAreaProps } from './types'
*/
/* @__PURE__ */
export function InteractableArea(props: UiInteractableAreaProps) {
const { top, left, right, bottom } = getInteractableArea()
// Insets are canvas px; pre-divide so the parser's scale multiplication cancels out.
const { top, left, right, bottom } = compensateInsetForUiScale(getInteractableArea())
const { uiTransform, ...otherProps } = props

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactEcs } from '../../react-ecs'
import { UiEntity } from '../index'
import { getScreenInsetArea } from '../utils'
import { compensateInsetForUiScale, getScreenInsetArea } from '../utils'
import { UiScreenInsetAreaProps } from './types'

/**
Expand All @@ -24,7 +24,8 @@ import { UiScreenInsetAreaProps } from './types'
*/
/* @__PURE__ */
export function ScreenInsetArea(props: UiScreenInsetAreaProps) {
const { top, left, right, bottom } = getScreenInsetArea()
// Insets are canvas px; pre-divide so the parser's scale multiplication cancels out.
const { top, left, right, bottom } = compensateInsetForUiScale(getScreenInsetArea())
const { uiTransform, ...otherProps } = props

return (
Expand Down
29 changes: 24 additions & 5 deletions packages/@dcl/react-ecs/src/components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export function getScaleAndUnit(scaleUnit: ScaleUnit): [number, ScaleUnits] {
/**
* @internal
*/
export function scaleOnDim(scale: number, dim: number, pxRatio: number) {
return (dim / 100) * (scale / pxRatio)
export function scaleOnDim(scale: number, dim: number) {
return (dim / 100) * scale
}

/**
Expand Down Expand Up @@ -109,6 +109,22 @@ export function resetUiScaleFactor(owner?: symbol): void {
uiScaleFactor = 1
}

/**
* Divides an inset area by the current UI scale factor.
*
* Inset areas are reported by the renderer in canvas pixels, but raw pixel
* values in `uiTransform` props are multiplied by the UI scale factor when
* parsed. Pre-dividing cancels that multiplication out, so the values sent to
* the renderer stay in canvas pixels regardless of the virtual screen.
*
* @internal
*/
export function compensateInsetForUiScale(area: BorderRect): BorderRect {
const scale = getUiScaleFactor()
const factor = scale > 0 ? scale : 1
return { top: area.top / factor, left: area.left / factor, right: area.right / factor, bottom: area.bottom / factor }
}

/**
* @internal
*/
Expand Down Expand Up @@ -192,10 +208,13 @@ export function calcOnViewport(value: ScaleUnit, ctx: ScaleContext | undefined =
const [scale, unit] = getScaleAndUnit(value)
if (!ctx) return scale

const { height, width, ratio } = ctx
// `ctx.ratio` is intentionally not read: '1vw' is 1% of the canvas width by
// definition, exactly as in CSS. It stays on ScaleContext as an informational
// density hint for callers that need to pick an asset resolution.
const { height, width } = ctx

if (unit === 'vh') return scaleOnDim(scale, height, ratio)
if (unit === 'vh') return scaleOnDim(scale, height)

// by default, we scale by 'vw' (width)
return scaleOnDim(scale, width, ratio)
return scaleOnDim(scale, width)
}
28 changes: 28 additions & 0 deletions packages/@dcl/react-ecs/src/platform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Platform detection hook for the UI scale system.
*
* react-ecs stays independent of the scene runtime (`~system/*` modules), so
* it cannot ask the explorer which platform it runs on. Instead, the host SDK
* injects the check at module-load time (see `@dcl/sdk/react-ecs`). Until a
* provider is injected — or when none ever is — the platform is assumed to be
* non-mobile.
*/
let isMobileProvider: () => boolean = () => false

/**
* Injects the function used to detect whether the scene runs on a mobile
* platform. Called by `@dcl/sdk` with its `isMobile()` platform helper.
*/
export function setIsMobileProvider(provider: () => boolean): void {
isMobileProvider = provider
}

/**
* Whether the scene is running on a mobile platform, according to the
* injected provider. Platform detection is asynchronous on the SDK side, so
* this may return false during the first ticks and flip to true once the
* explorer information arrives.
*/
export function isMobile(): boolean {
return isMobileProvider()
}
Loading
Loading