From 95293d3dda048630f260d02540a4b464dfb4acde Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 2 Dec 2024 18:14:56 +0530 Subject: [PATCH 1/5] Copy & Paste objects --- packages/base/src/commands.ts | 68 ++++++++++++++++++++++++++++++ packages/base/src/keybindings.json | 10 +++++ 2 files changed, 78 insertions(+) diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index aa687bb3..7cce91dd 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -1181,6 +1181,71 @@ export function addCommands( await dialog.launch(); } }); + commands.addCommand(CommandIDs.copyObject, { + label: trans.__('Copy Object'), + isEnabled: () => { + const current = tracker.currentWidget; + return current ? current.context.model.sharedModel.editable : false; + }, + execute: () => { + const current = tracker.currentWidget; + if (!current) { + return; + } + + const objectId = getSelectedObjectId(current); + if (!objectId) { + console.warn('No object is selected to copy.'); + return; + } + + const sharedModel = current.context.model.sharedModel; + const objectData = sharedModel.getObjectByName(objectId); + + if (!objectData) { + console.warn('Could not retrieve object data.'); + return; + } + + sharedModel.awareness.setLocalStateField('clipboard', objectData); + } + }); + commands.addCommand(CommandIDs.pasteObject, { + label: trans.__('Paste Object'), + isEnabled: () => { + const current = tracker.currentWidget; + const clipboard = + current?.context.model.sharedModel.awareness.getLocalState()?.clipboard; + return current && clipboard && current.context.model.sharedModel.editable; + }, + execute: () => { + const current = tracker.currentWidget; + if (!current) { + return; + } + + const sharedModel = current.context.model.sharedModel; + const clipboard = sharedModel.awareness.getLocalState()?.clipboard; + + if (!clipboard) { + console.warn('No data in clipboard to paste.'); + return; + } + + const originalName = clipboard.name || 'Unnamed Object'; + let newName = originalName; + + let counter = 1; + while (sharedModel.objects.some(obj => obj.name === newName)) { + newName = `${originalName} Copy${counter > 1 ? ` ${counter}` : ''}`; + counter++; + } + + const newObject = { ...clipboard, name: newName }; + sharedModel.addObject(newObject); + } + }); + loadKeybindings(commands, keybindings); } @@ -1208,6 +1273,9 @@ export namespace CommandIDs { export const wireframe = 'jupytercad:wireframe'; export const transform = 'jupytercad:transform'; + export const copyObject = 'jupytercad:copyObject'; + export const pasteObject = 'jupytercad:pasteObject'; + export const chamfer = 'jupytercad:chamfer'; export const fillet = 'jupytercad:fillet'; diff --git a/packages/base/src/keybindings.json b/packages/base/src/keybindings.json index 0ae48675..1bba967e 100644 --- a/packages/base/src/keybindings.json +++ b/packages/base/src/keybindings.json @@ -118,5 +118,15 @@ "command": "jupytercad:exportJcad", "keys": ["Accel Alt X"], "selector": ".data-jcad-keybinding" + }, + { + "command": "jupytercad:copyObject", + "keys": ["Accel C"], + "selector": ".data-jcad-keybinding" + }, + { + "command": "jupytercad:pasteObject", + "keys": ["Accel V"], + "selector": ".data-jcad-keybinding" } ] From 3a383b07f938136cf04c759766f954bfbe7c8ea3 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 2 Dec 2024 18:31:11 +0530 Subject: [PATCH 2/5] no need for warning --- packages/base/src/commands.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index 7cce91dd..f8d2d622 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -1194,10 +1194,6 @@ export function addCommands( } const objectId = getSelectedObjectId(current); - if (!objectId) { - console.warn('No object is selected to copy.'); - return; - } const sharedModel = current.context.model.sharedModel; const objectData = sharedModel.getObjectByName(objectId); @@ -1227,11 +1223,6 @@ export function addCommands( const sharedModel = current.context.model.sharedModel; const clipboard = sharedModel.awareness.getLocalState()?.clipboard; - if (!clipboard) { - console.warn('No data in clipboard to paste.'); - return; - } - const originalName = clipboard.name || 'Unnamed Object'; let newName = originalName; From 9d45af2f841032421ad44808e3753dbbdb3ac543 Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 2 Dec 2024 21:00:49 +0530 Subject: [PATCH 3/5] select newly copied object --- packages/base/src/commands.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index f8d2d622..b6aa8e55 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -1231,9 +1231,10 @@ export function addCommands( newName = `${originalName} Copy${counter > 1 ? ` ${counter}` : ''}`; counter++; } - + const jcadModel = current.context.model; const newObject = { ...clipboard, name: newName }; sharedModel.addObject(newObject); + jcadModel.syncSelected({ [newObject.name]: { type: 'shape' } }, uuid()); } }); From db651c5cfc719eac542f40208e65ad088b6e921d Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Mon, 2 Dec 2024 22:33:03 +0530 Subject: [PATCH 4/5] store Copied Object in model --- packages/base/src/commands.ts | 17 +++++++++++------ packages/schema/src/interfaces.ts | 3 +++ packages/schema/src/model.ts | 10 ++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index b6aa8e55..aa8c1b36 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -1194,7 +1194,6 @@ export function addCommands( } const objectId = getSelectedObjectId(current); - const sharedModel = current.context.model.sharedModel; const objectData = sharedModel.getObjectByName(objectId); @@ -1203,16 +1202,16 @@ export function addCommands( return; } - sharedModel.awareness.setLocalStateField('clipboard', objectData); + current.context.model.setCopiedObject([objectData]); } }); commands.addCommand(CommandIDs.pasteObject, { label: trans.__('Paste Object'), isEnabled: () => { const current = tracker.currentWidget; - const clipboard = - current?.context.model.sharedModel.awareness.getLocalState()?.clipboard; - return current && clipboard && current.context.model.sharedModel.editable; + const clipboard = current?.context.model.getCopiedObject(); + const editable = current?.context.model.sharedModel.editable; + return !!(current && clipboard && editable); }, execute: () => { const current = tracker.currentWidget; @@ -1221,7 +1220,13 @@ export function addCommands( } const sharedModel = current.context.model.sharedModel; - const clipboard = sharedModel.awareness.getLocalState()?.clipboard; + const copiedObjects = current.context.model.getCopiedObject(); + if (!copiedObjects || copiedObjects.length === 0) { + console.error('No object in clipboard to paste.'); + return; + } + + const clipboard = copiedObjects[0]; const originalName = clipboard.name || 'Unnamed Object'; let newName = originalName; diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index 87eb4a70..87874fe2 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -175,6 +175,9 @@ export interface IJupyterCadModel extends DocumentRegistry.IModel { addMetadata(key: string, value: string): void; removeMetadata(key: string): void; + getCopiedObject(): IJCadModel | null; + setCopiedObject(objectData: IJCadModel): void; + disposed: ISignal; } diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index 61abd9fb..22ab59fe 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -30,6 +30,7 @@ export class JupyterCadModel implements IJupyterCadModel { } this._connectSignal(); this.annotationModel = annotationModel; + this._copiedObject = null; } readonly collaborative = @@ -238,6 +239,14 @@ export class JupyterCadModel implements IJupyterCadModel { this.sharedModel.removeMetadata(key); } + setCopiedObject(object: IJCadModel | null): void { + this._copiedObject = object ? { ...object } : null; + } + + getCopiedObject(): IJCadModel | null { + return this._copiedObject ? { ...this._copiedObject } : null; + } + protected createSharedModel(): IJupyterCadDoc { return JupyterCadDoc.create(); } @@ -304,6 +313,7 @@ export class JupyterCadModel implements IJupyterCadModel { readonly annotationModel?: IAnnotationModel; private _sharedModel: IJupyterCadDoc; + private _copiedObject: IJCadModel | null; private _dirty = false; private _readOnly = false; From 71acb2901a1a323172d073d9751f669700030d3c Mon Sep 17 00:00:00 2001 From: arjxn-py Date: Tue, 3 Dec 2024 15:09:58 +0530 Subject: [PATCH 5/5] use `IJCadObject` instead of `IJCadModel` --- packages/base/src/commands.ts | 8 ++++---- packages/schema/src/interfaces.ts | 4 ++-- packages/schema/src/model.ts | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index aa8c1b36..e98f8c26 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -1202,7 +1202,7 @@ export function addCommands( return; } - current.context.model.setCopiedObject([objectData]); + current.context.model.setCopiedObject(objectData); } }); commands.addCommand(CommandIDs.pasteObject, { @@ -1220,13 +1220,13 @@ export function addCommands( } const sharedModel = current.context.model.sharedModel; - const copiedObjects = current.context.model.getCopiedObject(); - if (!copiedObjects || copiedObjects.length === 0) { + const copiedObject = current.context.model.getCopiedObject(); + if (!copiedObject) { console.error('No object in clipboard to paste.'); return; } - const clipboard = copiedObjects[0]; + const clipboard = copiedObject; const originalName = clipboard.name || 'Unnamed Object'; let newName = originalName; diff --git a/packages/schema/src/interfaces.ts b/packages/schema/src/interfaces.ts index 87874fe2..8a7273c9 100644 --- a/packages/schema/src/interfaces.ts +++ b/packages/schema/src/interfaces.ts @@ -175,8 +175,8 @@ export interface IJupyterCadModel extends DocumentRegistry.IModel { addMetadata(key: string, value: string): void; removeMetadata(key: string): void; - getCopiedObject(): IJCadModel | null; - setCopiedObject(objectData: IJCadModel): void; + getCopiedObject(): IJCadObject | null; + setCopiedObject(objectData: IJCadObject): void; disposed: ISignal; } diff --git a/packages/schema/src/model.ts b/packages/schema/src/model.ts index 22ab59fe..57d1d419 100644 --- a/packages/schema/src/model.ts +++ b/packages/schema/src/model.ts @@ -5,7 +5,7 @@ import { PartialJSONObject } from '@lumino/coreutils'; import { ISignal, Signal } from '@lumino/signaling'; import Ajv from 'ajv'; -import { IJCadContent, IJCadModel } from './_interface/jcad'; +import { IJCadContent, IJCadModel, IJCadObject } from './_interface/jcad'; import { JupyterCadDoc } from './doc'; import { Camera, @@ -239,11 +239,11 @@ export class JupyterCadModel implements IJupyterCadModel { this.sharedModel.removeMetadata(key); } - setCopiedObject(object: IJCadModel | null): void { + setCopiedObject(object: IJCadObject | null): void { this._copiedObject = object ? { ...object } : null; } - getCopiedObject(): IJCadModel | null { + getCopiedObject(): IJCadObject | null { return this._copiedObject ? { ...this._copiedObject } : null; } @@ -313,7 +313,7 @@ export class JupyterCadModel implements IJupyterCadModel { readonly annotationModel?: IAnnotationModel; private _sharedModel: IJupyterCadDoc; - private _copiedObject: IJCadModel | null; + private _copiedObject: IJCadObject | null; private _dirty = false; private _readOnly = false;