@netless/window-manager 0.4.70 → 0.4.71
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/AppManager.d.ts +2 -1
- package/dist/Cursor/Cursor.d.ts +7 -3
- package/dist/Cursor/icons2.d.ts +4 -0
- package/dist/Cursor/index.d.ts +9 -3
- package/dist/callback.d.ts +12 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +247 -50
- package/dist/index.mjs.map +1 -1
- package/dist/typings.d.ts +4 -0
- package/package.json +1 -1
- package/src/App/AppContext.ts +5 -0
- package/src/App/AppProxy.ts +8 -0
- package/src/AppManager.ts +31 -1
- package/src/Cursor/Cursor.svelte +9 -4
- package/src/Cursor/Cursor.svelte.d.ts +21 -0
- package/src/Cursor/Cursor.ts +75 -19
- package/src/Cursor/icons2.ts +66 -0
- package/src/Cursor/index.ts +84 -14
- package/src/callback.ts +12 -1
- package/src/index.ts +48 -15
- package/src/style.css +0 -1
- package/src/typings.ts +5 -0
package/dist/typings.d.ts
CHANGED
@@ -78,3 +78,7 @@ export type { SceneState, SceneDefinition, View, AnimationMode, Displayer, Room,
|
|
78
78
|
export type { Storage, StorageStateChangedEvent, StorageStateChangedListener } from "./App/Storage";
|
79
79
|
export * from "./Page";
|
80
80
|
export * from "./Utils/error";
|
81
|
+
export declare type AppPayload = {
|
82
|
+
appId: string;
|
83
|
+
view: View;
|
84
|
+
};
|
package/package.json
CHANGED
package/src/App/AppContext.ts
CHANGED
@@ -29,6 +29,8 @@ import type {
|
|
29
29
|
} from "./MagixEvent";
|
30
30
|
import type { AddPageParams, PageController, PageState } from "../Page";
|
31
31
|
import { internalEmitter } from "../InternalEmitter";
|
32
|
+
import { WindowManager } from "../index";
|
33
|
+
import { callbacks } from "../callback";
|
32
34
|
|
33
35
|
export class AppContext<TAttributes extends {} = any, TMagixEventPayloads = any, TAppOptions = any>
|
34
36
|
implements PageController
|
@@ -90,6 +92,9 @@ export class AppContext<TAttributes extends {} = any, TMagixEventPayloads = any,
|
|
90
92
|
setTimeout(() => {
|
91
93
|
// 渲染需要时间,延迟 refresh
|
92
94
|
this.getRoom()?.refreshViewSize();
|
95
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
96
|
+
callbacks.emit("onAppViewMounted", {appId: this.appId, view});
|
97
|
+
}
|
93
98
|
}, 1000);
|
94
99
|
}
|
95
100
|
};
|
package/src/App/AppProxy.ts
CHANGED
@@ -30,6 +30,8 @@ import type { ReadonlyTeleBox } from "@netless/telebox-insider";
|
|
30
30
|
import type { PageRemoveService, PageState } from "../Page";
|
31
31
|
import { calculateNextIndex } from "../Page";
|
32
32
|
import { boxEmitter } from "../BoxEmitter";
|
33
|
+
import { WindowManager } from "../index";
|
34
|
+
import { callbacks } from "../callback";
|
33
35
|
|
34
36
|
export type AppEmitter = Emittery<AppEmitterEvent>;
|
35
37
|
|
@@ -194,6 +196,9 @@ export class AppProxy implements PageRemoveService {
|
|
194
196
|
appRegister.notifyApp(this.kind, "created", { appId, result });
|
195
197
|
this.afterSetupApp(boxInitState);
|
196
198
|
this.fixMobileSize();
|
199
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
200
|
+
callbacks.emit("onAppSetup", appId);
|
201
|
+
}
|
197
202
|
}, SETUP_APP_DELAY);
|
198
203
|
});
|
199
204
|
this.boxManager?.createBox({
|
@@ -391,6 +396,9 @@ export class AppProxy implements PageRemoveService {
|
|
391
396
|
private setFocusScenePathHandler = debounce((fullPath: string | undefined) => {
|
392
397
|
if (this.view && fullPath && fullPath !== this.view?.focusScenePath) {
|
393
398
|
setViewFocusScenePath(this.view, fullPath);
|
399
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
400
|
+
callbacks.emit("onAppScenePathChange", {appId: this.id, view:this.view});
|
401
|
+
}
|
394
402
|
}
|
395
403
|
}, 50);
|
396
404
|
|
package/src/AppManager.ts
CHANGED
@@ -31,7 +31,14 @@ import {
|
|
31
31
|
} from "./Utils/Common";
|
32
32
|
import type { ReconnectRefresher } from "./ReconnectRefresher";
|
33
33
|
import type { BoxManager } from "./BoxManager";
|
34
|
-
import type {
|
34
|
+
import type {
|
35
|
+
Displayer,
|
36
|
+
Room,
|
37
|
+
ScenesCallbacksNode,
|
38
|
+
SceneState,
|
39
|
+
RoomState,
|
40
|
+
MemberState,
|
41
|
+
} from "white-web-sdk";
|
35
42
|
import type { AddAppParams, BaseInsertParams, TeleBoxRect } from "./index";
|
36
43
|
import type {
|
37
44
|
BoxClosePayload,
|
@@ -111,6 +118,10 @@ export class AppManager {
|
|
111
118
|
});
|
112
119
|
}
|
113
120
|
|
121
|
+
public getMemberState(): MemberState {
|
122
|
+
return this.room?.state.memberState || ({ strokeColor: [0, 0, 0] } as MemberState);
|
123
|
+
}
|
124
|
+
|
114
125
|
private onRemoveScenes = async (params: RemoveSceneParams) => {
|
115
126
|
const { scenePath } = params;
|
116
127
|
// 如果移除根目录就把 scenePath 设置为初始值
|
@@ -373,6 +384,9 @@ export class AppManager {
|
|
373
384
|
x: payload.x,
|
374
385
|
y: payload.y,
|
375
386
|
});
|
387
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
388
|
+
callbacks.emit("onBoxMove", payload);
|
389
|
+
}
|
376
390
|
};
|
377
391
|
|
378
392
|
private onBoxResize = (payload: BoxResizePayload) => {
|
@@ -382,11 +396,18 @@ export class AppManager {
|
|
382
396
|
width: payload.width,
|
383
397
|
height: payload.height,
|
384
398
|
});
|
399
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
400
|
+
callbacks.emit("onBoxResize", payload);
|
401
|
+
}
|
385
402
|
}
|
386
403
|
};
|
387
404
|
|
388
405
|
private onBoxFocus = (payload: BoxFocusPayload) => {
|
389
406
|
this.windowManger.safeSetAttributes({ focus: payload.appId });
|
407
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
408
|
+
// (WindowManager.externalNotifyManager as any).emit('onBoxFocus', payload)
|
409
|
+
callbacks.emit("onBoxFocus", payload);
|
410
|
+
}
|
390
411
|
};
|
391
412
|
|
392
413
|
private onBoxClose = (payload: BoxClosePayload) => {
|
@@ -394,10 +415,16 @@ export class AppManager {
|
|
394
415
|
if (appProxy) {
|
395
416
|
appProxy.destroy(false, true, true, payload.error);
|
396
417
|
}
|
418
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
419
|
+
callbacks.emit("onBoxClose", payload);
|
420
|
+
}
|
397
421
|
};
|
398
422
|
|
399
423
|
private onBoxStateChange = (payload: BoxStateChangePayload) => {
|
400
424
|
this.dispatchInternalEvent(Events.AppBoxStateChange, payload);
|
425
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
426
|
+
callbacks.emit("onBoxStateChange", payload);
|
427
|
+
}
|
401
428
|
};
|
402
429
|
|
403
430
|
public addAppsChangeListener = () => {
|
@@ -581,6 +608,9 @@ export class AppManager {
|
|
581
608
|
this.setMainViewFocusPath();
|
582
609
|
}
|
583
610
|
internalEmitter.emit("mainViewMounted");
|
611
|
+
if (WindowManager.supportTeachingAidsPlugin) {
|
612
|
+
callbacks.emit("onMainViewMounted", mainView);
|
613
|
+
}
|
584
614
|
}
|
585
615
|
|
586
616
|
public setMainViewFocusPath(scenePath?: string) {
|
package/src/Cursor/Cursor.svelte
CHANGED
@@ -15,7 +15,8 @@
|
|
15
15
|
export let color: string;
|
16
16
|
export let cursorTagBackgroundColor: string;
|
17
17
|
export let opacity: number;
|
18
|
-
export let pencilEraserSize: number;
|
18
|
+
export let pencilEraserSize: number | undefined;
|
19
|
+
export let custom: boolean | undefined;
|
19
20
|
|
20
21
|
$: hasName = !isEmpty(cursorName);
|
21
22
|
$: hasTagName = !isEmpty(tagName);
|
@@ -40,9 +41,9 @@
|
|
40
41
|
</script>
|
41
42
|
|
42
43
|
<div
|
43
|
-
class="netless-window-manager-cursor-mid"
|
44
|
+
class={"netless-window-manager-cursor-mid" + (custom ? " netless-window-manager-cursor-custom" : "")}
|
44
45
|
style="transform: translateX({x}px) translateY({y}px);display: {display}"
|
45
|
-
>
|
46
|
+
>
|
46
47
|
{#if !isLaserPointer}
|
47
48
|
<div class="netless-window-manager-cursor-name {offset} {pencilEraserSize3ImageOffset}">
|
48
49
|
<div
|
@@ -67,6 +68,10 @@
|
|
67
68
|
</div>
|
68
69
|
{/if}
|
69
70
|
<div class="cursor-image-wrapper">
|
70
|
-
<img
|
71
|
+
<img
|
72
|
+
class="netless-window-manager-cursor-{appliance}-image {pencilEraserSize3ImageOffset}"
|
73
|
+
{src}
|
74
|
+
alt={appliance}
|
75
|
+
/>
|
71
76
|
</div>
|
72
77
|
</div>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
2
|
+
|
3
|
+
declare class Cursor extends SvelteComponentTyped<{
|
4
|
+
readonly cursorName: string;
|
5
|
+
readonly tagName?: string;
|
6
|
+
readonly backgroundColor: string;
|
7
|
+
readonly appliance: string;
|
8
|
+
readonly x: number;
|
9
|
+
readonly y: number;
|
10
|
+
readonly src?: string;
|
11
|
+
readonly visible: boolean;
|
12
|
+
readonly avatar: string;
|
13
|
+
readonly theme: string;
|
14
|
+
readonly color: string;
|
15
|
+
readonly cursorTagBackgroundColor: string;
|
16
|
+
readonly opacity: number;
|
17
|
+
readonly pencilEraserSize?: number;
|
18
|
+
readonly custom?: boolean;
|
19
|
+
}> {}
|
20
|
+
|
21
|
+
export default Cursor;
|
package/src/Cursor/Cursor.ts
CHANGED
@@ -1,12 +1,15 @@
|
|
1
|
-
import App from "./Cursor.svelte";
|
2
|
-
import { ApplianceNames } from "white-web-sdk";
|
3
|
-
import { findMemberByUid } from "../Helper";
|
4
|
-
import { omit } from "lodash";
|
5
|
-
import type { Position } from "../AttributesDelegate";
|
6
1
|
import type { RoomMember } from "white-web-sdk";
|
7
|
-
import type {
|
8
|
-
import type { SvelteComponent } from "svelte";
|
2
|
+
import type { CursorOptions } from "../index";
|
9
3
|
import type { AppManager } from "../AppManager";
|
4
|
+
import type { Position } from "../AttributesDelegate";
|
5
|
+
import type { CursorManager } from "./index";
|
6
|
+
|
7
|
+
import { omit } from "lodash";
|
8
|
+
import { ApplianceNames } from "white-web-sdk";
|
9
|
+
|
10
|
+
import { findMemberByUid } from "../Helper";
|
11
|
+
import App from "./Cursor.svelte";
|
12
|
+
import { remoteIcon } from "./icons2";
|
10
13
|
|
11
14
|
export type Payload = {
|
12
15
|
[key: string]: any;
|
@@ -15,7 +18,8 @@ export type Payload = {
|
|
15
18
|
export class Cursor {
|
16
19
|
private member?: RoomMember;
|
17
20
|
private timer?: number;
|
18
|
-
private component?:
|
21
|
+
private component?: App;
|
22
|
+
private style: CursorOptions["style"] & string = "default";
|
19
23
|
|
20
24
|
constructor(
|
21
25
|
private manager: AppManager,
|
@@ -26,6 +30,7 @@ export class Cursor {
|
|
26
30
|
this.updateMember();
|
27
31
|
this.createCursor();
|
28
32
|
this.autoHidden();
|
33
|
+
this.setStyle(cursorManager.style);
|
29
34
|
}
|
30
35
|
|
31
36
|
public move = (position: Position) => {
|
@@ -46,6 +51,16 @@ export class Cursor {
|
|
46
51
|
}
|
47
52
|
};
|
48
53
|
|
54
|
+
public setStyle = (style: typeof this.style) => {
|
55
|
+
this.style = style;
|
56
|
+
if (this.component) {
|
57
|
+
this.component.$set({
|
58
|
+
src: this.getIcon(),
|
59
|
+
custom: this.isCustomIcon(),
|
60
|
+
});
|
61
|
+
}
|
62
|
+
};
|
63
|
+
|
49
64
|
public leave = () => {
|
50
65
|
this.hide();
|
51
66
|
};
|
@@ -56,6 +71,10 @@ export class Cursor {
|
|
56
71
|
if (point) {
|
57
72
|
let translateX = point.x - 2;
|
58
73
|
let translateY = point.y - 18;
|
74
|
+
if (this.isCustomIcon()) {
|
75
|
+
translateX -= 11;
|
76
|
+
translateY += 4;
|
77
|
+
}
|
59
78
|
if (type === "app") {
|
60
79
|
const wrapperRect = this.cursorManager.wrapperRect;
|
61
80
|
if (wrapperRect) {
|
@@ -80,6 +99,11 @@ export class Cursor {
|
|
80
99
|
return `rgb(${rgb})`;
|
81
100
|
}
|
82
101
|
|
102
|
+
public get memberColorHex(): string {
|
103
|
+
const [r, g, b] = this.member?.memberState?.strokeColor || [236, 52, 85];
|
104
|
+
return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
105
|
+
}
|
106
|
+
|
83
107
|
private get payload(): Payload | undefined {
|
84
108
|
return this.member?.payload;
|
85
109
|
}
|
@@ -145,6 +169,7 @@ export class Cursor {
|
|
145
169
|
appliance: this.memberApplianceName as string,
|
146
170
|
avatar: this.memberAvatar,
|
147
171
|
src: this.getIcon(),
|
172
|
+
custom: this.isCustomIcon(),
|
148
173
|
visible: false,
|
149
174
|
backgroundColor: this.memberColor,
|
150
175
|
cursorName: this.memberCursorName,
|
@@ -157,18 +182,49 @@ export class Cursor {
|
|
157
182
|
};
|
158
183
|
}
|
159
184
|
|
160
|
-
private getIcon() {
|
161
|
-
if (this.member)
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
}
|
170
|
-
return applianceSrc || icons[ApplianceNames.shape];
|
185
|
+
private getIcon(): string | undefined {
|
186
|
+
if (!this.member) return;
|
187
|
+
|
188
|
+
const { memberApplianceName, memberColorHex } = this;
|
189
|
+
const { userApplianceIcons, applianceIcons } = this.cursorManager;
|
190
|
+
|
191
|
+
let iconsKey: string | undefined = this.memberApplianceName;
|
192
|
+
if (iconsKey === ApplianceNames.pencilEraser) {
|
193
|
+
iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
|
171
194
|
}
|
195
|
+
|
196
|
+
const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
|
197
|
+
if (userApplianceSrc) return userApplianceSrc;
|
198
|
+
|
199
|
+
if (this.style === "custom" && memberApplianceName) {
|
200
|
+
const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
|
201
|
+
if (customApplianceSrc) return customApplianceSrc;
|
202
|
+
}
|
203
|
+
|
204
|
+
const applianceSrc = applianceIcons[iconsKey || ApplianceNames.shape];
|
205
|
+
return applianceSrc || applianceIcons[ApplianceNames.shape];
|
206
|
+
}
|
207
|
+
|
208
|
+
private isCustomIcon(): boolean {
|
209
|
+
if (!this.member) return false;
|
210
|
+
|
211
|
+
const { memberApplianceName, memberColorHex } = this;
|
212
|
+
const { userApplianceIcons } = this.cursorManager;
|
213
|
+
|
214
|
+
let iconsKey: string | undefined = this.memberApplianceName;
|
215
|
+
if (iconsKey === ApplianceNames.pencilEraser) {
|
216
|
+
iconsKey = `${iconsKey}${this.member?.memberState.pencilEraserSize || 1}`;
|
217
|
+
}
|
218
|
+
|
219
|
+
const userApplianceSrc = iconsKey && userApplianceIcons[iconsKey];
|
220
|
+
if (userApplianceSrc) return false;
|
221
|
+
|
222
|
+
if (this.style === "custom" && memberApplianceName) {
|
223
|
+
const customApplianceSrc = remoteIcon(memberApplianceName, memberColorHex);
|
224
|
+
if (customApplianceSrc) return true;
|
225
|
+
}
|
226
|
+
|
227
|
+
return false;
|
172
228
|
}
|
173
229
|
|
174
230
|
public updateMember() {
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import type { MemberState } from "white-web-sdk";
|
2
|
+
import { ApplianceNames } from "white-web-sdk";
|
3
|
+
|
4
|
+
type Color = string;
|
5
|
+
|
6
|
+
const staticCircle = `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='12' cy='12' r='2.5' stroke='%23000' stroke-linejoin='square'/%3E%3Ccircle cx='12' cy='12' r='3.5' stroke='%23FFF'/%3E%3C/g%3E%3C/svg%3E`;
|
7
|
+
|
8
|
+
function circleUrl(color: Color): string {
|
9
|
+
return `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle cx='12' cy='12' r='2.5' stroke='%23${color}' stroke-linejoin='square'/%3E%3Ccircle cx='12' cy='12' r='3.5' stroke='%23${color}'/%3E%3C/g%3E%3C/svg%3E`;
|
10
|
+
}
|
11
|
+
|
12
|
+
function crossUrl(color: Color): string {
|
13
|
+
return `data:image/svg+xml,%3Csvg width='24' height='24' xmlns='http://www.w3.org/2000/svg' fill='none'%3E%3Cpath d='M5 12H19' stroke='%23${color}' stroke-linejoin='round'/%3E%3Cpath d='M12 5V19' stroke='%23${color}' stroke-linejoin='round'/%3E%3C/svg%3E`;
|
14
|
+
}
|
15
|
+
|
16
|
+
function cssCursor(url: string): string {
|
17
|
+
return `url("${url}") 12 12, auto`;
|
18
|
+
}
|
19
|
+
|
20
|
+
function makeStyleContent(config: { [cursor: string]: string }): string {
|
21
|
+
let result = "";
|
22
|
+
for (const cursor in config) {
|
23
|
+
result += `.netless-whiteboard.${cursor} {cursor: ${config[cursor]}}\n`;
|
24
|
+
}
|
25
|
+
return result;
|
26
|
+
}
|
27
|
+
|
28
|
+
const $style = document.createElement("style");
|
29
|
+
|
30
|
+
export function enableLocal(memberState: MemberState): () => void {
|
31
|
+
const [r, g, b] = memberState.strokeColor;
|
32
|
+
const hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
33
|
+
$style.textContent = makeStyleContent({
|
34
|
+
"cursor-pencil": cssCursor(circleUrl(hex)),
|
35
|
+
"cursor-eraser": cssCursor(staticCircle),
|
36
|
+
"cursor-rectangle": cssCursor(crossUrl(hex)),
|
37
|
+
"cursor-ellipse": cssCursor(crossUrl(hex)),
|
38
|
+
"cursor-straight": cssCursor(crossUrl(hex)),
|
39
|
+
"cursor-arrow": cssCursor(crossUrl(hex)),
|
40
|
+
"cursor-shape": cssCursor(crossUrl(hex)),
|
41
|
+
});
|
42
|
+
document.head.appendChild($style);
|
43
|
+
|
44
|
+
return () => {
|
45
|
+
if ($style.parentNode == null) return;
|
46
|
+
document.head.removeChild($style);
|
47
|
+
};
|
48
|
+
}
|
49
|
+
|
50
|
+
const shapeAppliances: Set<ApplianceNames> = new Set([
|
51
|
+
ApplianceNames.rectangle,
|
52
|
+
ApplianceNames.ellipse,
|
53
|
+
ApplianceNames.straight,
|
54
|
+
ApplianceNames.arrow,
|
55
|
+
ApplianceNames.shape,
|
56
|
+
]);
|
57
|
+
|
58
|
+
export function remoteIcon(applianceName: ApplianceNames, hex: string): string | undefined {
|
59
|
+
if (applianceName === ApplianceNames.pencil) {
|
60
|
+
return circleUrl(hex);
|
61
|
+
} else if (applianceName === ApplianceNames.eraser) {
|
62
|
+
return staticCircle;
|
63
|
+
} else if (shapeAppliances.has(applianceName)) {
|
64
|
+
return crossUrl(hex);
|
65
|
+
}
|
66
|
+
}
|
package/src/Cursor/index.ts
CHANGED
@@ -1,15 +1,16 @@
|
|
1
|
-
import { ApplianceNames } from "white-web-sdk";
|
1
|
+
import { ApplianceNames, isRoom } from "white-web-sdk";
|
2
2
|
import { Cursor } from "./Cursor";
|
3
3
|
import { CursorState, Events } from "../constants";
|
4
4
|
import { internalEmitter } from "../InternalEmitter";
|
5
5
|
import { SideEffectManager } from "side-effect-manager";
|
6
6
|
import { WindowManager } from "../index";
|
7
|
-
import type { CursorMovePayload, ApplianceIcons } from "../index";
|
7
|
+
import type { CursorMovePayload, ApplianceIcons, CursorOptions } from "../index";
|
8
8
|
import type { PositionType } from "../AttributesDelegate";
|
9
|
-
import type { Point, RoomMember, View } from "white-web-sdk";
|
9
|
+
import type { Point, Room, RoomMember, RoomState, View } from "white-web-sdk";
|
10
10
|
import type { AppManager } from "../AppManager";
|
11
11
|
import { ApplianceMap } from "./icons";
|
12
12
|
import { findMemberByUid } from "../Helper";
|
13
|
+
import { enableLocal } from "./icons2";
|
13
14
|
|
14
15
|
export type EventType = {
|
15
16
|
type: PositionType;
|
@@ -22,17 +23,27 @@ export type MoveCursorParams = {
|
|
22
23
|
y: number;
|
23
24
|
};
|
24
25
|
|
26
|
+
const LocalCursorSideEffectId = "local-cursor";
|
27
|
+
|
25
28
|
export class CursorManager {
|
26
29
|
public containerRect?: DOMRect;
|
27
30
|
public wrapperRect?: DOMRect;
|
28
31
|
public cursorInstances: Map<string, Cursor> = new Map();
|
29
32
|
public roomMembers?: readonly RoomMember[];
|
33
|
+
public userApplianceIcons: ApplianceIcons = {};
|
34
|
+
|
30
35
|
private mainViewElement?: HTMLDivElement;
|
31
36
|
private sideEffectManager = new SideEffectManager();
|
32
37
|
private store = this.manager.store;
|
33
|
-
|
38
|
+
private leaveFlag = true;
|
39
|
+
private _style: CursorOptions["style"] & string = "default";
|
34
40
|
|
35
|
-
constructor(
|
41
|
+
constructor(
|
42
|
+
private manager: AppManager,
|
43
|
+
private enableCursor: boolean,
|
44
|
+
cursorOptions?: CursorOptions,
|
45
|
+
applianceIcons?: ApplianceIcons
|
46
|
+
) {
|
36
47
|
this.roomMembers = this.manager.room?.state.roomMembers;
|
37
48
|
const wrapper = WindowManager.wrapper;
|
38
49
|
if (wrapper) {
|
@@ -41,12 +52,44 @@ export class CursorManager {
|
|
41
52
|
this.sideEffectManager.add(() => {
|
42
53
|
return internalEmitter.on("cursorMove", this.onCursorMove);
|
43
54
|
});
|
44
|
-
|
45
55
|
this.sideEffectManager.add(() => {
|
46
56
|
return internalEmitter.on("playgroundSizeChange", () => this.updateContainerRect());
|
47
57
|
});
|
58
|
+
const room = this.manager.room;
|
59
|
+
if (room) {
|
60
|
+
this.sideEffectManager.add(() => {
|
61
|
+
const update = (state: RoomState) => {
|
62
|
+
if (this.style === "custom" && state.memberState) this.enableCustomCursor();
|
63
|
+
};
|
64
|
+
room.callbacks.on("onRoomStateChanged", update);
|
65
|
+
return () => room.callbacks.off("onRoomStateChanged", update);
|
66
|
+
});
|
67
|
+
}
|
48
68
|
if (applianceIcons) {
|
49
|
-
this.
|
69
|
+
this.userApplianceIcons = applianceIcons;
|
70
|
+
}
|
71
|
+
this.style = cursorOptions?.style || "default";
|
72
|
+
}
|
73
|
+
|
74
|
+
public get applianceIcons(): ApplianceIcons {
|
75
|
+
return { ...ApplianceMap, ...this.userApplianceIcons };
|
76
|
+
}
|
77
|
+
|
78
|
+
public get style() {
|
79
|
+
return this._style;
|
80
|
+
}
|
81
|
+
|
82
|
+
public set style(value) {
|
83
|
+
if (this._style !== value) {
|
84
|
+
this._style = value;
|
85
|
+
this.cursorInstances.forEach(cursor => {
|
86
|
+
cursor.setStyle(value);
|
87
|
+
});
|
88
|
+
if (value === "custom") {
|
89
|
+
this.enableCustomCursor();
|
90
|
+
} else {
|
91
|
+
this.sideEffectManager.flush(LocalCursorSideEffectId);
|
92
|
+
}
|
50
93
|
}
|
51
94
|
}
|
52
95
|
|
@@ -71,6 +114,13 @@ export class CursorManager {
|
|
71
114
|
return cursorInstance;
|
72
115
|
};
|
73
116
|
|
117
|
+
private enableCustomCursor() {
|
118
|
+
this.sideEffectManager.add(
|
119
|
+
() => enableLocal(this.manager.getMemberState()),
|
120
|
+
LocalCursorSideEffectId
|
121
|
+
);
|
122
|
+
}
|
123
|
+
|
74
124
|
private canMoveCursor(member: RoomMember | undefined) {
|
75
125
|
const isLaserPointer =
|
76
126
|
member?.memberState.currentApplianceName === ApplianceNames.laserPointer;
|
@@ -109,28 +159,48 @@ export class CursorManager {
|
|
109
159
|
const type = this.getType(event);
|
110
160
|
this.updateCursor(type, event.clientX, event.clientY);
|
111
161
|
isTouch && this.showPencilEraserIfNeeded(type, event.clientX, event.clientY);
|
112
|
-
}
|
162
|
+
};
|
113
163
|
|
114
164
|
private mouseMoveTimer = 0;
|
115
165
|
private mouseMoveListener = (event: PointerEvent) => {
|
116
166
|
const isTouch = event.pointerType === "touch";
|
117
167
|
if (isTouch && !event.isPrimary) return;
|
118
|
-
|
119
|
-
const now = Date.now()
|
168
|
+
const now = Date.now();
|
120
169
|
if (now - this.mouseMoveTimer > 48) {
|
121
170
|
this.mouseMoveTimer = now;
|
171
|
+
if (
|
172
|
+
WindowManager.supportTeachingAidsPlugin &&
|
173
|
+
isRoom(WindowManager.displayer) &&
|
174
|
+
(WindowManager.displayer as Room).disableDeviceInputs
|
175
|
+
) {
|
176
|
+
if (this.leaveFlag) {
|
177
|
+
this.manager.dispatchInternalEvent(Events.CursorMove, {
|
178
|
+
uid: this.manager.uid,
|
179
|
+
state: CursorState.Leave,
|
180
|
+
} as CursorMovePayload);
|
181
|
+
this.leaveFlag = false;
|
182
|
+
}
|
183
|
+
return;
|
184
|
+
}
|
122
185
|
this.mouseMoveListener_(event, isTouch);
|
186
|
+
this.leaveFlag = true;
|
123
187
|
}
|
124
|
-
}
|
188
|
+
};
|
125
189
|
|
126
190
|
private mouseLeaveListener = () => {
|
127
191
|
this.hideCursor(this.manager.uid);
|
128
|
-
}
|
192
|
+
};
|
129
193
|
|
130
194
|
private showPencilEraserIfNeeded(event: EventType, clientX: number, clientY: number) {
|
131
195
|
const self = findMemberByUid(this.manager.room, this.manager.uid);
|
132
|
-
const isPencilEraser =
|
133
|
-
|
196
|
+
const isPencilEraser =
|
197
|
+
self?.memberState.currentApplianceName === ApplianceNames.pencilEraser;
|
198
|
+
if (
|
199
|
+
this.wrapperRect &&
|
200
|
+
this.manager.canOperate &&
|
201
|
+
this.canMoveCursor(self) &&
|
202
|
+
isPencilEraser
|
203
|
+
) {
|
134
204
|
const view = event.type === "main" ? this.manager.mainView : this.focusView;
|
135
205
|
const point = this.getPoint(view, clientX, clientY);
|
136
206
|
if (point) {
|
package/src/callback.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import Emittery from "emittery";
|
2
2
|
import type { TeleBoxColorScheme, TELE_BOX_STATE } from "@netless/telebox-insider";
|
3
|
-
import type { CameraState, SceneState, ViewVisionMode } from "white-web-sdk";
|
3
|
+
import type { CameraState, SceneState, View, ViewVisionMode } from "white-web-sdk";
|
4
4
|
import type { LoadAppEvent } from "./Register";
|
5
5
|
import type { PageState } from "./Page";
|
6
|
+
import type { BoxClosePayload, BoxFocusPayload, BoxMovePayload, BoxResizePayload, BoxStateChangePayload } from "./BoxEmitter";
|
7
|
+
import type { AppPayload } from "./typings";
|
6
8
|
|
7
9
|
export type PublicEvent = {
|
8
10
|
mainViewModeChange: ViewVisionMode;
|
@@ -22,6 +24,15 @@ export type PublicEvent = {
|
|
22
24
|
pageStateChange: PageState;
|
23
25
|
fullscreenChange: boolean;
|
24
26
|
appsChange: string[]; // APP 列表变化时触发
|
27
|
+
onBoxMove: BoxMovePayload;
|
28
|
+
onBoxResize: BoxResizePayload;
|
29
|
+
onBoxFocus: BoxFocusPayload;
|
30
|
+
onBoxClose: BoxClosePayload;
|
31
|
+
onBoxStateChange: BoxStateChangePayload;
|
32
|
+
onMainViewMounted: View;
|
33
|
+
onAppViewMounted: AppPayload;
|
34
|
+
onAppSetup: string;
|
35
|
+
onAppScenePathChange: AppPayload;
|
25
36
|
};
|
26
37
|
|
27
38
|
export type CallbacksType = Emittery<PublicEvent>;
|