123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- import { Entity } from "../base/entity";
- import { onEntity, openOnEntityTree, TreeEvent } from "../event/spread";
- import { findEntityByShape } from "../base/entity-server";
- import { debounce, getChangePart, mergeFuns } from "../../shared";
- import { Root } from "./entity-root";
- export const injectPointerEvents = (root: Root) => {
- const store = {
- hovers: new Set<Entity>(),
- focus: new Set<Entity>(),
- drag: null as Entity,
- };
- const oldStore = {
- hovers: [] as Entity[],
- focus: [] as Entity[],
- drag: null as Entity,
- };
- const emit = debounce(() => {
- const hovers = [...store.hovers];
- const focus = [...store.focus];
- const hoverChange = getChangePart(hovers, oldStore.hovers);
- const focusChange = getChangePart(focus, oldStore.hovers);
- hoverChange.addPort.forEach((entity) => entity.bus.emit("hover"));
- hoverChange.delPort.forEach((entity) => entity.bus.emit("leave"));
- focusChange.addPort.forEach((entity) => entity.bus.emit("focus"));
- focusChange.delPort.forEach((entity) => entity.bus.emit("blur"));
- if (oldStore.drag !== store.drag) {
- oldStore.drag && oldStore.drag.bus.emit("drop");
- store.drag && store.drag.bus.emit("drag");
- }
- oldStore.drag = store.drag;
- oldStore.hovers = hovers;
- oldStore.focus = focus;
- }, 16);
- const needReleases = [
- openOnEntityTree(root, "mouseenter"),
- openOnEntityTree(root, "mouseleave"),
- openOnEntityTree(root, "click"),
- openOnEntityTree(root, "touchend"),
- onEntity(root, "dragstart", (ev) => {
- const hit = findEntityByShape(root, ev.target);
- hit.contenteditable && (store.drag = hit);
- emit();
- }),
- onEntity(root, "dragend", () => {
- store.drag = null;
- emit();
- }),
- ];
- const enterHandler = ({ paths }: TreeEvent) => {
- paths.forEach((entity) => {
- store.hovers.add(entity);
- });
- emit();
- };
- const leaveHandler = ({ paths }: TreeEvent) => {
- paths.forEach((entity) => {
- store.hovers.delete(entity);
- });
- emit();
- };
- const clickHandler = ({ paths }: TreeEvent) => {
- store.focus.clear();
- paths.forEach((entity) => {
- store.focus.add(entity);
- });
- emit();
- };
- root.bus.on("mouseenter", enterHandler);
- root.bus.on("mouseleave", leaveHandler);
- root.bus.on("click", clickHandler);
- root.bus.on("touchend", clickHandler);
- const destory = () => {
- mergeFuns(needReleases)();
- root.bus.off("mouseenter", enterHandler);
- root.bus.off("mouseleave", leaveHandler);
- root.bus.off("click", clickHandler);
- root.bus.off("touchend", clickHandler);
- };
- root.bus.on("destroyBefore", destory);
- return {
- focus(...entitys: Entity[]) {
- store.focus.clear();
- entitys.forEach((entity) => store.focus.add(entity));
- emit();
- },
- blur(...entitys: Entity[]) {
- entitys.forEach((entity) => store.focus.delete(entity));
- emit();
- },
- hover(...entitys: Entity[]) {
- store.hovers.clear();
- entitys.forEach((entity) => store.hovers.add(entity));
- emit();
- },
- leave(...entitys: Entity[]) {
- entitys.forEach((entity) => store.hovers.delete(entity));
- emit();
- },
- drag(entity: Entity) {
- store.drag = entity;
- emit();
- },
- drop(entity: Entity) {
- if (store.drag === entity) {
- store.drag = entity;
- emit();
- }
- },
- destory,
- };
- };
- export type PointerEvents = ReturnType<typeof injectPointerEvents>;
- // 指定某些entity可编辑
- export const openOnlyMode = (
- root: Root,
- _entitys: Entity[],
- includeNews = true
- ) => {
- const prevRootReply = root.replyEvents;
- const entitys: (Entity | null)[] = [];
- const prevEntitysReply: Entity["replyEvents"][] = [];
- const pushEntity = (entity: Entity) => {
- const ndx = entitys.length;
- entitys[ndx] = entity;
- prevEntitysReply[ndx] = entity.replyEvents;
- entity.replyEvents = "all";
- };
- const delEntity = (entity: Entity) => {
- const ndx = entitys.indexOf(entity);
- prevEntitysReply[ndx] = null;
- entitys[ndx] = null;
- };
- _entitys.forEach(pushEntity);
- // 新增的entity可以使用事件
- includeNews && root.bus.on("addEntity", pushEntity);
- root.bus.on("delEntity", delEntity);
- return () => {
- root.bus.off("addEntity", pushEntity);
- root.bus.off("delEntity", delEntity);
- root.replyEvents = prevRootReply;
- entitys.forEach((entity, ndx) => {
- if (entity) {
- entity.replyEvents = prevEntitysReply[ndx];
- }
- });
- };
- };
- export type EditModeProps = {
- entitys: Entity[];
- only: boolean;
- includeNews: boolean;
- };
- export const openEditMode = async (
- root: Root,
- main: () => any,
- props: EditModeProps = { entitys: [], only: false, includeNews: false }
- ) => {
- root.bus.emit("dataChangeBefore");
- const quitOnlyMode =
- props.only && openOnlyMode(root, props.entitys, props.includeNews);
- const addEntitys = [];
- const setEntitys = [];
- const delEntitys = [];
- const addHandler = (entity: Entity) => {
- addEntitys.push(entity);
- };
- const setHandler = (entity: Entity) => {
- if (
- !addEntitys.includes(entity) &&
- !delEntitys.includes(entity) &&
- !setEntitys.includes(entity)
- ) {
- setEntitys.push(entity);
- }
- };
- const delHandler = (entity: Entity) => {
- const delNdx = delEntitys.indexOf(entity);
- if (~delNdx) return;
- const addNdx = addEntitys.indexOf(entity);
- if (~addNdx) addEntitys.splice(addNdx, 1);
- const setNdx = setEntitys.indexOf(entity);
- if (~setNdx) setEntitys.splice(setNdx, 1);
- delEntitys.push(entity);
- };
- root.bus.on("addEntity", addHandler);
- root.bus.on("updateEntity", setHandler);
- root.bus.on("delEntity", delHandler);
- let state = "normal";
- const interrupt = new Promise<void>((resolve) => {
- state = "interrupt";
- resolve();
- });
- const draw = Promise.any([interrupt, Promise.resolve(main())]).then(() => {
- root.bus.off("addEntity", addHandler);
- root.bus.off("updateEntity", setHandler);
- root.bus.off("delEntity", addHandler);
- quitOnlyMode && quitOnlyMode();
- const change = {
- addEntitys,
- delEntitys,
- setEntitys,
- };
- root.bus.emit("dataChangeAfter", change);
- return { state, change };
- });
- return {
- draw,
- interrupt,
- };
- };
- const cursorResources = import.meta.glob("../resource/cursor?url");
- const cursorDefs = ["move", "inherit", "pointer"];
- const cursorMap = new WeakMap<Root, string[]>();
- const setCursorStyle = (root: Root, ico: string) => {
- const style = cursorDefs.includes(ico)
- ? ico
- : ico in cursorResources
- ? `url("${ico}"), auto`
- : null;
- if (!style) throw "ico 不存在!";
- root.container.style.cursor = ico;
- };
- export const setCursor = (root: Root, ico: string) => {
- if (!cursorMap.get(root)) {
- cursorMap.set(root, []);
- }
- const stack = cursorMap.get(root);
- const ndx = stack.length;
- stack[ndx] = ico;
- setCursorStyle(root, ico);
- return () => {
- stack[ndx] = null;
- let last = stack.length - 1;
- for (; last >= 0; last--) {
- if (stack[last] !== null) {
- break;
- }
- }
- if (last === -1) {
- setCursor(root, "inherit");
- stack.length = 0;
- } else if (last < ndx) {
- setCursor(root, stack[last]);
- }
- };
- };
|