Skip to content

Commit 9685498

Browse files
committed
Add isCopyable and isCuttable as optional methods on ICopyable
1 parent 02f89d6 commit 9685498

File tree

4 files changed

+62
-51
lines changed

4 files changed

+62
-51
lines changed

core/block_svg.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,6 +1721,16 @@ export class BlockSvg
17211721
this.dragStrategy = dragStrategy;
17221722
}
17231723

1724+
/** Returns whether this block is copyable or not. */
1725+
isCopyable(): boolean {
1726+
return this.isOwnDeletable() && this.isOwnMovable();
1727+
}
1728+
1729+
/** Returns whether this block is cuttable or not. */
1730+
isCuttable(): boolean {
1731+
return this.isDeletable() && this.isMovable();
1732+
}
1733+
17241734
/** Returns whether this block is movable or not. */
17251735
override isMovable(): boolean {
17261736
return this.dragStrategy.isMovable();

core/comments/rendered_workspace_comment.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,16 @@ export class RenderedWorkspaceComment
244244
}
245245
}
246246

247+
/** Returns whether this comment is copyable or not */
248+
isCopyable(): boolean {
249+
return this.isOwnMovable() && this.isOwnDeletable();
250+
}
251+
252+
/** Returns whether this comment is cuttable or not */
253+
isCuttable(): boolean {
254+
return this.isMovable() && this.isDeletable();
255+
}
256+
247257
/** Returns whether this comment is movable or not. */
248258
isMovable(): boolean {
249259
return this.dragStrategy.isMovable();

core/interfaces/i_copyable.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ export interface ICopyable<T extends ICopyData> extends ISelectable {
1515
* @returns Copy metadata.
1616
*/
1717
toCopyData(): T | null;
18+
19+
/**
20+
* Whether this instance is currently copyable.
21+
*
22+
* @returns True if it can currently be copied.
23+
*/
24+
isCopyable?(): boolean;
25+
26+
/**
27+
* Whether this instance is currently cuttable.
28+
*
29+
* @returns True if it can currently be cut.
30+
*/
31+
isCuttable?(): boolean;
1832
}
1933

2034
export namespace ICopyable {
@@ -25,7 +39,7 @@ export namespace ICopyable {
2539

2640
export type ICopyData = ICopyable.ICopyData;
2741

28-
/** @returns true if the given object is copyable. */
42+
/** @returns true if the given object is an ICopyable. */
2943
export function isCopyable(obj: any): obj is ICopyable<ICopyData> {
3044
return obj.toCopyData !== undefined;
3145
}

core/shortcut_items.ts

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import {BlockSvg} from './block_svg.js';
1010
import * as clipboard from './clipboard.js';
11+
import { RenderedWorkspaceComment } from './comments.js';
1112
import * as eventUtils from './events/utils.js';
1213
import {getFocusManager} from './focus_manager.js';
1314
import {Gesture} from './gesture.js';
@@ -106,68 +107,44 @@ let copyCoords: Coordinate | null = null;
106107
/**
107108
* Determine if a focusable node can be copied.
108109
*
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.
110+
* This will use the isCopyable method if the node implements it, otherwise
111+
* it will fall back to checking if the node is deletable and draggable not
112+
* considering the workspace's edit state.
124113
*
125114
* @param focused The focused object.
126115
*/
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-
);
116+
function isCopyable(focused: IFocusableNode): boolean {
117+
if (!isICopyable(focused) || !isIDeletable(focused) || !isDraggable(focused))
118+
return false;
119+
if (focused.isCopyable !== undefined) {
120+
return focused.isCopyable();
121+
} else if (
122+
focused instanceof BlockSvg ||
123+
focused instanceof RenderedWorkspaceComment
124+
) {
125+
return focused.isOwnDeletable() && focused.isOwnMovable();
126+
}
127+
// This isn't a class Blockly knows about, so fall back to the stricter
128+
// checks for deletable and movable.
129+
return focused.isDeletable() && focused.isMovable();
138130
}
139131

140132
/**
141133
* Determine if a focusable node can be cut.
142134
*
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.
135+
* This will use the isCuttable method if the node implements it, otherwise
136+
* it will fall back to checking if the node can be moved and deleted in its
137+
* current workspace.
159138
*
160139
* @param focused The focused object.
161140
*/
162141
function 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-
);
142+
if (!isICopyable(focused) || !isIDeletable(focused) || !isDraggable(focused))
143+
return false;
144+
if (focused.isCuttable !== undefined) {
145+
return focused.isCuttable();
146+
}
147+
return focused.isMovable() && focused.isDeletable();
171148
}
172149

173150
/**

0 commit comments

Comments
 (0)