Skip to content
Open
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6b4b3f2
Update to Blockly 12.5.1
mjgallag Jun 24, 2026
74c4c45
Blockly 12 renamed setEnabled to setDisabledReason
mjgallag Jan 10, 2026
045ed33
Blockly 12 dropped Ctrl shortcut key on Mac
mjgallag Apr 8, 2026
c210adc
Remove duplicate blockly[Non]EditableText CSS
mjgallag Jan 10, 2026
a9e692e
Remove duplicate blocklyScrollbarKnob CSS
mjgallag Jan 10, 2026
24b37e7
Remove duplicate CSS
mjgallag Jan 10, 2026
3d7105c
Remove unneeded setSelected(null) from tests
mjgallag Jan 10, 2026
35775b3
Update to @blockly/plugin-scroll-options 7.0.8
mjgallag Jan 12, 2026
21cb8f2
Update to @blockly/plugin-workspace-search 10.1.7
mjgallag Jan 12, 2026
29a26b5
Update to @blockly/block-dynamic-connection 0.8.8
mjgallag Jan 12, 2026
7a5c12a
Update to @blockly/field-colour 6.0.11
mjgallag Jan 12, 2026
b9b6de7
Update typeblocking to josmas-pr5-414bba9
mjgallag Jan 13, 2026
2698ce0
Update block-lexical-variables to pr68-db9c159
mjgallag Feb 18, 2026
ac2f5f3
Update keyboard-navigation to 3.0.5
mjgallag Apr 20, 2026
e51a741
Update workspace-multiselect to pr133-f26447c
mjgallag Feb 10, 2026
3cc7612
Show Blockly shortcuts from Help menu in Blocks editor
mjgallag May 7, 2026
b247f66
Blockly 12 expects hidden workspaces to be attached
mjgallag May 13, 2026
05f4e67
Attach BlocklyPanel before initializing workspace
mjgallag May 15, 2026
5d00908
Update to Blockly 13.2.1
mjgallag Aug 13, 2026
2b1c340
Revert "Show Blockly shortcuts from Help menu in Blocks editor"
mjgallag Aug 14, 2026
c323c42
Remove @blockly/keyboard-navigation
mjgallag Aug 14, 2026
8dbb02d
Update to @blockly/plugin-scroll-options 13.2.0
mjgallag Aug 14, 2026
0c8fd21
Update to @blockly/plugin-workspace-search 13.2.0
mjgallag Aug 14, 2026
ad43c8d
Update to @blockly/block-dynamic-connection 13.2.0
mjgallag Aug 14, 2026
1c27e25
Update to @blockly/field-colour 13.2.0
mjgallag Aug 14, 2026
30c06b5
Update typeblocking to josmas-pr6-fd54c90
mjgallag Aug 14, 2026
2b1cc35
Update block-lexical-variables to pr88-71ee694
mjgallag Aug 14, 2026
dabe4ad
Update @blockly/field-multilineinput to 13.2.0
mjgallag Aug 14, 2026
5bad8e7
Update workspace-multiselect to pr154-e4e120e
mjgallag Aug 14, 2026
0dc2093
Remap Blockly shortcut keys for Typeblocking
mjgallag Aug 14, 2026
c29d2b9
Preserve App Inventor Blockly sprite colors
mjgallag Aug 14, 2026
367f7da
Move BlocksEditor workspace initialization to onLoad
mjgallag Aug 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public abstract class ProjectEditor extends Composite {
protected final long projectId;
protected final Project project;

// Invariants: openFileEditors, fileIds, and deckPanel contain corresponding
// Invariants: fileIds and deckPanel contain corresponding
// elements, i.e., if a FileEditor is in openFileEditors, its fileid should be
// in fileIds and the FileEditor should be in deckPanel. If selectedFileEditor
// is non-null, it is one of the file editors in openFileEditors and the
Expand Down Expand Up @@ -163,13 +163,22 @@ public final void buildScreenHashMap() {
* @param beforeIndex the index before which fileEditor will be inserted
*/
public final void insertFileEditor(FileEditor fileEditor, int beforeIndex) {
String fileId = fileEditor.getFileId();
openFileEditors.put(fileId, fileEditor);
fileIds.add(beforeIndex, fileId);
fileIds.add(beforeIndex, fileEditor.getFileId());
deckPanel.insert(fileEditor, beforeIndex);
LOG.info("Inserted file editor for " + fileEditor.getFileId() + " at pos " + beforeIndex);
}

/**
* Opens an inserted file editor.
*
* @param fileEditor file editor to open
*/
public final void openFileEditor(FileEditor fileEditor) {
String fileId = fileEditor.getFileId();
openFileEditors.put(fileId, fileEditor);
LOG.info("Opened file editor for " + fileEditor.getFileId());
}

protected final void addFileEditorByType(FileEditor fileEditor) {
String entityName = SourceNode.getEntityName(fileEditor.getFileId());
if (!editorsByType.containsKey(entityName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public interface BlocklyWorkspaceChangeListener {
// My entity name
private final String formName;

private final String projectId;

private final boolean readOnly;

/**
* Objects registered to listen for workspace changes.
*/
Expand Down Expand Up @@ -140,22 +144,9 @@ public BlocklyPanel(String formName, BlocksCodeGenerationTarget targetPlatform,
getElement().addClassName("svg");
getElement().setId(formName);
this.formName = formName;
this.projectId = formName.split("_")[0];
this.readOnly = readOnly;
this.targetPlatform = targetPlatform;
String projectId = formName.split("_")[0];

/* Blockly initialization now occurs in three stages. This is due to the fact that certain
* Blockly objects rely on SVG methods such as getScreenCTM(), which are not properly
* initialized and/or null prior to the svg element being attached to the DOM. The first
* stage of initialization happens here.
*
* Stages 2 and 3 can occur in different orders depending on network latency. On a fast
* connection, the second stage will be loading of the .bky content into the workspace.
* The third stage will then be rendering of the workspace when the user switches to the
* Blocks editor. On slow connections, the workspace may render blank until the blocks file
* has been downloaded from the server.
*/
initWorkspace(projectId, readOnly, LocaleInfo.getCurrentLocale().isRTL(), targetPlatform.getTarget());

LOG.info("Created BlocklyPanel for " + formName);
}

Expand Down Expand Up @@ -722,12 +713,29 @@ private static native void exportMethodsToJavascript() /*-{
$entry(@com.google.appinventor.client.editor.blocks.BlocklyPanel::getComponentContainerUuid(*));
}-*/;

private native void initWorkspace(String projectId, boolean readOnly, boolean rtl, String targetLang)/*-{
/* Blockly initialization now occurs in three stages. This is due to the fact that certain
* Blockly objects rely on SVG methods such as getScreenCTM(), which are not properly
* initialized and/or null prior to the svg element being attached to the DOM. The first
* stage of initialization happens here.
*
* Stages 2 and 3 can occur in different orders depending on network latency. On a fast
* connection, the second stage will be loading of the .bky content into the workspace.
* The third stage will then be rendering of the workspace when the user switches to the
* Blocks editor. On slow connections, the workspace may render blank until the blocks file
* has been downloaded from the server.
*/
public void initWorkspace() {
initWorkspace(LocaleInfo.getCurrentLocale().isRTL(), targetPlatform.getTarget());
LOG.info("Initialized Blockly workspace for " + formName);
}

private native void initWorkspace(boolean rtl, String targetLang)/*-{
var el = this.@com.google.gwt.user.client.ui.UIObject::getElement()();
var workspace = $wnd.Blockly.BlocklyEditor.create(el,
this.@com.google.appinventor.client.editor.blocks.BlocklyPanel::formName,
readOnly, rtl, targetLang);
workspace.projectId = projectId;
this.@com.google.appinventor.client.editor.blocks.BlocklyPanel::readOnly,
rtl, targetLang);
workspace.projectId = this.@com.google.appinventor.client.editor.blocks.BlocklyPanel::projectId;
var cb = $entry(this.@com.google.appinventor.client.editor.blocks.BlocklyPanel::workspaceChanged(Lcom/google/gwt/core/client/JavaScriptObject;));
cb = cb.bind(this);
workspace.addChangeListener(function(e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,8 @@ public BlocksEditor(ProjectEditor projectEditor, S blocksNode, int systemVersion
// New layouts don't need all this messing; see comments on selected answer at:
// http://stackoverflow.com/questions/86901/creating-a-fluid-panel-in-gwt-to-fill-the-page
initWidget(blocksArea);
blocksArea.populateComponentTypes(componentDatabase.getComponentsJSONString());

// Get references to the source structure explorer
sourceStructureExplorer = BlockSelectorBox.getBlockSelectorBox().getSourceStructureExplorer();

// Listen for selection events for built-in drawers
BlockSelectorBox.getBlockSelectorBox().addBlockDrawerSelectionListener(this);

designer = (T) projectEditor.getFileEditor(blocksNode.getEntityName(), DesignerEditor.EDITOR_TYPE);
formToBlocksEditor.put(entityName, this);
}

public abstract void prepareForUnload();
Expand Down Expand Up @@ -218,6 +210,17 @@ public boolean isLoaded() {
return loadComplete;
}

public void initWorkspace() {
blocksArea.initWorkspace();
blocksArea.populateComponentTypes(componentDatabase.getComponentsJSONString());

// Listen for selection events for built-in drawers
BlockSelectorBox.getBlockSelectorBox().addBlockDrawerSelectionListener(this);

designer = (T) projectEditor.getFileEditor(blocksNode.getEntityName(), DesignerEditor.EDITOR_TYPE);
formToBlocksEditor.put(entityName, this);
}

public WorkspaceSvg getWorkspace() {
return blocksArea.getWorkspace();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ public final class YaBlocksEditor extends BlocksEditor<YoungAndroidBlocksNode, Y
super(projectEditor, blocksNode, YaVersion.YOUNG_ANDROID_VERSION, YAIL,
BlocksCodeGenerationTarget.YAIL,
SimpleComponentDatabase.getInstance(blocksNode.getProjectId()));
}

@Override
public void initWorkspace() {
super.initWorkspace();
project = Ode.getInstance().getProjectManager().getProject(blocksNode.getProjectId());
project.addProjectChangeListener(this);
onProjectLoaded(project);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,7 @@ public void execute() {

public void addBlocksEditor(BlocksEditor<?, ?> editor) {
String formName = editor.getEntityName();
int pos = Collections.binarySearch(fileIds, editor.getFileId(),
getFileIdComparator());
if (pos < 0) {
pos = -pos - 1;
}
insertFileEditor(editor, pos);
openFileEditor(editor);
if (isLastOpened(formName)) {
screen1BlocksLoaded = true;
if (readyToShowScreen1()) {
Expand Down Expand Up @@ -295,8 +290,9 @@ private Promise<Object> loadProject(Object result) {
}
for (ProjectNode source: projectRootNode.getAllSourceNodes()) {
if (source instanceof YoungAndroidBlocksNode) {
addBlocksEditor(((YoungAndroidBlocksNode) source).getFormName(),
new YaBlocksEditor(this, (YoungAndroidBlocksNode) source));
YaBlocksEditor newBlocksEditor = new YaBlocksEditor(this, (YoungAndroidBlocksNode) source);
addBlocksEditor(((YoungAndroidBlocksNode) source).getFormName(), newBlocksEditor);
newBlocksEditor.initWorkspace();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with this change. Initializing the workspace is an implementation detail of the blocks editor class and it shouldn't be up to the caller to have to manually do it. In other branches where we've introduced block editors for other platforms we will need to remember to make this change when we merge upstream.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ewpatton good point, fixed & deployed.

}
}

Expand Down Expand Up @@ -393,8 +389,9 @@ public void onProjectNodeAdded(Project project, ProjectNode node) {
}
else if (node instanceof YoungAndroidBlocksNode) {
if (getFileEditor(node.getFileId()) == null) {
addBlocksEditor(((YoungAndroidBlocksNode) node).getEntityName(),
new YaBlocksEditor(this, (YoungAndroidBlocksNode) node));
YaBlocksEditor newBlocksEditor = new YaBlocksEditor(this, (YoungAndroidBlocksNode) node);
addBlocksEditor(((YoungAndroidBlocksNode) node).getEntityName(), newBlocksEditor);
newBlocksEditor.initWorkspace();
formName = ((YoungAndroidBlocksNode) node).getFormName();
}
}
Expand Down Expand Up @@ -574,6 +571,7 @@ public void execute() {
pos = -pos - 1;
}
insertFileEditor(newDesigner, pos);
openFileEditor(newDesigner);
if (isLastOpened(entityName)) {
screen1FormLoaded = true;
if (readyToShowScreen1()) {
Expand Down Expand Up @@ -619,6 +617,12 @@ private void addBlocksEditor(String entityName, final BlocksEditor<?, ?> newBloc
editorMap.put(entityName, editors);
}
addFileEditorByType(newBlocksEditor);
int pos = Collections.binarySearch(fileIds, newBlocksEditor.getFileId(),
getFileIdComparator());
if (pos < 0) {
pos = -pos - 1;
}
insertFileEditor(newBlocksEditor, pos);
}

private void removeFormEditor(String formName) {
Expand Down
11 changes: 5 additions & 6 deletions appinventor/appengine/war/index.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,15 @@
<script src="static/js/tutorial.js"></script>
<script type="text/javascript" src="static/closure-compiler/base.js"></script>
<script type="text/javascript" src="<%= odeBase %>ode/aiblockly-@blocklyeditor_BlocklyChecksum@.js"></script>
<script type="text/javascript" src="static/js/scroll-options-6.0.16.min.js"></script>
<script type="text/javascript" src="static/js/workspace-search-9.1.10.min.js"></script>
<script type="text/javascript" src="static/js/block-dynamic-connection-0.7.16.min.js"></script>
<script type="text/javascript" src="static/js/scroll-options-13.2.0.min.js"></script>
<script type="text/javascript" src="static/js/workspace-search-13.2.0.min.js"></script>
<script type="text/javascript" src="static/js/block-dynamic-connection-13.2.0.min.js"></script>
<script type="text/javascript" src="static/js/pickr.min.js"></script>
<script type="text/javascript" src="static/js/workspace-multiselect-pr135-edf5dd2.min.js"></script>
<script type="text/javascript" src="static/js/keyboard-navigation-0.7.0.min.js"></script>
<script type="text/javascript" src="static/js/workspace-multiselect-pr154-e4e120e.min.js"></script>
<script type="text/javascript" src="<%= odeBase %>ode/cdnok.js"></script>
<script type="text/javascript" src="static/js/jszip.min.js"></script>
<script type="text/javascript" src="<%= odeBase %>ode/ode.nocache.js"></script>
<script type="text/javascript" src="static/js/blockly-field-colour-5.0.19.min.js"></script>
<script type="text/javascript" src="static/js/blockly-field-colour-13.2.0.min.js"></script>
<script type="text/javascript">
// Register FieldColour plugin for Blockly v11 compatibility
if (typeof window.registerFieldColour === 'function') {
Expand Down
Loading