diff --git a/package-lock.json b/package-lock.json index ff1cbd2fe..6dbeaa855 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "eezstudio", - "version": "0.14.0", + "version": "0.15.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "eezstudio", - "version": "0.14.0", + "version": "0.15.0", "hasInstallScript": true, "license": "GPL-3.0-only", "dependencies": { @@ -132,7 +132,7 @@ "request": "^2.88.2", "request-promise-native": "^1.0.5", "run-script-os": "^1.1.6", - "typescript": "^5.3.3", + "typescript": "^5.5.3", "wpapi": "^1.2.2" } }, @@ -14744,9 +14744,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -27011,9 +27011,9 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "dev": true }, "unbox-primitive": { diff --git a/package.json b/package.json index c1b43c9b0..c4292cb51 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "request": "^2.88.2", "request-promise-native": "^1.0.5", "run-script-os": "^1.1.6", - "typescript": "^5.3.3", + "typescript": "^5.5.3", "wpapi": "^1.2.2" }, "dependencies": { @@ -184,4 +184,4 @@ "xterm": "^4.14.1", "xterm-addon-fit": "^0.5.0" } -} \ No newline at end of file +} diff --git a/packages/project-editor/core/object.ts b/packages/project-editor/core/object.ts index d6bc966f4..31f6c9844 100644 --- a/packages/project-editor/core/object.ts +++ b/packages/project-editor/core/object.ts @@ -282,10 +282,15 @@ export type InheritedValue = export interface SerializedData { originProjectFilePath: string; + objectClassName: string; classInfo?: ClassInfo; + object?: EezObject; + objectParentPath?: string; + objects?: EezObject[]; + objectsParentPath?: string[]; } interface LVGLClassInfoProperties { diff --git a/packages/project-editor/flow/flow.tsx b/packages/project-editor/flow/flow.tsx index 1d4f8c8cb..a0493dcb9 100644 --- a/packages/project-editor/flow/flow.tsx +++ b/packages/project-editor/flow/flow.tsx @@ -10,7 +10,8 @@ import { PropertyInfo, PropertyType, registerClass, - SerializedData + SerializedData, + setParent } from "project-editor/core/object"; import { visitObjects } from "project-editor/core/search"; import { @@ -185,6 +186,7 @@ export abstract class Flow extends EezObject { objectsToClipboardData(objects: IEezObject[]) { const flowFragment = new FlowFragment(); flowFragment.addObjects(this, objects); + setParent(flowFragment, this); return objectToClipboardData( ProjectEditor.getProjectStore(this), flowFragment @@ -324,87 +326,91 @@ export class FlowFragment extends EezObject { const flow = ProjectEditor.getFlow(clipboardData.pastePlace); const pasteFlowFragment = clipboardData.serializedData .object as FlowFragment; - const projectStore = getProjectStore(flow); - const flowFragment = createObject( + return FlowFragment.paste( projectStore, + flow, pasteFlowFragment, - FlowFragment, - undefined, - false + object ); + } + }; - let closeCombineCommands = false; - if (!projectStore.undoManager.combineCommands) { - projectStore.undoManager.setCombineCommands(true); - closeCombineCommands = true; - } + static paste( + projectStore: ProjectStore, + flow: Flow, + pasteFlowFragment: FlowFragment, + object: IEezObject + ) { + const flowFragment = createObject( + projectStore, + pasteFlowFragment, + FlowFragment, + undefined, + false + ); - let components: EezObject[] = []; + let closeCombineCommands = false; + if (!projectStore.undoManager.combineCommands) { + projectStore.undoManager.setCombineCommands(true); + closeCombineCommands = true; + } - if (flowFragment.connectionLines.length > 0) { - flowFragment.connectionLines.forEach(connectionLine => - projectStore.addObject(flow.connectionLines, connectionLine) - ); + let components: EezObject[] = []; - flowFragment.components.forEach(component => { - components.push( - projectStore.addObject(flow.components, component) - ); - }); - } else { - if ( - (object instanceof Widget || - object instanceof ProjectEditor.LVGLWidgetClass) && - flowFragment.components.every( - component => component instanceof Widget - ) - ) { - let containerAncestor: - | ContainerWidget - | SelectWidget - | LVGLWidget - | undefined = getAncestorOfType( - getParent(getParent(object)), - ContainerWidget.classInfo - ) as ContainerWidget | undefined; + if (flowFragment.connectionLines.length > 0) { + flowFragment.connectionLines.forEach(connectionLine => + projectStore.addObject(flow.connectionLines, connectionLine) + ); + flowFragment.components.forEach(component => { + components.push( + projectStore.addObject(flow.components, component) + ); + }); + } else { + if ( + (object instanceof Widget || + object instanceof ProjectEditor.LVGLWidgetClass) && + flowFragment.components.every( + component => component instanceof Widget + ) + ) { + let containerAncestor: + | ContainerWidget + | SelectWidget + | LVGLWidget + | undefined = getAncestorOfType( + getParent(getParent(object)), + ContainerWidget.classInfo + ) as ContainerWidget | undefined; + + if (!containerAncestor) { + containerAncestor = getAncestorOfType( + getParent(getParent(object)), + SelectWidget.classInfo + ) as SelectWidget | undefined; if (!containerAncestor) { containerAncestor = getAncestorOfType( getParent(getParent(object)), - SelectWidget.classInfo - ) as SelectWidget | undefined; - if (!containerAncestor) { - containerAncestor = getAncestorOfType( - getParent(getParent(object)), - ProjectEditor.LVGLWidgetClass.classInfo - ) as LVGLWidget | undefined; - } + ProjectEditor.LVGLWidgetClass.classInfo + ) as LVGLWidget | undefined; } + } - if (containerAncestor) { - const parent = - containerAncestor instanceof - ProjectEditor.LVGLWidgetClass - ? containerAncestor.children - : containerAncestor.widgets; - - flowFragment.components.forEach(component => { - components.push( - projectStore.addObject(parent, component) - ); - }); - } else { - flowFragment.components.forEach(component => { - components.push( - projectStore.addObject( - flow.components, - component - ) - ); - }); - } + if (containerAncestor) { + const parent = + containerAncestor instanceof + ProjectEditor.LVGLWidgetClass + ? containerAncestor.children + : containerAncestor.widgets; + + flowFragment.components.forEach(component => { + components.push( + projectStore.addObject(parent, component) + ); + }); } else { flowFragment.components.forEach(component => { components.push( @@ -412,15 +418,21 @@ export class FlowFragment extends EezObject { ); }); } + } else { + flowFragment.components.forEach(component => { + components.push( + projectStore.addObject(flow.components, component) + ); + }); } + } - if (closeCombineCommands) { - projectStore.undoManager.setCombineCommands(false); - } - - return components; + if (closeCombineCommands) { + projectStore.undoManager.setCombineCommands(false); } - }; + + return components; + } addObjects(flow: Flow, objects: IEezObject[]) { this.components = []; diff --git a/packages/project-editor/project-editor-create.tsx b/packages/project-editor/project-editor-create.tsx index cc35b5900..88518c9b2 100644 --- a/packages/project-editor/project-editor-create.tsx +++ b/packages/project-editor/project-editor-create.tsx @@ -82,7 +82,7 @@ import { LVGLLedWidget } from "project-editor/lvgl/widgets"; -import { getBitmapData } from "project-editor/features/bitmap/bitmap"; +import { Bitmap, getBitmapData } from "project-editor/features/bitmap/bitmap"; import { migrateProjectVersion, migrateProjectType @@ -117,6 +117,7 @@ import { ConditionalStyle, Style } from "project-editor/features/style/style"; import { PropertyType } from "project-editor/core/object"; import { evalProperty } from "project-editor/flow/helper"; import { migrateLvglVersion } from "./lvgl/migrate"; +import { FlowTabState } from "project-editor/flow/flow-tab-state"; export const conditionalStyleConditionProperty = makeExpressionProperty( { @@ -180,6 +181,7 @@ export async function createProjectEditor( ScpiCommandClass: ScpiCommand, ScpiSubsystemClass: ScpiSubsystem, StyleClass: Style, + BitmapClass: Bitmap, LVGLWidgetClass: LVGLWidget, LVGLScreenWidgetClass: LVGLScreenWidget, LVGLPanelWidgetClass: LVGLPanelWidget, @@ -220,7 +222,8 @@ export async function createProjectEditor( createActionComponentClass, makeExpressionProperty, evalProperty, - conditionalStyleConditionProperty + conditionalStyleConditionProperty, + FlowTabStateClass: FlowTabState }; ConditionalStyle.classInfo.properties.push( diff --git a/packages/project-editor/project-editor-interface.tsx b/packages/project-editor/project-editor-interface.tsx index f43d4ef89..32e1f956b 100644 --- a/packages/project-editor/project-editor-interface.tsx +++ b/packages/project-editor/project-editor-interface.tsx @@ -40,7 +40,10 @@ import type { ScpiSubsystem } from "project-editor/features/scpi/scpi"; import type { getObjectVariableTypeFromType } from "project-editor/features/variable/value-type"; -import type { getBitmapData } from "project-editor/features/bitmap/bitmap"; +import type { + Bitmap, + getBitmapData +} from "project-editor/features/bitmap/bitmap"; import type { migrateProjectVersion, migrateProjectType @@ -94,6 +97,7 @@ import type { Style } from "project-editor/features/style/style"; import type { evalProperty } from "project-editor/flow/helper"; import type { PropertyInfo } from "project-editor/core/object"; import type { migrateLvglVersion } from "project-editor/lvgl/migrate"; +import type { FlowTabState } from "project-editor/flow/flow-tab-state"; export interface IProjectEditor { homeTabs?: Tabs; @@ -129,6 +133,7 @@ export interface IProjectEditor { ScpiCommandClass: typeof ScpiCommand; ScpiSubsystemClass: typeof ScpiSubsystem; StyleClass: typeof Style; + BitmapClass: typeof Bitmap; LVGLWidgetClass: typeof LVGLWidget; LVGLScreenWidgetClass: typeof LVGLScreenWidget; LVGLPanelWidgetClass: typeof LVGLPanelWidget; @@ -170,6 +175,7 @@ export interface IProjectEditor { makeExpressionProperty: typeof makeExpressionProperty; evalProperty: typeof evalProperty; conditionalStyleConditionProperty: PropertyInfo; + FlowTabStateClass: typeof FlowTabState; } export const ProjectEditor: IProjectEditor = {} as any; diff --git a/packages/project-editor/store/clipboard.ts b/packages/project-editor/store/clipboard.ts index 4304ba063..74ff6dbe3 100644 --- a/packages/project-editor/store/clipboard.ts +++ b/packages/project-editor/store/clipboard.ts @@ -24,7 +24,8 @@ import { ProjectStore, rewireBegin, rewireEnd, - canContain + canContain, + getObjectPathAsString } from "project-editor/store"; //////////////////////////////////////////////////////////////////////////////// @@ -33,7 +34,7 @@ const CLIPOARD_DATA_ID = "application/eez-studio-project-editor-data"; //////////////////////////////////////////////////////////////////////////////// -function cloneObjectWithNewObjIds( +export function cloneObjectWithNewObjIds( projectStore: ProjectStore, object: IEezObject ) { @@ -59,7 +60,8 @@ export function objectToClipboardData( const serializeData: SerializedData = { originProjectFilePath: projectStore.filePath!, objectClassName: getClass(object).name, - object: objectToJson(clonedObject) as any as EezObject + object: objectToJson(clonedObject) as any as EezObject, + objectParentPath: getObjectPathAsString(getParent(object)) }; return JSON.stringify(serializeData); @@ -80,6 +82,9 @@ export function objectsToClipboardData( objectClassName: getClass(objects[0]).name, objects: clonedObjects.map( clonedObject => objectToJson(clonedObject) as any as EezObject + ), + objectsParentPath: objects.map(object => + getObjectPathAsString(getParent(object)) ) }; diff --git a/packages/project-editor/store/deep-paste.tsx b/packages/project-editor/store/deep-paste.tsx index 1a8a6a807..398c12521 100644 --- a/packages/project-editor/store/deep-paste.tsx +++ b/packages/project-editor/store/deep-paste.tsx @@ -17,17 +17,21 @@ import { setParent } from "project-editor/core/object"; import { - copyProjectEditorDataToClipboard, - getProjectEditorDataFromClipboard, - objectsToClipboardData + cloneObjectWithNewObjIds, + getProjectEditorDataFromClipboard } from "project-editor/store/clipboard"; import { + addObject, + getClass, getLabel, getObjectFromPath, + getObjectFromStringPath, type ProjectStore } from "project-editor/store"; import { ProjectEditor } from "project-editor/project-editor-interface"; import { searchForObjectDependencies } from "project-editor/core/search"; +import type { Style } from "project-editor/features/style/style"; +import { Loader } from "eez-studio-ui/loader"; //////////////////////////////////////////////////////////////////////////////// @@ -38,43 +42,111 @@ interface ObjectReference { toIndex: number; } +interface ObjectsMapValue { + clonedObject: EezObject; + references: ObjectReference[]; +} + class DeepPasteModel { - objects = new Map(); + objects = new Map(); + remaining: number = 0; constructor( public sourceProjectStore: ProjectStore, public destinationProjectStore: ProjectStore, public serializedData: SerializedData ) { + console.log(serializedData); makeObservable(this, { - objects: observable + objects: observable, + remaining: observable }); } async findAllDependencies() { if (this.serializedData.object) { - this.findObjectDependencies(this.serializedData.object); + this.findObjectDependencies( + this.serializedData.object, + this.serializedData.objectParentPath + ); } else { - for (const object of this.serializedData.objects!) { - this.findObjectDependencies(object); + for (let i = 0; i < this.serializedData.objects!.length; i++) { + this.findObjectDependencies( + this.serializedData.objects![i], + this.serializedData.objectsParentPath![i] + ); } } } - findObjectDependencies(object: EezObject, reference?: ObjectReference) { - let references = this.objects.get(object); - if (references) { + findObjectDependencies( + object: EezObject, + objectParentPath?: string, + reference?: ObjectReference + ) { + let objectsMapValue = this.objects.get(object); + if (objectsMapValue) { if (reference) { - references.push(reference); + objectsMapValue.references.push(reference); } - return; + return objectsMapValue; } + let clonedObject: EezObject; + if (objectParentPath) { + clonedObject = object; + setParent( + object, + getObjectFromStringPath( + this.sourceProjectStore.project, + objectParentPath + ) + ); + } else { + clonedObject = cloneObjectWithNewObjIds( + this.destinationProjectStore, + object + ); + + if ( + clonedObject instanceof ProjectEditor.StyleClass || + clonedObject instanceof ProjectEditor.LVGLStyleClass + ) { + clonedObject.childStyles = []; + } + } + + objectsMapValue = { + clonedObject, + references: reference ? [reference] : [] + }; + runInAction(() => { - this.objects.set(object, reference ? [reference] : []); + this.objects.set(object, objectsMapValue); + this.remaining++; }); - setParent(object, this.sourceProjectStore.project); + if ( + object instanceof ProjectEditor.StyleClass || + object instanceof ProjectEditor.LVGLStyleClass + ) { + if (object.parentStyle) { + let parentObjectMapValue = this.findObjectDependencies( + object.parentStyle + ); + + runInAction(() => { + ( + parentObjectMapValue.clonedObject as Style + ).childStyles.push(objectsMapValue.clonedObject as Style); + }); + + setParent( + objectsMapValue.clonedObject, + parentObjectMapValue.clonedObject + ); + } + } const search = searchForObjectDependencies( this.sourceProjectStore, @@ -88,6 +160,9 @@ class DeepPasteModel { let visitResult = search.next(); if (visitResult.done) { clearInterval(interval); + runInAction(() => { + this.remaining--; + }); return; } @@ -129,20 +204,67 @@ class DeepPasteModel { object => name == getProperty(object, "name") ); if (referencedObject) { - this.findObjectDependencies(referencedObject, { - inObject: getParent( - dependency.valueObject - ) as EezObject, - inPropertyName: - dependency.valueObject.propertyInfo.name, - fromIndex: 0, - toIndex: name.length - }); + this.findObjectDependencies( + referencedObject, + undefined, + { + inObject: getParent( + dependency.valueObject + ) as EezObject, + inPropertyName: + dependency.valueObject.propertyInfo + .name, + fromIndex: 0, + toIndex: name.length + } + ); } } } } }, 0); + + return objectsMapValue; + } + + doPaste() { + this.destinationProjectStore.undoManager.setCombineCommands(true); + + for (const objectsMapValue of this.objects.values()) { + const object = objectsMapValue.clonedObject; + if (object instanceof ProjectEditor.FlowFragmentClass) { + const editorState = + this.destinationProjectStore.editorsStore.activeEditor + ?.state; + if (editorState instanceof ProjectEditor.FlowTabStateClass) { + let fromObject; + if (editorState.widgetContainer.selectedItems.length == 0) { + fromObject = editorState.widgetContainer.object; + } else { + fromObject = + editorState.widgetContainer.selectedItems[0].object; + } + + ProjectEditor.FlowFragmentClass.paste( + this.destinationProjectStore, + editorState.flow, + object, + fromObject + ); + } + } else if (object instanceof ProjectEditor.BitmapClass) { + addObject(this.destinationProjectStore.project.bitmaps, object); + } else if (object instanceof ProjectEditor.StyleClass) { + if (getParent(object) == undefined) { + addObject( + this.destinationProjectStore.project.styles, + object + ); + } + } + } + + this.destinationProjectStore.undoManager.setCombineCommands(false); } } @@ -156,7 +278,7 @@ export const DeepPasteDialog = observer( onCancel: () => void; }> { onOkEnabled = () => { - return true; + return this.props.deepPasteModel.remaining == 0; }; onOk = () => { @@ -175,13 +297,24 @@ export const DeepPasteDialog = observer( onCancel={this.props.onCancel} >
- {[...objects.keys()].map(object => { - // const references = objects.get(object)!; - - return ( -
{getLabel(object)}
- ); - })} + {this.props.deepPasteModel.remaining > 0 && ( +
+
Searching for dependencies ...
+ +
+ )} +
+ {[...objects.keys()].map(object => { + // const references = objects.get(object)!; + + return ( +
+ {getClass(object).name}:{" "} + {getLabel(object)} +
+ ); + })} +
); @@ -234,13 +367,7 @@ export function deepPaste(projectStore: ProjectStore) { }; const onOk = () => { - copyProjectEditorDataToClipboard( - objectsToClipboardData(deepPasteModel.sourceProjectStore, [ - ...deepPasteModel.objects.keys() - ]) - ); - - deepPasteModel.destinationProjectStore.paste(); + deepPasteModel.doPaste(); onDispose(); };