@@ -37,6 +37,14 @@ import { DuplicateOnDragConnectionChecker } from "../../pxtblocks/plugins/duplic
37
37
import { PathObject } from "../../pxtblocks/plugins/renderer/pathObject" ;
38
38
import { Measurements } from "./constants" ;
39
39
40
+ interface CopyDataEntry {
41
+ version : 1 ;
42
+ data : Blockly . ICopyData ;
43
+ coord : Blockly . utils . Coordinate ;
44
+ workspaceId : string ;
45
+ targetVersion : string ;
46
+ }
47
+
40
48
41
49
export class Editor extends toolboxeditor . ToolboxEditor {
42
50
editor : Blockly . WorkspaceSvg ;
@@ -470,6 +478,8 @@ export class Editor extends toolboxeditor.ToolboxEditor {
470
478
if ( pxt . Util . isTranslationMode ( ) ) {
471
479
pxtblockly . external . setPromptTranslateBlock ( dialogs . promptTranslateBlock ) ;
472
480
}
481
+
482
+ pxtblockly . external . setCopyPaste ( copy , cut , this . pasteCallback ) ;
473
483
}
474
484
475
485
private initBlocklyToolbox ( ) {
@@ -1938,6 +1948,93 @@ export class Editor extends toolboxeditor.ToolboxEditor {
1938
1948
this . removeBreakpointFromEvent ( block . id )
1939
1949
}
1940
1950
}
1951
+
1952
+ protected pasteCallback = ( ) => {
1953
+ const data = getCopyData ( ) ;
1954
+ if ( ! data ?. data || ! this . editor ) return false ;
1955
+
1956
+ this . pasteAsync ( data ) ;
1957
+ return true ;
1958
+ }
1959
+
1960
+ protected async pasteAsync ( data : CopyDataEntry ) {
1961
+ const copyData = data . data ;
1962
+ const copyWorkspace = this . editor ;
1963
+ const copyCoords = copyWorkspace . id === data . workspaceId ? data . coord : undefined ;
1964
+
1965
+ // this pasting code is adapted from Blockly/core/shortcut_items.ts
1966
+ const doPaste = ( ) => {
1967
+ if ( ! copyCoords ) {
1968
+ // If we don't have location data about the original copyable, let the
1969
+ // paster determine position.
1970
+ return ! ! Blockly . clipboard . paste ( copyData , copyWorkspace ) ;
1971
+ }
1972
+
1973
+ const { left, top, width, height } = copyWorkspace
1974
+ . getMetricsManager ( )
1975
+ . getViewMetrics ( true ) ;
1976
+ const viewportRect = new Blockly . utils . Rect (
1977
+ top ,
1978
+ top + height ,
1979
+ left ,
1980
+ left + width
1981
+ ) ;
1982
+
1983
+ if ( viewportRect . contains ( copyCoords . x , copyCoords . y ) ) {
1984
+ // If the original copyable is inside the viewport, let the paster
1985
+ // determine position.
1986
+ return ! ! Blockly . clipboard . paste ( copyData , copyWorkspace ) ;
1987
+ }
1988
+
1989
+ // Otherwise, paste in the middle of the viewport.
1990
+ const centerCoords = new Blockly . utils . Coordinate (
1991
+ left + width / 2 ,
1992
+ top + height / 2
1993
+ ) ;
1994
+ return ! ! Blockly . clipboard . paste ( copyData , copyWorkspace , centerCoords ) ;
1995
+ } ;
1996
+
1997
+ if ( data . version !== 1 ) {
1998
+ await core . confirmAsync ( {
1999
+ header : lf ( "Paste Error" ) ,
2000
+ body : lf ( "The code you are pasting comes from an incompatible version of the editor." ) ,
2001
+ hideCancel : true
2002
+ } ) ;
2003
+
2004
+ return ;
2005
+ }
2006
+
2007
+ if ( copyData . paster === Blockly . clipboard . BlockPaster . TYPE ) {
2008
+ const typeCounts : { [ index : string ] : number } = ( copyData as any ) . typeCounts ;
2009
+
2010
+ for ( const blockType of Object . keys ( typeCounts ) ) {
2011
+ if ( ! Blockly . Blocks [ blockType ] ) {
2012
+ await core . confirmAsync ( {
2013
+ header : lf ( "Paste Error" ) ,
2014
+ body : lf ( "The code that you're trying to paste contains blocks that aren't available in the current project. If pasting from another project, make sure that you have installed all of the necessary extensions and try again." ) ,
2015
+ hideCancel : true
2016
+ } ) ;
2017
+
2018
+ return ;
2019
+ }
2020
+ }
2021
+ }
2022
+
2023
+ if ( data . targetVersion !== pxt . appTarget . versions . target ) {
2024
+ const result = await core . confirmAsync ( {
2025
+ header : lf ( "Paste Warning" ) ,
2026
+ body : lf ( "The code you're trying to paste is from a different version of Microsoft MakeCode. Pasting it may cause issues with your current project. Are you sure you want to continue?" ) ,
2027
+ agreeLbl : lf ( "Paste Anyway" ) ,
2028
+ agreeClass : "red"
2029
+ } ) ;
2030
+
2031
+ if ( result !== 1 ) {
2032
+ return ;
2033
+ }
2034
+ }
2035
+
2036
+ doPaste ( ) ;
2037
+ }
1941
2038
}
1942
2039
1943
2040
function forEachImageField ( workspace : Blockly . Workspace , cb : ( asset : pxtblockly . FieldAssetEditor < any , any > ) => void ) {
@@ -2028,4 +2125,101 @@ function resolveLocalizedMarkdown(url: string) {
2028
2125
}
2029
2126
2030
2127
return undefined ;
2128
+ }
2129
+
2130
+ // adapted from Blockly/core/shortcut_items.ts
2131
+ function copy ( workspace : Blockly . WorkspaceSvg , e : Event ) {
2132
+ // Prevent the default copy behavior, which may beep or otherwise indicate
2133
+ // an error due to the lack of a selection.
2134
+ e . preventDefault ( ) ;
2135
+ workspace . hideChaff ( ) ;
2136
+ const selected = Blockly . common . getSelected ( ) ;
2137
+ if ( ! selected || ! Blockly . isCopyable ( selected ) ) return false ;
2138
+
2139
+ const copyData = selected . toCopyData ( ) ;
2140
+ const copyWorkspace =
2141
+ selected . workspace instanceof Blockly . WorkspaceSvg
2142
+ ? selected . workspace
2143
+ : workspace ;
2144
+ const copyCoords = Blockly . isDraggable ( selected )
2145
+ ? selected . getRelativeToSurfaceXY ( )
2146
+ : null ;
2147
+
2148
+ if ( copyData ) {
2149
+ saveCopyData (
2150
+ copyData ,
2151
+ copyCoords ,
2152
+ copyWorkspace
2153
+ ) ;
2154
+ }
2155
+
2156
+ return ! ! copyData ;
2157
+ }
2158
+
2159
+ // adapted from Blockly/core/shortcut_items.ts
2160
+ function cut ( workspace : Blockly . WorkspaceSvg , e : Event ) {
2161
+ const selected = Blockly . common . getSelected ( ) ;
2162
+
2163
+ if ( selected instanceof Blockly . BlockSvg ) {
2164
+ const copyData = selected . toCopyData ( ) ;
2165
+ const copyWorkspace = workspace ;
2166
+ const copyCoords = selected . getRelativeToSurfaceXY ( ) ;
2167
+ saveCopyData (
2168
+ copyData ,
2169
+ copyCoords ,
2170
+ copyWorkspace
2171
+ ) ;
2172
+ selected . checkAndDelete ( ) ;
2173
+ return true ;
2174
+ } else if (
2175
+ Blockly . isDeletable ( selected ) &&
2176
+ selected . isDeletable ( ) &&
2177
+ Blockly . isCopyable ( selected )
2178
+ ) {
2179
+ const copyData = selected . toCopyData ( ) ;
2180
+ const copyWorkspace = workspace ;
2181
+ const copyCoords = Blockly . isDraggable ( selected )
2182
+ ? selected . getRelativeToSurfaceXY ( )
2183
+ : null ;
2184
+ saveCopyData (
2185
+ copyData ,
2186
+ copyCoords ,
2187
+ copyWorkspace
2188
+ ) ;
2189
+ selected . dispose ( ) ;
2190
+ return true ;
2191
+ }
2192
+ return false ;
2193
+ }
2194
+
2195
+ function saveCopyData (
2196
+ data : Blockly . ICopyData ,
2197
+ coord : Blockly . utils . Coordinate ,
2198
+ workspace : Blockly . Workspace
2199
+ ) {
2200
+ const entry : CopyDataEntry = {
2201
+ version : 1 ,
2202
+ data,
2203
+ coord,
2204
+ workspaceId : workspace . id ,
2205
+ targetVersion : pxt . appTarget . versions . target
2206
+ } ;
2207
+
2208
+ pxt . storage . setLocal (
2209
+ copyDataKey ( ) ,
2210
+ JSON . stringify ( entry )
2211
+ ) ;
2212
+ }
2213
+
2214
+ function getCopyData ( ) : CopyDataEntry | undefined {
2215
+ const data = pxt . storage . getLocal ( copyDataKey ( ) ) ;
2216
+
2217
+ if ( data ) {
2218
+ return pxt . U . jsonTryParse ( data ) ;
2219
+ }
2220
+ return undefined ;
2221
+ }
2222
+
2223
+ function copyDataKey ( ) {
2224
+ return "copyData" ;
2031
2225
}
0 commit comments