-
Notifications
You must be signed in to change notification settings - Fork 9
Pr try steps block #291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Pr try steps block #291
Changes from 21 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
37a9000
For rough prototype
alan412 5e3ae9a
Split step into a label and field
alan412 5a4d63d
Merge branch 'main' of github.com:wpilibsuite/systemcore-blocks-inter…
alan412 28d38ae
Change to allow field flydown for more than params
alan412 bf26084
Add advance to step blocks
alan412 4afcbff
use flydown
alan412 5a3e824
Merge remote-tracking branch 'origin/main' into pr_try_steps_block
alan412 200b773
First bit of having steps
alan412 7ebcb00
Make default starting out with one step
alan412 06ebe8e
Change from Advance to Jump
alan412 428f6df
beginning of generating code
alan412 6eb73b7
Limit Jump to step to be within steps
alan412 1208362
Move steps into opmode and only show when not already in opmode
alan412 c4af155
Call steps as part of loop if it is defined
alan412 b84f6fd
Clean up warnings
alan412 504948e
Ran format on new files
alan412 fa32b2d
Simplify python generation
alan412 1b55055
Simplify python generation
alan412 e54d696
Updates jump correctly
alan412 e18c259
remove warnings
alan412 5de62ad
fix typo
alan412 acd2813
add Shadow true blocks
alan412 34571e7
Bump version to 0.0.4
alan412 f0e4298
Change case of Repeat Until
alan412 c888d11
make sure self.steps is callable
alan412 a8775b9
Change to 2 space indentation
alan412 2c19451
change to 2 space indentation
alan412 e8346ca
Addressed review comments
alan412 2d53e0b
Merge branch 'main' of github.com:alan412/systemcore-blocks-interface…
alan412 b08d01f
In mrc_steps.ts:
lizlooney 0610995
Added constants for INPUT_CONDITION_PREFIX and INPUT_STEP_PREFIX.
lizlooney dfd03a4
Moved code to add shadow True blocks from updateShape_ to compose.
lizlooney 92b3ec7
Merge pull request #17 from lizlooney/pr_try_steps_block
alan412 f8f4f35
In mrc_jump_to_steps:
lizlooney 7ef7583
Added STEPS and REPEAT_UNTIL strings to Hebrew translation file.
lizlooney 291cd02
Added strings for mrc_jump_to_step blocks to i18n.
lizlooney 42564a5
Fixed inaccurate comments.
lizlooney f51f3a9
Renamed createAdvanceToBlock to createJumpToStepBlock.
lizlooney dcf2a24
Merge pull request #18 from lizlooney/pr_try_steps_block
alan412 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2025 Porpoiseful LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| /** | ||
| * @fileoverview This is a block that allows your code to jump to a specific step. | ||
| * @author alan@porpoiseful.com (Alan Smith) | ||
| */ | ||
| import * as Blockly from 'blockly'; | ||
|
|
||
| import { ExtendedPythonGenerator } from '../editor/extended_python_generator'; | ||
| import { createFieldNonEditableText } from '../fields/FieldNonEditableText'; | ||
| import { MRC_STYLE_VARIABLES } from '../themes/styles'; | ||
| import { BLOCK_NAME as MRC_STEPS, StepsBlock } from './mrc_steps' | ||
|
|
||
| export const BLOCK_NAME = 'mrc_jump_to_step'; | ||
|
|
||
| const FIELD_STEP_NAME = 'STEP_NAME'; | ||
|
|
||
| const WARNING_ID_NOT_IN_STEP = 'not in step'; | ||
|
|
||
|
|
||
| type JumpToStepBlock = Blockly.Block & Blockly.BlockSvg & JumpToStepMixin; | ||
|
|
||
| interface JumpToStepMixin extends JumpToStepMixinType { | ||
| mrcHasWarning: boolean, | ||
| } | ||
|
|
||
| type JumpToStepMixinType = typeof JUMP_TO_STEP_BLOCK; | ||
|
|
||
| const JUMP_TO_STEP_BLOCK = { | ||
| /** | ||
| * Block initialization. | ||
| */ | ||
| init: function (this: JumpToStepBlock): void { | ||
| this.appendDummyInput() | ||
| .appendField('Jump to') | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .appendField(createFieldNonEditableText(''), FIELD_STEP_NAME); | ||
| this.setPreviousStatement(true, null); | ||
| this.setInputsInline(true); | ||
| this.setStyle(MRC_STYLE_VARIABLES); | ||
| this.setTooltip('Jump to the specified step.'); | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }, | ||
| /** | ||
| * mrcOnMove is called when an EventBlock is moved. | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| */ | ||
| mrcOnMove: function (this: JumpToStepBlock, _reason: string[]): void { | ||
| this.checkBlockPlacement(); | ||
| }, | ||
| mrcOnAncestorMove: function (this: JumpToStepBlock): void { | ||
| this.checkBlockPlacement(); | ||
| }, | ||
| checkBlockPlacement: function (this: JumpToStepBlock): void { | ||
| const legalStepNames: string[] = []; | ||
|
|
||
| const rootBlock: Blockly.Block | null = this.getRootBlock(); | ||
| if (rootBlock.type === MRC_STEPS) { | ||
| // This block is within a class method definition. | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const stepsBlock = rootBlock as StepsBlock; | ||
| // Add the method's parameter names to legalStepNames. | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| legalStepNames.push(...stepsBlock.mrcGetStepNames()); | ||
| } | ||
|
|
||
| if (legalStepNames.includes(this.getFieldValue(FIELD_STEP_NAME))) { | ||
| // If this blocks's parameter name is in legalParameterNames, it's good. | ||
lizlooney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.setWarningText(null, WARNING_ID_NOT_IN_STEP); | ||
| this.mrcHasWarning = false; | ||
| } else { | ||
| // Otherwise, add a warning to this block. | ||
| if (!this.mrcHasWarning) { | ||
| this.setWarningText(Blockly.Msg.JUMP_CAN_ONLY_GO_IN_THEIR_STEPS_BLOCK, WARNING_ID_NOT_IN_STEP); | ||
| this.getIcon(Blockly.icons.IconType.WARNING)!.setBubbleVisible(true); | ||
| this.mrcHasWarning = true; | ||
| } | ||
| } | ||
| }, | ||
| }; | ||
|
|
||
| export const setup = function () { | ||
| Blockly.Blocks[BLOCK_NAME] = JUMP_TO_STEP_BLOCK; | ||
| }; | ||
|
|
||
| export const pythonFromBlock = function ( | ||
| block: JumpToStepBlock, | ||
| _generator: ExtendedPythonGenerator, | ||
| ) { | ||
| let code = 'self._current_step = "' + | ||
| block.getFieldValue(FIELD_STEP_NAME) + '"\n'; | ||
| code += 'return\n'; | ||
|
|
||
| return code; | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,212 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2025 Porpoiseful LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * https://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| /** | ||
| * @fileoverview Mutator for steps. | ||
| * @author alan@porpoiseful.com (Alan Smith) | ||
| */ | ||
| import * as Blockly from 'blockly'; | ||
| import { MRC_STYLE_CLASS_BLOCKS } from '../themes/styles'; | ||
|
|
||
| export const STEP_CONTAINER_BLOCK_NAME = 'mrc_step_container'; | ||
| const STEP_ITEM_BLOCK_NAME = 'mrc_step_item'; | ||
|
|
||
| export const setup = function () { | ||
| Blockly.Blocks[STEP_CONTAINER_BLOCK_NAME] = STEP_CONTAINER; | ||
| Blockly.Blocks[STEP_ITEM_BLOCK_NAME] = STEP_ITEM; | ||
| }; | ||
|
|
||
| // The step container block. | ||
|
|
||
| const INPUT_STACK = 'STACK'; | ||
|
|
||
| export type StepContainerBlock = StepContainerMixin & Blockly.BlockSvg; | ||
| interface StepContainerMixin extends StepContainerMixinType {} | ||
| type StepContainerMixinType = typeof STEP_CONTAINER; | ||
|
|
||
| const STEP_CONTAINER = { | ||
| init: function (this: StepContainerBlock) { | ||
| this.appendDummyInput().appendField(Blockly.Msg.STEPS); | ||
| this.appendStatementInput(INPUT_STACK); | ||
| this.setStyle(MRC_STYLE_CLASS_BLOCKS); | ||
| this.contextMenu = false; | ||
| }, | ||
| getStepItemBlocks: function (this: StepContainerBlock): StepItemBlock[] { | ||
| const stepItemBlocks: StepItemBlock[] = []; | ||
| let block = this.getInputTargetBlock(INPUT_STACK); | ||
| while (block && !block.isInsertionMarker()) { | ||
| if (block.type !== STEP_ITEM_BLOCK_NAME) { | ||
| throw new Error('getItemNames: block.type should be ' + STEP_ITEM_BLOCK_NAME); | ||
| } | ||
| stepItemBlocks.push(block as StepItemBlock); | ||
| block = block.nextConnection && block.nextConnection.targetBlock(); | ||
| } | ||
| return stepItemBlocks; | ||
| }, | ||
| }; | ||
|
|
||
| // The step item block. | ||
|
|
||
| const FIELD_NAME = 'NAME'; | ||
|
|
||
| export type StepItemBlock = StepItemMixin & Blockly.BlockSvg; | ||
| interface StepItemMixin extends StepItemMixinType { | ||
| originalName: string, | ||
| } | ||
|
|
||
| type StepItemMixinType = typeof STEP_ITEM; | ||
|
|
||
| const STEP_ITEM = { | ||
| init: function (this: StepItemBlock) { | ||
| this.appendDummyInput() | ||
| .appendField(new Blockly.FieldTextInput(''), FIELD_NAME); | ||
| this.setPreviousStatement(true); | ||
| this.setNextStatement(true); | ||
| this.setStyle(MRC_STYLE_CLASS_BLOCKS); | ||
| this.originalName = ''; | ||
| this.contextMenu = false; | ||
| }, | ||
| makeNameLegal: function (this: StepItemBlock): void { | ||
| const rootBlock: Blockly.Block | null = this.getRootBlock(); | ||
| if (rootBlock) { | ||
| const otherNames: string[] = [] | ||
| rootBlock!.getDescendants(true)?.forEach(itemBlock => { | ||
| if (itemBlock != this) { | ||
| otherNames.push(itemBlock.getFieldValue(FIELD_NAME)); | ||
| } | ||
| }); | ||
| let currentName = this.getFieldValue(FIELD_NAME); | ||
| while (otherNames.includes(currentName)) { | ||
| // Check if currentName ends with a number | ||
| const match = currentName.match(/^(.*?)(\d+)$/); | ||
| if (match) { | ||
| // If it ends with a number, increment it | ||
| const baseName = match[1]; | ||
| const number = parseInt(match[2], 10); | ||
| currentName = baseName + (number + 1); | ||
| } else { | ||
| // If it doesn't end with a number, append 2 | ||
| currentName = currentName + '2'; | ||
| } | ||
| } | ||
| this.setFieldValue(currentName, FIELD_NAME); | ||
| updateMutatorFlyout(this.workspace); | ||
| } | ||
| }, | ||
| getName: function (this: StepItemBlock): string { | ||
| return this.getFieldValue(FIELD_NAME); | ||
| }, | ||
| getOriginalName: function (this: StepItemBlock): string { | ||
| return this.originalName; | ||
| }, | ||
| setOriginalName: function (this: StepItemBlock, originalName: string): void { | ||
| this.originalName = originalName; | ||
| }, | ||
| } | ||
|
|
||
| /** | ||
| * Updates the mutator's flyout so that it contains a single step item block | ||
| * whose name is not a duplicate of an existing step item. | ||
| * | ||
| * @param workspace The mutator's workspace. This workspace's flyout is what is being updated. | ||
| */ | ||
| function updateMutatorFlyout(workspace: Blockly.WorkspaceSvg) { | ||
| const usedNames: string[] = []; | ||
| workspace.getBlocksByType(STEP_ITEM_BLOCK_NAME, false).forEach(block => { | ||
| usedNames.push(block.getFieldValue(FIELD_NAME)); | ||
| }); | ||
|
|
||
| // Find the first unused number starting from 0 | ||
| let counter = 0; | ||
| let uniqueName = counter.toString(); | ||
| while (usedNames.includes(uniqueName)) { | ||
| counter++; | ||
| uniqueName = counter.toString(); | ||
| } | ||
|
|
||
| const jsonBlock = { | ||
| kind: 'block', | ||
| type: STEP_ITEM_BLOCK_NAME, | ||
| fields: { | ||
| NAME: uniqueName, | ||
| }, | ||
| }; | ||
|
|
||
| workspace.updateToolbox({ contents: [jsonBlock] }); | ||
| } | ||
|
|
||
| /** | ||
| * The blockly event listener function for the mutator's workspace. | ||
| */ | ||
| function onChange(mutatorWorkspace: Blockly.Workspace, event: Blockly.Events.Abstract) { | ||
| if (event.type === Blockly.Events.BLOCK_MOVE) { | ||
| const blockMoveEvent = event as Blockly.Events.BlockMove; | ||
| const reason: string[] = blockMoveEvent.reason ?? []; | ||
| if (reason.includes('connect') && blockMoveEvent.blockId) { | ||
| const block = mutatorWorkspace.getBlockById(blockMoveEvent.blockId); | ||
| if (block && block.type === STEP_ITEM_BLOCK_NAME) { | ||
| (block as StepItemBlock).makeNameLegal(); | ||
| } | ||
| } | ||
| } else if (event.type === Blockly.Events.BLOCK_CHANGE) { | ||
| const blockChangeEvent = event as Blockly.Events.BlockChange; | ||
| if (blockChangeEvent.blockId) { | ||
| const block = mutatorWorkspace.getBlockById(blockChangeEvent.blockId); | ||
| if (block && block.type === STEP_ITEM_BLOCK_NAME) { | ||
| (block as StepItemBlock).makeNameLegal(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Called for mrc_event and mrc_class_method_def blocks when their mutator opesn. | ||
| * Triggers a flyout update and adds an event listener to the mutator workspace. | ||
| * | ||
| * @param block The block whose mutator is open. | ||
| */ | ||
| export function onMutatorOpen(block: Blockly.BlockSvg) { | ||
| const mutatorIcon = block.getIcon(Blockly.icons.MutatorIcon.TYPE) as Blockly.icons.MutatorIcon; | ||
| const mutatorWorkspace = mutatorIcon.getWorkspace()!; | ||
| updateMutatorFlyout(mutatorWorkspace); | ||
| mutatorWorkspace.addChangeListener(event => onChange(mutatorWorkspace, event)); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the MutatorIcon for the given block. | ||
| */ | ||
| export function getMutatorIcon(block: Blockly.BlockSvg): Blockly.icons.MutatorIcon { | ||
| return new Blockly.icons.MutatorIcon([STEP_ITEM_BLOCK_NAME], block); | ||
| } | ||
|
|
||
| export function createMutatorBlocks(workspace: Blockly.Workspace, stepNames: string[]): Blockly.BlockSvg { | ||
| // First create the container block. | ||
| const containerBlock = workspace.newBlock(STEP_CONTAINER_BLOCK_NAME) as Blockly.BlockSvg; | ||
| containerBlock.initSvg(); | ||
|
|
||
| // Then add one step item block for each step. | ||
| let connection = containerBlock!.getInput(INPUT_STACK)!.connection; | ||
| for (const stepName of stepNames) { | ||
| const itemBlock = workspace.newBlock(STEP_ITEM_BLOCK_NAME) as StepItemBlock; | ||
| itemBlock.initSvg(); | ||
| itemBlock.setFieldValue(stepName, FIELD_NAME); | ||
| itemBlock.originalName = stepName; | ||
| connection!.connect(itemBlock.previousConnection!); | ||
| connection = itemBlock.nextConnection; | ||
| } | ||
| return containerBlock; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.