88
99import { BlockSvg } from './block_svg.js' ;
1010import * as clipboard from './clipboard.js' ;
11+ import { RenderedWorkspaceComment } from './comments.js' ;
1112import * as eventUtils from './events/utils.js' ;
1213import { getFocusManager } from './focus_manager.js' ;
1314import { Gesture } from './gesture.js' ;
14- import {
15- ICopyable ,
16- ICopyData ,
17- isCopyable as isICopyable ,
18- } from './interfaces/i_copyable.js' ;
19- import {
20- IDeletable ,
21- isDeletable as isIDeletable ,
22- } from './interfaces/i_deletable.js' ;
23- import { IDraggable , isDraggable } from './interfaces/i_draggable.js' ;
15+ import { ICopyData , isCopyable as isICopyable } from './interfaces/i_copyable.js' ;
16+ import { isDeletable as isIDeletable } from './interfaces/i_deletable.js' ;
17+ import { isDraggable } from './interfaces/i_draggable.js' ;
2418import { IFocusableNode } from './interfaces/i_focusable_node.js' ;
2519import { KeyboardShortcut , ShortcutRegistry } from './shortcut_registry.js' ;
2620import { Coordinate } from './utils/coordinate.js' ;
@@ -100,74 +94,43 @@ export function registerDelete() {
10094}
10195
10296let copyData : ICopyData | null = null ;
103- let copyWorkspace : WorkspaceSvg | null = null ;
10497let copyCoords : Coordinate | null = null ;
10598
10699/**
107100 * Determine if a focusable node can be copied.
108101 *
109- * Unfortunately the ICopyable interface doesn't include an isCopyable
110- * method, so we must use some other criteria to make the decision.
111- * Specifically,
112- *
113- * - It must be an ICopyable.
114- * - So that a pasted copy can be manipluated and/or disposed of, it
115- * must be both an IDraggable and an IDeletable.
116- * - Additionally, both .isOwnMovable() and .isOwnDeletable() must return
117- * true (i.e., the copy could be moved and deleted).
118- *
119- * TODO(#9098): Revise these criteria. The latter criteria prevents
120- * shadow blocks from being copied; additionally, there are likely to
121- * be other circumstances were it is desirable to allow movable /
122- * copyable copies of a currently-unmovable / -copyable block to be
123- * made.
102+ * This will use the isCopyable method if the node implements it, otherwise
103+ * it will fall back to checking if the node is deletable and draggable not
104+ * considering the workspace's edit state.
124105 *
125106 * @param focused The focused object.
126107 */
127- function isCopyable (
128- focused : IFocusableNode ,
129- ) : focused is ICopyable < ICopyData > & IDeletable & IDraggable {
130- if ( ! ( focused instanceof BlockSvg ) ) return false ;
131- return (
132- isICopyable ( focused ) &&
133- isIDeletable ( focused ) &&
134- focused . isOwnDeletable ( ) &&
135- isDraggable ( focused ) &&
136- focused . isOwnMovable ( )
137- ) ;
108+ function isCopyable ( focused : IFocusableNode ) : boolean {
109+ if ( ! isICopyable ( focused ) || ! isIDeletable ( focused ) || ! isDraggable ( focused ) )
110+ return false ;
111+ if ( focused . isCopyable ) {
112+ return focused . isCopyable ( ) ;
113+ } else if (
114+ focused instanceof BlockSvg ||
115+ focused instanceof RenderedWorkspaceComment
116+ ) {
117+ return focused . isOwnDeletable ( ) && focused . isOwnMovable ( ) ;
118+ }
119+ // This isn't a class Blockly knows about, so fall back to the stricter
120+ // checks for deletable and movable.
121+ return focused . isDeletable ( ) && focused . isMovable ( ) ;
138122}
139123
140124/**
141125 * Determine if a focusable node can be cut.
142126 *
143- * Unfortunately the ICopyable interface doesn't include an isCuttable
144- * method, so we must use some other criteria to make the decision.
145- * Specifically,
146- *
147- * - It must be an ICopyable.
148- * - So that a pasted copy can be manipluated and/or disposed of, it
149- * must be both an IDraggable and an IDeletable.
150- * - Additionally, both .isMovable() and .isDeletable() must return
151- * true (i.e., can currently be moved and deleted). This is the main
152- * difference with isCopyable.
153- *
154- * TODO(#9098): Revise these criteria. The latter criteria prevents
155- * shadow blocks from being copied; additionally, there are likely to
156- * be other circumstances were it is desirable to allow movable /
157- * copyable copies of a currently-unmovable / -copyable block to be
158- * made.
127+ * This will check if the node can be both copied and deleted in its current
128+ * workspace.
159129 *
160130 * @param focused The focused object.
161131 */
162132function isCuttable ( focused : IFocusableNode ) : boolean {
163- if ( ! ( focused instanceof BlockSvg ) ) return false ;
164- return (
165- isICopyable ( focused ) &&
166- isIDeletable ( focused ) &&
167- focused . isDeletable ( ) &&
168- isDraggable ( focused ) &&
169- focused . isMovable ( )
170- ) ;
133+ return isCopyable ( focused ) && isIDeletable ( focused ) && focused . isDeletable ( ) ;
171134}
172135
173136/**
@@ -185,15 +148,13 @@ export function registerCopy() {
185148 name : names . COPY ,
186149 preconditionFn ( workspace , scope ) {
187150 const focused = scope . focusedNode ;
188- if ( ! ( focused instanceof BlockSvg ) ) return false ;
189151
190152 const targetWorkspace = workspace . isFlyout
191153 ? workspace . targetWorkspace
192154 : workspace ;
193155 return (
194156 ! ! focused &&
195157 ! ! targetWorkspace &&
196- ! targetWorkspace . isReadOnly ( ) &&
197158 ! targetWorkspace . isDragging ( ) &&
198159 ! getFocusManager ( ) . ephemeralFocusTaken ( ) &&
199160 isCopyable ( focused )
@@ -205,21 +166,17 @@ export function registerCopy() {
205166 e . preventDefault ( ) ;
206167
207168 const focused = scope . focusedNode ;
208- if ( ! focused || ! isCopyable ( focused ) ) return false ;
209- let targetWorkspace : WorkspaceSvg | null =
210- focused . workspace instanceof WorkspaceSvg
211- ? focused . workspace
212- : workspace ;
213- targetWorkspace = targetWorkspace . isFlyout
214- ? targetWorkspace . targetWorkspace
215- : targetWorkspace ;
169+ if ( ! focused || ! isICopyable ( focused ) || ! isCopyable ( focused ) )
170+ return false ;
171+ const targetWorkspace = workspace . isFlyout
172+ ? workspace . targetWorkspace
173+ : workspace ;
216174 if ( ! targetWorkspace ) return false ;
217175
218176 if ( ! focused . workspace . isFlyout ) {
219177 targetWorkspace . hideChaff ( ) ;
220178 }
221179 copyData = focused . toCopyData ( ) ;
222- copyWorkspace = targetWorkspace ;
223180 copyCoords =
224181 isDraggable ( focused ) && focused . workspace == targetWorkspace
225182 ? focused . getRelativeToSurfaceXY ( )
@@ -256,27 +213,20 @@ export function registerCut() {
256213 } ,
257214 callback ( workspace , e , shortcut , scope ) {
258215 const focused = scope . focusedNode ;
216+ if ( ! focused || ! isCuttable ( focused ) || ! isICopyable ( focused ) ) {
217+ return false ;
218+ }
219+ copyData = focused . toCopyData ( ) ;
220+ copyCoords = isDraggable ( focused )
221+ ? focused . getRelativeToSurfaceXY ( )
222+ : null ;
259223
260224 if ( focused instanceof BlockSvg ) {
261- copyData = focused . toCopyData ( ) ;
262- copyWorkspace = workspace ;
263- copyCoords = focused . getRelativeToSurfaceXY ( ) ;
264225 focused . checkAndDelete ( ) ;
265- return true ;
266- } else if (
267- isIDeletable ( focused ) &&
268- focused . isDeletable ( ) &&
269- isICopyable ( focused )
270- ) {
271- copyData = focused . toCopyData ( ) ;
272- copyWorkspace = workspace ;
273- copyCoords = isDraggable ( focused )
274- ? focused . getRelativeToSurfaceXY ( )
275- : null ;
226+ } else if ( isIDeletable ( focused ) ) {
276227 focused . dispose ( ) ;
277- return true ;
278228 }
279- return false ;
229+ return ! ! copyData ;
280230 } ,
281231 keyCodes : [ ctrlX , metaX ] ,
282232 } ;
@@ -310,7 +260,11 @@ export function registerPaste() {
310260 ) ;
311261 } ,
312262 callback ( workspace : WorkspaceSvg , e : Event ) {
313- if ( ! copyData || ! copyWorkspace ) return false ;
263+ if ( ! copyData ) return false ;
264+ const targetWorkspace = workspace . isFlyout
265+ ? workspace . targetWorkspace
266+ : workspace ;
267+ if ( ! targetWorkspace || targetWorkspace . isReadOnly ( ) ) return false ;
314268
315269 if ( e instanceof PointerEvent ) {
316270 // The event that triggers a shortcut would conventionally be a KeyboardEvent.
@@ -319,32 +273,32 @@ export function registerPaste() {
319273 // at the mouse coordinates where the menu was opened, and this PointerEvent
320274 // is where the menu was opened.
321275 const mouseCoords = svgMath . screenToWsCoordinates (
322- copyWorkspace ,
276+ targetWorkspace ,
323277 new Coordinate ( e . clientX , e . clientY ) ,
324278 ) ;
325- return ! ! clipboard . paste ( copyData , copyWorkspace , mouseCoords ) ;
279+ return ! ! clipboard . paste ( copyData , targetWorkspace , mouseCoords ) ;
326280 }
327281
328282 if ( ! copyCoords ) {
329283 // If we don't have location data about the original copyable, let the
330284 // paster determine position.
331- return ! ! clipboard . paste ( copyData , copyWorkspace ) ;
285+ return ! ! clipboard . paste ( copyData , targetWorkspace ) ;
332286 }
333287
334- const { left, top, width, height} = copyWorkspace
288+ const { left, top, width, height} = targetWorkspace
335289 . getMetricsManager ( )
336290 . getViewMetrics ( true ) ;
337291 const viewportRect = new Rect ( top , top + height , left , left + width ) ;
338292
339293 if ( viewportRect . contains ( copyCoords . x , copyCoords . y ) ) {
340294 // If the original copyable is inside the viewport, let the paster
341295 // determine position.
342- return ! ! clipboard . paste ( copyData , copyWorkspace ) ;
296+ return ! ! clipboard . paste ( copyData , targetWorkspace ) ;
343297 }
344298
345299 // Otherwise, paste in the middle of the viewport.
346300 const centerCoords = new Coordinate ( left + width / 2 , top + height / 2 ) ;
347- return ! ! clipboard . paste ( copyData , copyWorkspace , centerCoords ) ;
301+ return ! ! clipboard . paste ( copyData , targetWorkspace , centerCoords ) ;
348302 } ,
349303 keyCodes : [ ctrlV , metaV ] ,
350304 } ;
0 commit comments