Skip to content

Commit

Permalink
chore: Revert removal of XML-based category functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
gonfunko committed Jan 8, 2025
1 parent 326878b commit aff572d
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 17 deletions.
121 changes: 119 additions & 2 deletions core/procedures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,126 @@ export function rename(this: Field, name: string): string {
* Construct the blocks required by the flyout for the procedure category.
*
* @param workspace The workspace containing procedures.
* @returns Array of JSON block elements.
* @returns Array of XML block elements.
*/
export function flyoutCategory(workspace: WorkspaceSvg): FlyoutItemInfo[] {
function xmlFlyoutCategory(workspace: WorkspaceSvg): Element[] {
const xmlList = [];
if (Blocks['procedures_defnoreturn']) {
// <block type="procedures_defnoreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_defnoreturn');
block.setAttribute('gap', '16');
const nameField = utilsXml.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(
utilsXml.createTextNode(Msg['PROCEDURES_DEFNORETURN_PROCEDURE']),
);
block.appendChild(nameField);
xmlList.push(block);
}
if (Blocks['procedures_defreturn']) {
// <block type="procedures_defreturn" gap="16">
// <field name="NAME">do something</field>
// </block>
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_defreturn');
block.setAttribute('gap', '16');
const nameField = utilsXml.createElement('field');
nameField.setAttribute('name', 'NAME');
nameField.appendChild(
utilsXml.createTextNode(Msg['PROCEDURES_DEFRETURN_PROCEDURE']),
);
block.appendChild(nameField);
xmlList.push(block);
}
if (Blocks['procedures_ifreturn']) {
// <block type="procedures_ifreturn" gap="16"></block>
const block = utilsXml.createElement('block');
block.setAttribute('type', 'procedures_ifreturn');
block.setAttribute('gap', '16');
xmlList.push(block);
}
if (xmlList.length) {
// Add slightly larger gap between system blocks and user calls.
xmlList[xmlList.length - 1].setAttribute('gap', '24');
}

/**
* Add items to xmlList for each listed procedure.
*
* @param procedureList A list of procedures, each of which is defined by a
* three-element list of name, parameter list, and return value boolean.
* @param templateName The type of the block to generate.
*/
function populateProcedures(
procedureList: ProcedureTuple[],
templateName: string,
) {
for (let i = 0; i < procedureList.length; i++) {
const name = procedureList[i][0];
const args = procedureList[i][1];
// <block type="procedures_callnoreturn" gap="16">
// <mutation name="do something">
// <arg name="x"></arg>
// </mutation>
// </block>
const block = utilsXml.createElement('block');
block.setAttribute('type', templateName);
block.setAttribute('gap', '16');
const mutation = utilsXml.createElement('mutation');
mutation.setAttribute('name', name);
block.appendChild(mutation);
for (let j = 0; j < args.length; j++) {
const arg = utilsXml.createElement('arg');
arg.setAttribute('name', args[j]);
mutation.appendChild(arg);
}
xmlList.push(block);
}
}

const tuple = allProcedures(workspace);
populateProcedures(tuple[0], 'procedures_callnoreturn');
populateProcedures(tuple[1], 'procedures_callreturn');
return xmlList;
}

/**
* Internal wrapper that returns the contents of the procedure category.
*
* @internal
* @param workspace The workspace to populate procedure blocks for.
*/
export function internalFlyoutCategory(
workspace: WorkspaceSvg,
): FlyoutItemInfo[] {
return flyoutCategory(workspace, false);
}

export function flyoutCategory(
workspace: WorkspaceSvg,
useXml: true,
): Element[];
export function flyoutCategory(
workspace: WorkspaceSvg,
useXml: false,
): FlyoutItemInfo[];
/**
* Construct the blocks required by the flyout for the procedure category.
*
* @param workspace The workspace containing procedures.
* @param useXml True to return the contents as XML, false to use JSON.
* @returns List of flyout contents as either XML or JSON.
*/
export function flyoutCategory(
workspace: WorkspaceSvg,
useXml = true,
): Element[] | FlyoutItemInfo[] {
if (useXml) {
return xmlFlyoutCategory(workspace);
}
const blocks = [];
if (Blocks['procedures_defnoreturn']) {
blocks.push({
Expand Down
137 changes: 133 additions & 4 deletions core/variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {isVariableBackedParameterModel} from './interfaces/i_variable_backed_par
import {IVariableModel, IVariableState} from './interfaces/i_variable_model.js';
import {Msg} from './msg.js';
import type {BlockInfo, FlyoutItemInfo} from './utils/toolbox.js';
import * as utilsXml from './utils/xml.js';
import type {Workspace} from './workspace.js';
import type {WorkspaceSvg} from './workspace_svg.js';

Expand Down Expand Up @@ -84,20 +85,48 @@ export function allDeveloperVariables(workspace: Workspace): string[] {
return Array.from(variables.values());
}

/**
* Internal wrapper that returns the contents of the variables category.
*
* @internal
* @param workspace The workspace to populate variable blocks for.
*/
export function internalFlyoutCategory(
workspace: WorkspaceSvg,
): FlyoutItemInfo[] {
return flyoutCategory(workspace, false);
}

export function flyoutCategory(
workspace: WorkspaceSvg,
useXml: true,
): Element[];
export function flyoutCategory(
workspace: WorkspaceSvg,
useXml: false,
): FlyoutItemInfo[];
/**
* Construct the elements (blocks and button) required by the flyout for the
* variable category.
*
* @param workspace The workspace containing variables.
* @returns JSON list of flyout contents.
* @param useXml True to return the contents as XML, false to use JSON.
* @returns List of flyout contents as either XML or JSON.
*/
export function flyoutCategory(workspace: WorkspaceSvg): FlyoutItemInfo[] {
export function flyoutCategory(
workspace: WorkspaceSvg,
useXml = true,
): Element[] | FlyoutItemInfo[] {
if (!Blocks['variables_set'] && !Blocks['variables_get']) {
console.warn(
'There are no variable blocks, but there is a variable category.',
);
}

if (useXml) {
return xmlFlyoutCategory(workspace);
}

workspace.registerButtonCallback('CREATE_VARIABLE', function (button) {
createVariableButtonHandler(button.getTargetWorkspace());
});
Expand All @@ -108,7 +137,11 @@ export function flyoutCategory(workspace: WorkspaceSvg): FlyoutItemInfo[] {
'text': '%{BKY_NEW_VARIABLE}',
'callbackkey': 'CREATE_VARIABLE',
},
...flyoutCategoryBlocks(workspace, workspace.getVariablesOfType(''), true),
...jsonFlyoutCategoryBlocks(
workspace,
workspace.getVariablesOfType(''),
true,
),
];
}

Expand All @@ -130,14 +163,15 @@ function generateVariableFieldJson(variable: IVariableModel<IVariableState>) {
/**
* Construct the blocks required by the flyout for the variable category.
*
* @internal
* @param workspace The workspace containing variables.
* @param variables List of variables to create blocks for.
* @param includeChangeBlocks True to include `change x by _` blocks.
* @param getterType The type of the variable getter block to generate.
* @param setterType The type of the variable setter block to generate.
* @returns JSON list of blocks.
*/
export function flyoutCategoryBlocks(
export function jsonFlyoutCategoryBlocks(
workspace: Workspace,
variables: IVariableModel<IVariableState>[],
includeChangeBlocks: boolean,
Expand Down Expand Up @@ -196,6 +230,80 @@ export function flyoutCategoryBlocks(
return blocks;
}

/**
* Construct the elements (blocks and button) required by the flyout for the
* variable category.
*
* @param workspace The workspace containing variables.
* @returns Array of XML elements.
*/
function xmlFlyoutCategory(workspace: WorkspaceSvg): Element[] {
let xmlList = new Array<Element>();
const button = document.createElement('button');
button.setAttribute('text', '%{BKY_NEW_VARIABLE}');
button.setAttribute('callbackKey', 'CREATE_VARIABLE');

workspace.registerButtonCallback('CREATE_VARIABLE', function (button) {
createVariableButtonHandler(button.getTargetWorkspace());
});

xmlList.push(button);

const blockList = flyoutCategoryBlocks(workspace);
xmlList = xmlList.concat(blockList);
return xmlList;
}

/**
* Construct the blocks required by the flyout for the variable category.
*
* @param workspace The workspace containing variables.
* @returns Array of XML block elements.
*/
export function flyoutCategoryBlocks(workspace: Workspace): Element[] {
const variableModelList = workspace.getVariablesOfType('');

const xmlList = [];
if (variableModelList.length > 0) {
// New variables are added to the end of the variableModelList.
const mostRecentVariable = variableModelList[variableModelList.length - 1];
if (Blocks['variables_set']) {
const block = utilsXml.createElement('block');
block.setAttribute('type', 'variables_set');
block.setAttribute('gap', Blocks['math_change'] ? '8' : '24');
block.appendChild(generateVariableFieldDom(mostRecentVariable));
xmlList.push(block);
}
if (Blocks['math_change']) {
const block = utilsXml.createElement('block');
block.setAttribute('type', 'math_change');
block.setAttribute('gap', Blocks['variables_get'] ? '20' : '8');
block.appendChild(generateVariableFieldDom(mostRecentVariable));
const value = utilsXml.textToDom(
'<value name="DELTA">' +
'<shadow type="math_number">' +
'<field name="NUM">1</field>' +
'</shadow>' +
'</value>',
);
block.appendChild(value);
xmlList.push(block);
}

if (Blocks['variables_get']) {
variableModelList.sort(compareByName);
for (let i = 0, variable; (variable = variableModelList[i]); i++) {
const block = utilsXml.createElement('block');
block.setAttribute('type', 'variables_get');
block.setAttribute('gap', '8');
block.appendChild(generateVariableFieldDom(variable));
xmlList.push(block);
}
}
}
return xmlList;
}

export const VAR_LETTER_OPTIONS = 'ijkmnopqrstuvwxyzabcdefgh';

/**
Expand Down Expand Up @@ -535,6 +643,27 @@ function checkForConflictingParamWithLegacyProcedures(
return null;
}

/**
* Generate DOM objects representing a variable field.
*
* @param variableModel The variable model to represent.
* @returns The generated DOM.
*/
export function generateVariableFieldDom(
variableModel: IVariableModel<IVariableState>,
): Element {
/* Generates the following XML:
* <field name="VAR" id="goKTKmYJ8DhVHpruv" variabletype="int">foo</field>
*/
const field = utilsXml.createElement('field');
field.setAttribute('name', 'VAR');
field.setAttribute('id', variableModel.getId());
field.setAttribute('variabletype', variableModel.getType());
const name = utilsXml.createTextNode(variableModel.getName());
field.appendChild(name);
return field;
}

/**
* Helper function to look up or create a variable on the given workspace.
* If no variable exists, creates and returns it.
Expand Down
Loading

0 comments on commit aff572d

Please sign in to comment.