@omnipad/core 0.1.1-alpha.1 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +301 -155
- package/dist/index.d.ts +301 -155
- package/dist/index.js +377 -38
- package/dist/index.mjs +373 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,17 +24,19 @@ var index_exports = {};
|
|
|
24
24
|
__export(index_exports, {
|
|
25
25
|
ACTION_TYPES: () => ACTION_TYPES,
|
|
26
26
|
BaseEntity: () => BaseEntity,
|
|
27
|
+
CMP_TYPES: () => CMP_TYPES,
|
|
27
28
|
CONTEXT: () => CONTEXT,
|
|
28
29
|
InputManager: () => InputManager,
|
|
29
30
|
InputZoneCore: () => InputZoneCore,
|
|
30
31
|
KEYS: () => KEYS,
|
|
31
32
|
KeyboardButtonCore: () => KeyboardButtonCore,
|
|
33
|
+
MouseButtonCore: () => MouseButtonCore,
|
|
32
34
|
OmniPad: () => OmniPad,
|
|
33
35
|
Registry: () => Registry,
|
|
34
36
|
RootLayerCore: () => RootLayerCore,
|
|
35
37
|
SimpleEmitter: () => SimpleEmitter,
|
|
36
|
-
TYPES: () => TYPES,
|
|
37
38
|
TargetZoneCore: () => TargetZoneCore,
|
|
39
|
+
TrackpadCore: () => TrackpadCore,
|
|
38
40
|
addVec: () => addVec,
|
|
39
41
|
applyAxialDeadzone: () => applyAxialDeadzone,
|
|
40
42
|
applyRadialDeadzone: () => applyRadialDeadzone,
|
|
@@ -187,7 +189,7 @@ var STANDARD_KEYS = {
|
|
|
187
189
|
var KEYS = STANDARD_KEYS;
|
|
188
190
|
|
|
189
191
|
// src/types/index.ts
|
|
190
|
-
var
|
|
192
|
+
var CMP_TYPES = {
|
|
191
193
|
// --- Zones ---
|
|
192
194
|
/** Area responsible for capturing touches and spawning dynamic widgets */
|
|
193
195
|
INPUT_ZONE: "input-zone",
|
|
@@ -322,14 +324,17 @@ var applyAxialDeadzone = (v, threshold, max) => {
|
|
|
322
324
|
};
|
|
323
325
|
|
|
324
326
|
// src/utils/dom.ts
|
|
325
|
-
var getDeepElement = (x, y) => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
327
|
+
var getDeepElement = (x, y, ignoreClass = "omnipad-target-zone") => {
|
|
328
|
+
const elements = document.elementsFromPoint(x, y);
|
|
329
|
+
let target = elements.find((el) => !el.classList.contains(ignoreClass));
|
|
330
|
+
if (!target) return null;
|
|
331
|
+
while (target && target.shadowRoot) {
|
|
332
|
+
const nestedElements = target.shadowRoot.elementsFromPoint(x, y);
|
|
333
|
+
const nestedTarget = nestedElements.find((el) => !el.classList.contains(ignoreClass));
|
|
334
|
+
if (!nestedTarget || nestedTarget === target) break;
|
|
335
|
+
target = nestedTarget;
|
|
336
|
+
}
|
|
337
|
+
return target;
|
|
333
338
|
};
|
|
334
339
|
var getDeepActiveElement = () => {
|
|
335
340
|
let el = document.activeElement;
|
|
@@ -373,7 +378,7 @@ var dispatchPointerEventAtPos = (type, x, y, opts = {}) => {
|
|
|
373
378
|
target.dispatchEvent(
|
|
374
379
|
new PointerEvent(type, {
|
|
375
380
|
isPrimary: true,
|
|
376
|
-
pointerId:
|
|
381
|
+
pointerId: 9999,
|
|
377
382
|
pointerType: "mouse",
|
|
378
383
|
// Emulate mouse behavior for Flash MouseOver/Down logic
|
|
379
384
|
...commonProps
|
|
@@ -706,6 +711,24 @@ var SimpleEmitter = class {
|
|
|
706
711
|
}
|
|
707
712
|
};
|
|
708
713
|
|
|
714
|
+
// src/utils/performance.ts
|
|
715
|
+
function createRafThrottler(callback) {
|
|
716
|
+
let ticking = false;
|
|
717
|
+
let latestPayload = null;
|
|
718
|
+
return function(payload) {
|
|
719
|
+
latestPayload = payload;
|
|
720
|
+
if (!ticking) {
|
|
721
|
+
ticking = true;
|
|
722
|
+
window.requestAnimationFrame(() => {
|
|
723
|
+
if (latestPayload !== null) {
|
|
724
|
+
callback(latestPayload);
|
|
725
|
+
}
|
|
726
|
+
ticking = false;
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
709
732
|
// src/imputManager/index.ts
|
|
710
733
|
var import_meta2 = {};
|
|
711
734
|
var INPUT_MANAGER_KEY = /* @__PURE__ */ Symbol.for("omnipad.input_manager.instance");
|
|
@@ -713,6 +736,8 @@ var InputManager = class _InputManager {
|
|
|
713
736
|
constructor() {
|
|
714
737
|
/** Internal flag to prevent multiple event registrations */
|
|
715
738
|
__publicField(this, "_isListening", false);
|
|
739
|
+
/** A throttled version of the reset logic */
|
|
740
|
+
__publicField(this, "throttledReset");
|
|
716
741
|
/**
|
|
717
742
|
* Manually triggers a system-wide input reset via Registry.
|
|
718
743
|
*/
|
|
@@ -722,6 +747,20 @@ var InputManager = class _InputManager {
|
|
|
722
747
|
}
|
|
723
748
|
Registry.getInstance().resetAll();
|
|
724
749
|
});
|
|
750
|
+
__publicField(this, "handleResizeReset", () => {
|
|
751
|
+
this.throttledReset(null);
|
|
752
|
+
});
|
|
753
|
+
__publicField(this, "handleBlurReset", () => {
|
|
754
|
+
this.handleGlobalReset();
|
|
755
|
+
});
|
|
756
|
+
__publicField(this, "handleVisibilityChangeReset", () => {
|
|
757
|
+
if (document.visibilityState === "hidden") {
|
|
758
|
+
this.handleGlobalReset();
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
this.throttledReset = createRafThrottler(() => {
|
|
762
|
+
this.handleGlobalReset();
|
|
763
|
+
});
|
|
725
764
|
}
|
|
726
765
|
/**
|
|
727
766
|
* Retrieves the global instance of the InputManager.
|
|
@@ -740,8 +779,9 @@ var InputManager = class _InputManager {
|
|
|
740
779
|
*/
|
|
741
780
|
init() {
|
|
742
781
|
if (this._isListening) return;
|
|
743
|
-
window.addEventListener("resize", this.
|
|
744
|
-
window.addEventListener("blur", this.
|
|
782
|
+
window.addEventListener("resize", this.handleResizeReset);
|
|
783
|
+
window.addEventListener("blur", this.handleBlurReset);
|
|
784
|
+
document.addEventListener("visibilitychange", this.handleVisibilityChangeReset);
|
|
745
785
|
this._isListening = true;
|
|
746
786
|
if (import_meta2.env?.DEV) {
|
|
747
787
|
console.log("[OmniPad-Core] Global InputManager monitoring started.");
|
|
@@ -775,8 +815,9 @@ var InputManager = class _InputManager {
|
|
|
775
815
|
* Detaches all global listeners.
|
|
776
816
|
*/
|
|
777
817
|
destroy() {
|
|
778
|
-
window.removeEventListener("resize", this.
|
|
779
|
-
window.removeEventListener("blur", this.
|
|
818
|
+
window.removeEventListener("resize", this.handleResizeReset);
|
|
819
|
+
window.removeEventListener("blur", this.handleBlurReset);
|
|
820
|
+
window.removeEventListener("visibilitychange", this.handleVisibilityChangeReset);
|
|
780
821
|
this._isListening = false;
|
|
781
822
|
}
|
|
782
823
|
};
|
|
@@ -788,7 +829,7 @@ var BaseEntity = class {
|
|
|
788
829
|
__publicField(this, "type");
|
|
789
830
|
__publicField(this, "config");
|
|
790
831
|
__publicField(this, "state");
|
|
791
|
-
__publicField(this, "
|
|
832
|
+
__publicField(this, "rectProvider", null);
|
|
792
833
|
// 内部状态发射器,负责处理状态订阅逻辑 / Internal emitter for state subscription logic
|
|
793
834
|
__publicField(this, "stateEmitter", new SimpleEmitter());
|
|
794
835
|
this.uid = uid;
|
|
@@ -817,8 +858,14 @@ var BaseEntity = class {
|
|
|
817
858
|
this.stateEmitter.clear();
|
|
818
859
|
Registry.getInstance().unregister(this.uid);
|
|
819
860
|
}
|
|
820
|
-
|
|
821
|
-
this.
|
|
861
|
+
bindRectProvider(provider) {
|
|
862
|
+
this.rectProvider = provider;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Called when triggering interactions, this subclass fetches the latest boundaries in real time.
|
|
866
|
+
*/
|
|
867
|
+
getRect() {
|
|
868
|
+
return this.rectProvider ? this.rectProvider() : null;
|
|
822
869
|
}
|
|
823
870
|
updateConfig(newConfig) {
|
|
824
871
|
this.config = { ...this.config, ...newConfig };
|
|
@@ -840,12 +887,13 @@ var INITIAL_STATE = {
|
|
|
840
887
|
};
|
|
841
888
|
var InputZoneCore = class extends BaseEntity {
|
|
842
889
|
constructor(uid, config) {
|
|
843
|
-
super(uid,
|
|
890
|
+
super(uid, CMP_TYPES.INPUT_ZONE, config, INITIAL_STATE);
|
|
844
891
|
}
|
|
845
892
|
onPointerDown(e) {
|
|
846
893
|
if (this.state.isDynamicActive) return;
|
|
847
894
|
if (e.target !== e.currentTarget) return;
|
|
848
895
|
if (e.cancelable) e.preventDefault();
|
|
896
|
+
e.stopPropagation();
|
|
849
897
|
const pos = this.calculateRelativePosition(e.clientX, e.clientY);
|
|
850
898
|
this.setState({
|
|
851
899
|
isDynamicActive: true,
|
|
@@ -857,6 +905,7 @@ var InputZoneCore = class extends BaseEntity {
|
|
|
857
905
|
if (!this.state.isDynamicActive || e.pointerId !== this.state.dynamicPointerId) return;
|
|
858
906
|
}
|
|
859
907
|
onPointerUp(e) {
|
|
908
|
+
if (e.cancelable) e.preventDefault();
|
|
860
909
|
this.handleRelease(e);
|
|
861
910
|
}
|
|
862
911
|
onPointerCancel(e) {
|
|
@@ -882,10 +931,11 @@ var InputZoneCore = class extends BaseEntity {
|
|
|
882
931
|
* Converts viewport pixels to percentage coordinates relative to the zone.
|
|
883
932
|
*/
|
|
884
933
|
calculateRelativePosition(clientX, clientY) {
|
|
885
|
-
|
|
934
|
+
const rect = this.getRect();
|
|
935
|
+
if (!rect) return { x: 0, y: 0 };
|
|
886
936
|
return {
|
|
887
|
-
x: pxToPercent(clientX -
|
|
888
|
-
y: pxToPercent(clientY -
|
|
937
|
+
x: pxToPercent(clientX - rect.left, rect.width),
|
|
938
|
+
y: pxToPercent(clientY - rect.top, rect.height)
|
|
889
939
|
};
|
|
890
940
|
}
|
|
891
941
|
/**
|
|
@@ -918,11 +968,12 @@ var KeyboardButtonCore = class extends BaseEntity {
|
|
|
918
968
|
* @param config - The flat configuration object for the button.
|
|
919
969
|
*/
|
|
920
970
|
constructor(uid, config) {
|
|
921
|
-
super(uid,
|
|
971
|
+
super(uid, CMP_TYPES.KEYBOARD_BUTTON, config, INITIAL_STATE2);
|
|
922
972
|
}
|
|
923
973
|
// --- IPointerHandler Implementation ---
|
|
924
974
|
onPointerDown(e) {
|
|
925
975
|
if (e.cancelable) e.preventDefault();
|
|
976
|
+
e.stopPropagation();
|
|
926
977
|
e.target.setPointerCapture(e.pointerId);
|
|
927
978
|
this.setState({
|
|
928
979
|
isActive: true,
|
|
@@ -977,7 +1028,7 @@ var KeyboardButtonCore = class extends BaseEntity {
|
|
|
977
1028
|
target.handleSignal(signal);
|
|
978
1029
|
} else {
|
|
979
1030
|
if (import_meta3.env?.DEV) {
|
|
980
|
-
console.warn(`[OmniPad-Core]
|
|
1031
|
+
console.warn(`[OmniPad-Core] KeyboardButton ${this.uid} target not found: ${targetId}`);
|
|
981
1032
|
}
|
|
982
1033
|
}
|
|
983
1034
|
}
|
|
@@ -990,20 +1041,109 @@ var KeyboardButtonCore = class extends BaseEntity {
|
|
|
990
1041
|
}
|
|
991
1042
|
};
|
|
992
1043
|
|
|
993
|
-
// src/entities/
|
|
1044
|
+
// src/entities/MouseButtonCore.ts
|
|
1045
|
+
var import_meta4 = {};
|
|
994
1046
|
var INITIAL_STATE3 = {
|
|
1047
|
+
isActive: false,
|
|
1048
|
+
isPressed: false,
|
|
1049
|
+
pointerId: null,
|
|
1050
|
+
value: 0
|
|
1051
|
+
};
|
|
1052
|
+
var MouseButtonCore = class extends BaseEntity {
|
|
1053
|
+
constructor(uid, config) {
|
|
1054
|
+
super(uid, CMP_TYPES.MOUSE_BUTTON, config, INITIAL_STATE3);
|
|
1055
|
+
}
|
|
1056
|
+
// --- IPointerHandler Implementation ---
|
|
1057
|
+
onPointerDown(e) {
|
|
1058
|
+
if (e.cancelable) e.preventDefault();
|
|
1059
|
+
e.stopPropagation();
|
|
1060
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1061
|
+
this.setState({
|
|
1062
|
+
isActive: true,
|
|
1063
|
+
isPressed: true,
|
|
1064
|
+
pointerId: e.pointerId
|
|
1065
|
+
});
|
|
1066
|
+
this.sendInputSignal(ACTION_TYPES.MOUSEDOWN);
|
|
1067
|
+
}
|
|
1068
|
+
onPointerUp(e) {
|
|
1069
|
+
if (e.cancelable) e.preventDefault();
|
|
1070
|
+
this.handleRelease(e, true);
|
|
1071
|
+
}
|
|
1072
|
+
onPointerCancel(e) {
|
|
1073
|
+
this.handleRelease(e, false);
|
|
1074
|
+
}
|
|
1075
|
+
onPointerMove(e) {
|
|
1076
|
+
if (e.cancelable) e.preventDefault();
|
|
1077
|
+
}
|
|
1078
|
+
// --- Internal Logic ---
|
|
1079
|
+
/**
|
|
1080
|
+
* Handles the release of the button.
|
|
1081
|
+
*
|
|
1082
|
+
* @param e - The pointer event.
|
|
1083
|
+
* @param isNormalRelease - If true, a 'click' event will also be dispatched.
|
|
1084
|
+
*/
|
|
1085
|
+
handleRelease(e, isNormalRelease) {
|
|
1086
|
+
const isCancelEvent = e.type === "pointercancel" || e.type === "lostpointercapture";
|
|
1087
|
+
if (!isCancelEvent && this.state.pointerId !== e.pointerId) return;
|
|
1088
|
+
if (e.target.hasPointerCapture(e.pointerId)) {
|
|
1089
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
1090
|
+
}
|
|
1091
|
+
this.setState(INITIAL_STATE3);
|
|
1092
|
+
this.sendInputSignal(ACTION_TYPES.MOUSEUP);
|
|
1093
|
+
if (isNormalRelease) {
|
|
1094
|
+
this.sendInputSignal(ACTION_TYPES.CLICK);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Dispatches input signals to the registered target stage.
|
|
1099
|
+
*
|
|
1100
|
+
* @param type - The action type (mousedown, mouseup, or click).
|
|
1101
|
+
*/
|
|
1102
|
+
sendInputSignal(type) {
|
|
1103
|
+
const targetId = this.config.targetStageId;
|
|
1104
|
+
if (!targetId) return;
|
|
1105
|
+
const target = Registry.getInstance().getEntity(targetId);
|
|
1106
|
+
if (target && typeof target.handleSignal === "function") {
|
|
1107
|
+
const signal = {
|
|
1108
|
+
targetStageId: targetId,
|
|
1109
|
+
type,
|
|
1110
|
+
payload: {
|
|
1111
|
+
// 传递配置中的鼠标按键索引 (0:左键, 1:中键, 2:右键)
|
|
1112
|
+
button: this.config.button ?? 0,
|
|
1113
|
+
// 如果配置了固定坐标,一并传递给 TargetZone
|
|
1114
|
+
point: this.config.fixedPoint
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
target.handleSignal(signal);
|
|
1118
|
+
} else {
|
|
1119
|
+
if (import_meta4.env?.DEV) {
|
|
1120
|
+
console.warn(`[OmniPad-Core] MouseButton ${this.uid} target not found: ${targetId}`);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
// --- IResettable Implementation ---
|
|
1125
|
+
reset() {
|
|
1126
|
+
if (this.state.isPressed) {
|
|
1127
|
+
this.sendInputSignal(ACTION_TYPES.MOUSEUP);
|
|
1128
|
+
}
|
|
1129
|
+
this.setState(INITIAL_STATE3);
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
// src/entities/RootLayerCore.ts
|
|
1134
|
+
var INITIAL_STATE4 = {
|
|
995
1135
|
isHighlighted: false
|
|
996
1136
|
};
|
|
997
1137
|
var RootLayerCore = class extends BaseEntity {
|
|
998
1138
|
constructor(uid, config) {
|
|
999
|
-
super(uid,
|
|
1139
|
+
super(uid, CMP_TYPES.ROOT_LAYER, config, INITIAL_STATE4);
|
|
1000
1140
|
}
|
|
1001
1141
|
reset() {
|
|
1002
1142
|
}
|
|
1003
1143
|
};
|
|
1004
1144
|
|
|
1005
1145
|
// src/entities/TargetZoneCore.ts
|
|
1006
|
-
var
|
|
1146
|
+
var INITIAL_STATE5 = {
|
|
1007
1147
|
position: { x: 50, y: 50 },
|
|
1008
1148
|
isVisible: false,
|
|
1009
1149
|
isPointerDown: false,
|
|
@@ -1011,9 +1151,48 @@ var INITIAL_STATE4 = {
|
|
|
1011
1151
|
};
|
|
1012
1152
|
var TargetZoneCore = class extends BaseEntity {
|
|
1013
1153
|
constructor(uid, config) {
|
|
1014
|
-
super(uid,
|
|
1154
|
+
super(uid, CMP_TYPES.TARGET_ZONE, config, INITIAL_STATE5);
|
|
1015
1155
|
__publicField(this, "hideTimer", null);
|
|
1016
1156
|
__publicField(this, "focusFeedbackTimer", null);
|
|
1157
|
+
__publicField(this, "throttledPointerMove");
|
|
1158
|
+
this.throttledPointerMove = createRafThrottler((e) => {
|
|
1159
|
+
this.processPhysicalEvent(e, ACTION_TYPES.MOUSEMOVE);
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
// --- IPointerHandler Implementation ---
|
|
1163
|
+
onPointerDown(e) {
|
|
1164
|
+
if (e.cancelable) e.preventDefault();
|
|
1165
|
+
e.stopPropagation();
|
|
1166
|
+
this.processPhysicalEvent(e, ACTION_TYPES.MOUSEDOWN);
|
|
1167
|
+
}
|
|
1168
|
+
onPointerMove(e) {
|
|
1169
|
+
if (e.cancelable) e.preventDefault();
|
|
1170
|
+
e.stopPropagation();
|
|
1171
|
+
this.throttledPointerMove(e);
|
|
1172
|
+
}
|
|
1173
|
+
onPointerUp(e) {
|
|
1174
|
+
if (e.cancelable) e.preventDefault();
|
|
1175
|
+
this.processPhysicalEvent(e, ACTION_TYPES.MOUSEUP);
|
|
1176
|
+
this.processPhysicalEvent(e, ACTION_TYPES.CLICK);
|
|
1177
|
+
}
|
|
1178
|
+
onPointerCancel(e) {
|
|
1179
|
+
this.processPhysicalEvent(e, ACTION_TYPES.MOUSEUP);
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Convert physical DOM events into internal signals
|
|
1183
|
+
*/
|
|
1184
|
+
processPhysicalEvent(e, type) {
|
|
1185
|
+
const rect = this.getRect();
|
|
1186
|
+
if (!rect) return;
|
|
1187
|
+
const point = {
|
|
1188
|
+
x: pxToPercent(e.clientX - rect.left, rect.width),
|
|
1189
|
+
y: pxToPercent(e.clientY - rect.top, rect.height)
|
|
1190
|
+
};
|
|
1191
|
+
this.handleSignal({
|
|
1192
|
+
targetStageId: this.uid,
|
|
1193
|
+
type,
|
|
1194
|
+
payload: { point, button: e.button }
|
|
1195
|
+
});
|
|
1017
1196
|
}
|
|
1018
1197
|
// --- ISignalReceiver Implementation ---
|
|
1019
1198
|
handleSignal(signal) {
|
|
@@ -1027,13 +1206,18 @@ var TargetZoneCore = class extends BaseEntity {
|
|
|
1027
1206
|
case ACTION_TYPES.MOUSEMOVE:
|
|
1028
1207
|
if (payload.point) {
|
|
1029
1208
|
this.updateCursorPosition(payload.point);
|
|
1030
|
-
|
|
1031
|
-
this.
|
|
1209
|
+
} else if (payload.delta) {
|
|
1210
|
+
this.updateCursorPositionByDelta(payload.delta);
|
|
1032
1211
|
}
|
|
1212
|
+
if (this.config.cursorEnabled) this.showCursor();
|
|
1213
|
+
this.executeMouseAction(ACTION_TYPES.POINTERMOVE, payload);
|
|
1033
1214
|
break;
|
|
1034
1215
|
case ACTION_TYPES.MOUSEDOWN:
|
|
1035
1216
|
case ACTION_TYPES.MOUSEUP:
|
|
1036
1217
|
case ACTION_TYPES.CLICK:
|
|
1218
|
+
if (payload.point) {
|
|
1219
|
+
this.updateCursorPosition(payload.point);
|
|
1220
|
+
}
|
|
1037
1221
|
if (this.config.cursorEnabled) this.showCursor();
|
|
1038
1222
|
this.executeMouseAction(
|
|
1039
1223
|
type.startsWith(ACTION_TYPES.MOUSE) ? type.replace(ACTION_TYPES.MOUSE, ACTION_TYPES.POINTER) : type,
|
|
@@ -1050,12 +1234,13 @@ var TargetZoneCore = class extends BaseEntity {
|
|
|
1050
1234
|
* @param payload - Data containing point coordinates or button info.
|
|
1051
1235
|
*/
|
|
1052
1236
|
executeMouseAction(pointerType, payload) {
|
|
1053
|
-
|
|
1237
|
+
const rect = this.getRect();
|
|
1238
|
+
if (!rect) return;
|
|
1054
1239
|
if (pointerType === ACTION_TYPES.POINTERDOWN) this.setState({ isPointerDown: true });
|
|
1055
1240
|
if (pointerType === ACTION_TYPES.POINTERUP) this.setState({ isPointerDown: false });
|
|
1056
1241
|
const target = payload.point || this.state.position;
|
|
1057
|
-
const px =
|
|
1058
|
-
const py =
|
|
1242
|
+
const px = rect.left + percentToPx(target.x, rect.width);
|
|
1243
|
+
const py = rect.top + percentToPx(target.y, rect.height);
|
|
1059
1244
|
dispatchPointerEventAtPos(pointerType, px, py, {
|
|
1060
1245
|
button: payload.button ?? 0,
|
|
1061
1246
|
buttons: this.state.isPointerDown ? 1 : 0
|
|
@@ -1066,9 +1251,10 @@ var TargetZoneCore = class extends BaseEntity {
|
|
|
1066
1251
|
* Checks if the target element under the virtual cursor has focus, and reclaims it if lost.
|
|
1067
1252
|
*/
|
|
1068
1253
|
ensureFocus() {
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
const
|
|
1254
|
+
const rect = this.getRect();
|
|
1255
|
+
if (!rect) return;
|
|
1256
|
+
const px = rect.left + percentToPx(this.state.position.x, rect.width);
|
|
1257
|
+
const py = rect.top + percentToPx(this.state.position.y, rect.height);
|
|
1072
1258
|
const target = getDeepElement(px, py);
|
|
1073
1259
|
if (!target) return;
|
|
1074
1260
|
if (getDeepActiveElement() !== target) {
|
|
@@ -1089,8 +1275,23 @@ var TargetZoneCore = class extends BaseEntity {
|
|
|
1089
1275
|
* Updates the internal virtual cursor coordinates.
|
|
1090
1276
|
*/
|
|
1091
1277
|
updateCursorPosition(point) {
|
|
1278
|
+
if (isVec2Equal(point, this.state.position)) return;
|
|
1092
1279
|
this.setState({ position: { ...point } });
|
|
1093
1280
|
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Updates the internal virtual cursor coordinates by delta.
|
|
1283
|
+
*/
|
|
1284
|
+
updateCursorPositionByDelta(delta) {
|
|
1285
|
+
if (isVec2Equal(delta, { x: 0, y: 0 })) return;
|
|
1286
|
+
const rect = this.getRect();
|
|
1287
|
+
if (!rect) return;
|
|
1288
|
+
const dxPercent = pxToPercent(delta.x, rect.width);
|
|
1289
|
+
const dyPercent = pxToPercent(delta.y, rect.height);
|
|
1290
|
+
this.updateCursorPosition({
|
|
1291
|
+
x: clamp(this.state.position.x + dxPercent, 0, 100),
|
|
1292
|
+
y: clamp(this.state.position.y + dyPercent, 0, 100)
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1094
1295
|
/**
|
|
1095
1296
|
* Makes the virtual cursor visible and sets a timeout for auto-hiding.
|
|
1096
1297
|
*/
|
|
@@ -1119,27 +1320,165 @@ var TargetZoneCore = class extends BaseEntity {
|
|
|
1119
1320
|
}
|
|
1120
1321
|
};
|
|
1121
1322
|
|
|
1323
|
+
// src/entities/TrackpadCore.ts
|
|
1324
|
+
var GESTURE = {
|
|
1325
|
+
TAP_TIME: 200,
|
|
1326
|
+
// 200ms 以内抬起视为轻点 / Release within 200ms counts as a tap
|
|
1327
|
+
TAP_DISTANCE: 10,
|
|
1328
|
+
// 位移小于 10px 视为点击判定 / Movement within 10px counts as a tap
|
|
1329
|
+
DOUBLE_TAP_GAP: 300
|
|
1330
|
+
// 两次点击间隔小于 300ms 视为双击触发 / Interval within 300ms counts as double-tap
|
|
1331
|
+
};
|
|
1332
|
+
var INITIAL_STATE6 = {
|
|
1333
|
+
isActive: false,
|
|
1334
|
+
isPressed: false,
|
|
1335
|
+
pointerId: null,
|
|
1336
|
+
value: 0
|
|
1337
|
+
};
|
|
1338
|
+
var TrackpadCore = class extends BaseEntity {
|
|
1339
|
+
/**
|
|
1340
|
+
* Creates an instance of TrackpadCore.
|
|
1341
|
+
*
|
|
1342
|
+
* @param uid - Unique entity ID.
|
|
1343
|
+
* @param config - Configuration for the trackpad.
|
|
1344
|
+
*/
|
|
1345
|
+
constructor(uid, config) {
|
|
1346
|
+
super(uid, CMP_TYPES.TRACKPAD, config, INITIAL_STATE6);
|
|
1347
|
+
__publicField(this, "lastPointerPos", { x: 0, y: 0 });
|
|
1348
|
+
__publicField(this, "startTime", 0);
|
|
1349
|
+
__publicField(this, "startPos", { x: 0, y: 0 });
|
|
1350
|
+
// 连击状态追踪 / State tracking for consecutive taps
|
|
1351
|
+
__publicField(this, "lastClickTime", 0);
|
|
1352
|
+
__publicField(this, "isDragMode", false);
|
|
1353
|
+
__publicField(this, "throttledPointerMove");
|
|
1354
|
+
this.throttledPointerMove = createRafThrottler((e) => {
|
|
1355
|
+
this.processPointerMove(e);
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
// --- IPointerHandler Implementation ---
|
|
1359
|
+
onPointerDown(e) {
|
|
1360
|
+
if (e.cancelable) e.preventDefault();
|
|
1361
|
+
e.stopPropagation();
|
|
1362
|
+
e.target.setPointerCapture(e.pointerId);
|
|
1363
|
+
const now = Date.now();
|
|
1364
|
+
this.startTime = now;
|
|
1365
|
+
this.startPos = { x: e.clientX, y: e.clientY };
|
|
1366
|
+
this.lastPointerPos = { x: e.clientX, y: e.clientY };
|
|
1367
|
+
if (now - this.lastClickTime < GESTURE.DOUBLE_TAP_GAP) {
|
|
1368
|
+
this.isDragMode = true;
|
|
1369
|
+
this.sendSignal(ACTION_TYPES.MOUSEDOWN);
|
|
1370
|
+
this.setState({ isPressed: true });
|
|
1371
|
+
}
|
|
1372
|
+
this.setState({ isActive: true, pointerId: e.pointerId });
|
|
1373
|
+
}
|
|
1374
|
+
onPointerMove(e) {
|
|
1375
|
+
if (e.cancelable) e.preventDefault();
|
|
1376
|
+
e.stopPropagation();
|
|
1377
|
+
if (this.state.pointerId !== e.pointerId) return;
|
|
1378
|
+
this.throttledPointerMove(e);
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Internal logic for processing pointer movement.
|
|
1382
|
+
* Calculates displacement and emits relative move signals.
|
|
1383
|
+
*
|
|
1384
|
+
* @param e - The pointer event.
|
|
1385
|
+
*/
|
|
1386
|
+
processPointerMove(e) {
|
|
1387
|
+
const dx = e.clientX - this.lastPointerPos.x;
|
|
1388
|
+
const dy = e.clientY - this.lastPointerPos.y;
|
|
1389
|
+
const rect = this.getRect();
|
|
1390
|
+
if (!rect) return;
|
|
1391
|
+
const deltaX = dx / rect.width * 100 * this.config.sensitivity;
|
|
1392
|
+
const deltaY = dy / rect.height * 100 * this.config.sensitivity;
|
|
1393
|
+
if (Math.abs(dx) > 0 || Math.abs(dy) > 0) {
|
|
1394
|
+
this.sendSignal(ACTION_TYPES.MOUSEMOVE, { delta: { x: deltaX, y: deltaY } });
|
|
1395
|
+
}
|
|
1396
|
+
this.lastPointerPos = { x: e.clientX, y: e.clientY };
|
|
1397
|
+
}
|
|
1398
|
+
onPointerUp(e) {
|
|
1399
|
+
if (this.state.pointerId !== e.pointerId) return;
|
|
1400
|
+
if (e.cancelable) e.preventDefault();
|
|
1401
|
+
const duration = Date.now() - this.startTime;
|
|
1402
|
+
const dist = Math.hypot(e.clientX - this.startPos.x, e.clientY - this.startPos.y);
|
|
1403
|
+
if (this.isDragMode) {
|
|
1404
|
+
this.sendSignal(ACTION_TYPES.MOUSEUP);
|
|
1405
|
+
this.isDragMode = false;
|
|
1406
|
+
} else if (duration < GESTURE.TAP_TIME && dist < GESTURE.TAP_DISTANCE) {
|
|
1407
|
+
this.sendSignal(ACTION_TYPES.CLICK);
|
|
1408
|
+
this.lastClickTime = Date.now();
|
|
1409
|
+
}
|
|
1410
|
+
this.handleRelease(e);
|
|
1411
|
+
}
|
|
1412
|
+
onPointerCancel(e) {
|
|
1413
|
+
this.handleRelease(e);
|
|
1414
|
+
}
|
|
1415
|
+
// --- IResettable Implementation ---
|
|
1416
|
+
reset() {
|
|
1417
|
+
if (this.isDragMode) this.sendSignal(ACTION_TYPES.MOUSEUP);
|
|
1418
|
+
this.isDragMode = false;
|
|
1419
|
+
this.setState(INITIAL_STATE6);
|
|
1420
|
+
}
|
|
1421
|
+
// --- Internal Helpers ---
|
|
1422
|
+
/**
|
|
1423
|
+
* Clean up pointer capture and reset interaction state.
|
|
1424
|
+
*/
|
|
1425
|
+
handleRelease(e) {
|
|
1426
|
+
if (e.target.hasPointerCapture(e.pointerId)) {
|
|
1427
|
+
try {
|
|
1428
|
+
e.target.releasePointerCapture(e.pointerId);
|
|
1429
|
+
} catch (err) {
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
this.setState(INITIAL_STATE6);
|
|
1433
|
+
}
|
|
1434
|
+
/**
|
|
1435
|
+
* Helper to send signals to the target stage via Registry.
|
|
1436
|
+
*
|
|
1437
|
+
* @param type - Signal action type.
|
|
1438
|
+
* @param extraPayload - Additional data like delta or point.
|
|
1439
|
+
*/
|
|
1440
|
+
sendSignal(type, extraPayload = {}) {
|
|
1441
|
+
const targetId = this.config.targetStageId;
|
|
1442
|
+
if (!targetId) return;
|
|
1443
|
+
const target = Registry.getInstance().getEntity(targetId);
|
|
1444
|
+
if (target && typeof target.handleSignal === "function") {
|
|
1445
|
+
target.handleSignal({
|
|
1446
|
+
targetStageId: targetId,
|
|
1447
|
+
type,
|
|
1448
|
+
payload: {
|
|
1449
|
+
button: 0,
|
|
1450
|
+
// 触摸板操作默认模拟左键 / Trackpad defaults to left button
|
|
1451
|
+
...extraPayload
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1122
1458
|
// src/index.ts
|
|
1123
1459
|
var OmniPad = {
|
|
1460
|
+
ActionTypes: ACTION_TYPES,
|
|
1124
1461
|
Context: CONTEXT,
|
|
1125
1462
|
Keys: KEYS,
|
|
1126
|
-
Types:
|
|
1463
|
+
Types: CMP_TYPES
|
|
1127
1464
|
};
|
|
1128
1465
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1129
1466
|
0 && (module.exports = {
|
|
1130
1467
|
ACTION_TYPES,
|
|
1131
1468
|
BaseEntity,
|
|
1469
|
+
CMP_TYPES,
|
|
1132
1470
|
CONTEXT,
|
|
1133
1471
|
InputManager,
|
|
1134
1472
|
InputZoneCore,
|
|
1135
1473
|
KEYS,
|
|
1136
1474
|
KeyboardButtonCore,
|
|
1475
|
+
MouseButtonCore,
|
|
1137
1476
|
OmniPad,
|
|
1138
1477
|
Registry,
|
|
1139
1478
|
RootLayerCore,
|
|
1140
1479
|
SimpleEmitter,
|
|
1141
|
-
TYPES,
|
|
1142
1480
|
TargetZoneCore,
|
|
1481
|
+
TrackpadCore,
|
|
1143
1482
|
addVec,
|
|
1144
1483
|
applyAxialDeadzone,
|
|
1145
1484
|
applyRadialDeadzone,
|