Skip to content

Commit

Permalink
Add ui button. (#21)
Browse files Browse the repository at this point in the history
* feat: add button
  • Loading branch information
cptbtptpbcptdtptp authored Nov 5, 2024
1 parent 15b8e6c commit 4b2487d
Show file tree
Hide file tree
Showing 23 changed files with 646 additions and 311 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ComponentCloner } from "./clone/ComponentCloner";
import { ActiveChangeFlag } from "./enums/ActiveChangeFlag";
import { ComponentType } from "./enums/ComponentType";
import { UITransform } from "./ui";
import { IUIElement } from "./ui/interface/IUIElement";
import { IUIGraphics } from "./ui/interface/IUIGraphics";
import { DisorderedArray } from "./utils/DisorderedArray";

/**
Expand Down Expand Up @@ -643,7 +643,7 @@ export class Entity extends EngineObject {
if (
component._componentType & ComponentType.UIElement &&
component.enabled &&
(component as unknown as IUIElement)._runtimeRaycastEnable
(component as unknown as IUIGraphics).raycastEnable
) {
this._interactive = true;
return;
Expand Down
13 changes: 8 additions & 5 deletions packages/core/src/input/pointer/PointerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ export class PointerManager implements IInput {
}
for (let j = 0; j < length; j++) {
const event = events[j];
pointer.button = _pointerDec2BinMap[event.button] || PointerButton.None;
pointer.pressedButtons = event.buttons;
switch (event.type) {
case "pointerdown":
pointer.phase = PointerPhase.Down;
Expand Down Expand Up @@ -218,26 +220,27 @@ export class PointerManager implements IInput {
position.set(currX, currY);
for (let i = 0; i < length; i++) {
const event = events[i];
const { button } = event;
pointer.button = _pointerDec2BinMap[button] || PointerButton.None;
pointer.pressedButtons = event.buttons;
switch (event.type) {
case "pointerdown":
case "pointerdown": {
const button = event.button;
_downList.add(button);
_downMap[button] = frameCount;
pointer._downList.add(button);
pointer._downMap[button] = frameCount;
pointer._frameEvents |= PointerEventType.Down;
pointer.phase = PointerPhase.Down;
break;
case "pointerup":
}
case "pointerup": {
const button = event.button;
_upList.add(button);
_upMap[button] = frameCount;
pointer._upList.add(button);
pointer._upMap[button] = frameCount;
pointer._frameEvents |= PointerEventType.Up;
pointer.phase = PointerPhase.Up;
break;
}
case "pointermove":
pointer._frameEvents |= PointerEventType.Move;
pointer.phase = PointerPhase.Move;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class PointerPhysicsEventEmitter extends PointerEventEmitter {
* @internal
*/
override _processDrag(pointer: Pointer): void {
const entity = this._pressedEntity;
const entity = this._draggedEntity;
if (entity) {
this._invokeEntityScripts(entity, (script: Script) => {
script.onPointerDrag?.(this._createEventData(pointer, entity, entity));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ export class PointerUIEventEmitter extends PointerEventEmitter {
}

override _processDrag(pointer: Pointer): void {
if (this._pressedElement) {
if (this._draggedElement) {
this._bubble(
this._composedPath(this._pressedElement, PointerUIEventEmitter._path0),
this._composedPath(this._draggedElement, PointerUIEventEmitter._path0),
pointer,
PointerMethods.onPointerDrag
);
Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/ui/Button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { PointerEventData } from "../input";
import { SafeLoopArray } from "../utils/SafeLoopArray";
import { UIInteractive } from "./interactive/UIInteractive";

export class Button extends UIInteractive {
private _listeners: SafeLoopArray<IUIListener> = new SafeLoopArray<IUIListener>();

/**
* Add a listening function for click.
* @param listener - The listening function
*/
addClicked(listener: (event: PointerEventData) => void): void {
this._listeners.push({ fn: listener });
}

/**
* Remove a listening function of click.
* @param listener - The listening function
*/
removeClicked(listener: (event: PointerEventData) => void): void {
this._listeners.findAndRemove((value) => (value.fn === listener ? (value.destroyed = true) : false));
}

override onPointerClick(event: PointerEventData): void {
const listeners = this._listeners.getLoopArray();
for (let i = 0, n = listeners.length; i < n; i++) {
const listener = listeners[i];
!listener.destroyed && listener.fn(event);
}
}

override onDestroy(): void {
super.onDestroy();
this._listeners.findAndRemove((value) => (value.destroyed = true));
}
}

export interface IUIListener {
fn: (event: PointerEventData) => void;
destroyed?: boolean;
}
147 changes: 23 additions & 124 deletions packages/core/src/ui/UICanvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MathUtil, Matrix, Ray, Vector2, Vector3, Vector4 } from "@galacean/engine-math";
import { MathUtil, Ray, Vector2, Vector3 } from "@galacean/engine-math";
import { Camera, CameraModifyFlags } from "../Camera";
import { Component } from "../Component";
import { DependentMode, dependentComponents } from "../ComponentsDependencies";
Expand All @@ -9,24 +9,16 @@ import { deepClone, ignoreClone } from "../clone/CloneManager";
import { ComponentType } from "../enums/ComponentType";
import { HitResult } from "../physics";
import { DisorderedArray } from "../utils/DisorderedArray";
import { GroupModifyFlags, UIGroup } from "./UIGroup";
import { UIRenderer } from "./UIRenderer";
import { UITransform } from "./UITransform";
import { UIUtils } from "./UIUtils";
import { CanvasRenderMode } from "./enums/CanvasRenderMode";
import { ResolutionAdaptationStrategy } from "./enums/ResolutionAdaptationStrategy";
import { IUIElement } from "./interface/IUIElement";
import { ICanvasElement } from "./interface/ICanvasElement";
import { IUIGraphics } from "./interface/IUIGraphics";

@dependentComponents(UITransform, DependentMode.AutoAdd)
export class UICanvas extends Component implements IUIElement {
@ignoreClone
depth: number = 0;
@deepClone
raycastPadding: Vector4 = new Vector4(0, 0, 0, 0);

/** @internal */
@ignoreClone
_rootCanvas: UICanvas;
export class UICanvas extends Component implements ICanvasElement {
/** @internal */
@ignoreClone
_isRootCanvas: boolean = false;
Expand All @@ -41,13 +33,10 @@ export class UICanvas extends Component implements IUIElement {
_hierarchyDirty: boolean = true;
/** @internal */
@ignoreClone
_indexInCanvas: number = -1;
/** @internal */
@ignoreClone
_group: UIGroup;
_rootCanvas: UICanvas;
/** @internal */
@ignoreClone
_indexInGroup: number = -1;
_indexInCanvas: number = -1;
/** @internal */
@ignoreClone
_renderElement: RenderElement;
Expand All @@ -56,16 +45,11 @@ export class UICanvas extends Component implements IUIElement {
_sortDistance: number = 0;
/** @internal */
@ignoreClone
_disorderedElements: DisorderedArray<IUIElement> = new DisorderedArray();
_disorderedElements: DisorderedArray<ICanvasElement> = new DisorderedArray();
/** @internal */
@ignoreClone
_orderedElements: IUIElement[] = [];
/** @internal */
@ignoreClone
_runtimeRaycastEnable: boolean = true;
_orderedElements: IUIGraphics[] = [];

@ignoreClone
private _raycastEnable: boolean = true;
@ignoreClone
private _renderMode = CanvasRenderMode.WorldSpace;
@ignoreClone
Expand All @@ -86,7 +70,7 @@ export class UICanvas extends Component implements IUIElement {
private _referenceResolution: Vector2 = new Vector2(800, 600);

/** @internal */
get elements(): IUIElement[] {
get elements(): IUIGraphics[] {
const elements = this._orderedElements;
if (this._hierarchyDirty) {
elements.length = this._walk(this.entity, elements);
Expand All @@ -95,21 +79,6 @@ export class UICanvas extends Component implements IUIElement {
return elements;
}

get raycastEnable(): boolean {
return this._raycastEnable;
}

set raycastEnable(val: boolean) {
if (this._raycastEnable !== val) {
this._raycastEnable = val;
const runtimeRaycastEnable = val && (!this._group || this._group._getGlobalRaycastEnable());
if (this._runtimeRaycastEnable !== runtimeRaycastEnable) {
this._runtimeRaycastEnable = runtimeRaycastEnable;
this._entity._onUIInteractiveChange(runtimeRaycastEnable);
}
}
}

get referenceResolution(): Vector2 {
return this._referenceResolution;
}
Expand Down Expand Up @@ -203,7 +172,7 @@ export class UICanvas extends Component implements IUIElement {
const { elements } = this;
for (let i = elements.length - 1; i >= 0; i--) {
const element = elements[i];
if (element._runtimeRaycastEnable && element._raycast(ray, out, distance)) {
if (element.raycastEnable && element._raycast(ray, out, distance)) {
return true;
}
}
Expand Down Expand Up @@ -283,107 +252,37 @@ export class UICanvas extends Component implements IUIElement {
override _onEnableInScene(): void {
const entity = this._entity;
entity._dispatchModify(EntityModifyFlags.UICanvasEnableInScene);
UIUtils.registerUIToGroup(this, UIUtils.getGroupInParents(entity));
const rootCanvas = UIUtils.getRootCanvasInParent(entity);
UIUtils.registerUIToCanvas(this, rootCanvas);
UIUtils.registerElementToCanvas(this, rootCanvas);
this._setIsRootCanvas(!rootCanvas);
UIUtils.registerEntityListener(this);
entity._onUIInteractiveChange(this._runtimeRaycastEnable);
}

/**
* @internal
*/
override _onDisableInScene(): void {
UIUtils.registerUIToGroup(this, null);
UIUtils.registerUIToCanvas(this, null);
UIUtils.registerElementToCanvas(this, null);
this._setIsRootCanvas(false);
UIUtils.unRegisterEntityListener(this);
const entity = this._entity;
entity._dispatchModify(EntityModifyFlags.UICanvasDisableInScene);
entity._onUIInteractiveChange(false);
}

/**
* @internal
*/
_raycast(ray: Ray, out: HitResult, distance: number = Number.MAX_SAFE_INTEGER): boolean {
const transform = this._transform;
const plane = UIRenderer._tempPlane;
const normal = plane.normal.copyFrom(transform.worldForward);
plane.distance = -Vector3.dot(normal, transform.worldPosition);
const curDistance = ray.intersectPlane(plane);
if (curDistance >= 0 && curDistance < distance) {
const hitPointWorld = ray.getPoint(curDistance, UIRenderer._tempVec30);
const worldMatrixInv = UIRenderer._tempMat;
Matrix.invert(transform.worldMatrix, worldMatrixInv);
const localPosition = UIRenderer._tempVec31;
Vector3.transformCoordinate(hitPointWorld, worldMatrixInv, localPosition);
if (this._hitTest(localPosition)) {
out.distance = curDistance;
out.entity = this._entity;
out.component = this;
out.normal.copyFrom(normal);
out.point.copyFrom(hitPointWorld);
return true;
}
return true;
}
return false;
}

/**
* @internal
*/
@ignoreClone
_onEntityModify(flag: EntityModifyFlags): void {
switch (flag) {
case EntityModifyFlags.SiblingIndex:
this._rootCanvas && (this._rootCanvas._hierarchyDirty = true);
break;
case EntityModifyFlags.UICanvasEnableInScene:
case EntityModifyFlags.Parent:
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
rootCanvas && (rootCanvas._hierarchyDirty = true);
this._setIsRootCanvas(!rootCanvas);
UIUtils.registerUIToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
case EntityModifyFlags.UIGroupEnableInScene:
UIUtils.registerUIToGroup(this, UIUtils.getGroupInParents(this._entity));
break;
default:
break;
if (flag === EntityModifyFlags.UICanvasEnableInScene || flag === EntityModifyFlags.Parent) {
const rootCanvas = UIUtils.getRootCanvasInParent(this._entity);
this._setIsRootCanvas(!rootCanvas);
UIUtils.registerElementToCanvas(this, rootCanvas);
UIUtils.registerEntityListener(this);
}
}

/**
* @internal
*/
@ignoreClone
_onGroupModify(flag: GroupModifyFlags): void {
if (flag & GroupModifyFlags.RaycastEnable) {
const runtimeRaycastEnable = this.raycastEnable && this._group._getGlobalRaycastEnable();
if (this._runtimeRaycastEnable !== runtimeRaycastEnable) {
this._runtimeRaycastEnable = runtimeRaycastEnable;
this.entity._onUIInteractiveChange(runtimeRaycastEnable);
}
}
}

private _hitTest(localPosition: Vector3): boolean {
const { x, y } = localPosition;
const uiTransform = this._transform;
const { x: width, y: height } = uiTransform.size;
const { x: pivotX, y: pivotY } = uiTransform.pivot;
const { x: paddingLeft, y: paddingBottom, z: paddingRight, w: paddingTop } = this.raycastPadding;
return (
x >= -width * pivotX + paddingLeft &&
x <= width * (1 - pivotX) - paddingRight &&
y >= -height * pivotY + paddingTop &&
y <= height * (1 - pivotY) - paddingBottom
);
}

private _adapterPoseInScreenSpace(): void {
const { _transform: transform, _realRenderMode: realRenderMode } = this;
if (realRenderMode === CanvasRenderMode.ScreenSpaceCamera) {
Expand Down Expand Up @@ -445,13 +344,13 @@ export class UICanvas extends Component implements IUIElement {
transform.size.set(curWidth / expectX, curHeight / expectY);
}

private _walk(entity: Entity, elements: IUIElement[], depth = 0): number {
private _walk(entity: Entity, elements: IUIGraphics[], depth = 0): number {
const { _components: components, _children: children } = entity;
for (let i = 0, n = components.length; i < n; i++) {
const component = components[i];
if (component.enabled && component._componentType & ComponentType.UIElement) {
(component as unknown as IUIElement).depth = depth;
elements[depth] = component as unknown as IUIElement;
if (component.enabled && component._componentType === ComponentType.UIRenderer) {
(component as unknown as IUIGraphics).depth = depth;
elements[depth] = component as unknown as IUIGraphics;
++depth;
}
}
Expand Down Expand Up @@ -542,8 +441,8 @@ export class UICanvas extends Component implements IUIElement {
this._setRealRenderMode(this._getRealRenderMode());
if (!isRootCanvas) {
const { _disorderedElements: disorderedElements } = this;
disorderedElements.forEach((element: IUIElement) => {
UIUtils.registerUIToCanvas(element, UIUtils.getRootCanvasInParent(element._entity));
disorderedElements.forEach((element: IUIGraphics) => {
UIUtils.registerElementToCanvas(element, UIUtils.getRootCanvasInParent(element._entity));
});
disorderedElements.length = 0;
disorderedElements.garbageCollection();
Expand Down
Loading

0 comments on commit 4b2487d

Please sign in to comment.