From 48b1986afe32b492786e427fb1390ac8ab6cb14e Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 09:43:08 -0500 Subject: [PATCH 01/12] For having add data to display --- src/blocks/mrc_display_add_data.ts | 63 ++++++++++++++++++++++++++ src/blocks/setup_custom_blocks.ts | 4 +- src/blocks/tokens.ts | 6 +++ src/i18n/locales/en/translation.json | 6 +++ src/i18n/locales/es/translation.json | 6 +++ src/i18n/locales/he/translation.json | 8 +++- src/themes/styles.ts | 4 ++ src/toolbox/driver_station_category.ts | 32 +++++++++++++ src/toolbox/toolbox.ts | 2 + 9 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 src/blocks/mrc_display_add_data.ts create mode 100644 src/toolbox/driver_station_category.ts diff --git a/src/blocks/mrc_display_add_data.ts b/src/blocks/mrc_display_add_data.ts new file mode 100644 index 00000000..4d17b3a0 --- /dev/null +++ b/src/blocks/mrc_display_add_data.ts @@ -0,0 +1,63 @@ +/** + * @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 A block that lets you add data to the driver station display. + * @author alan@porpoiseful.com (Alan Smith) + */ + +/* NOTE: This will likely go away when we can parse the driver station display class + * but it doesn't exist yet, so this is a placeholder. + */ + +import * as Blockly from 'blockly/core'; +import { Order, PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; + + +export const BLOCK_NAME = 'mrc_display_add_data'; + +export const setup = function() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.appendDummyInput() + .appendField(Blockly.Msg.CALL) + .appendField("display.add_data"); + this.appendValueInput("CAPTION") + .setCheck("String") + .setAlign(Blockly.inputs.Align.RIGHT) + .appendField(Blockly.Msg['CAPTION']); + this.appendValueInput("VALUE") + .setAlign(Blockly.inputs.Align.RIGHT) + .appendField(Blockly.Msg['VALUE']); + this.setPreviousStatement(true); + this.setNextStatement(true); + this.setStyle(MRC_STYLE_DRIVER_STATION); + this.setTooltip(Blockly.Msg['DISPLAY_ADD_DATA_TOOLTIP']); + }, + }; +}; + +export const pythonFromBlock = function( + block: Blockly.Block, + generator: PythonGenerator, +) { + const caption = generator.valueToCode(block, "CAPTION", Order.NONE); + const value = generator.valueToCode(block, "VALUE", Order.NONE); + // TODO: Update this when the actual driver station display class is implemented + return `display.add_data(${caption}, ${value})\n`; +}; diff --git a/src/blocks/setup_custom_blocks.ts b/src/blocks/setup_custom_blocks.ts index fecdb7bc..79902b99 100644 --- a/src/blocks/setup_custom_blocks.ts +++ b/src/blocks/setup_custom_blocks.ts @@ -21,6 +21,7 @@ import * as SetPythonVariable from './mrc_set_python_variable'; import * as Steps from './mrc_steps'; import * as StepContainer from './mrc_step_container'; import * as JumpToStep from './mrc_jump_to_step'; +import * as DisplayAddData from './mrc_display_add_data'; const customBlocks = [ CallPythonFunction, @@ -44,7 +45,8 @@ const customBlocks = [ SetPythonVariable, Steps, StepContainer, - JumpToStep + JumpToStep, + DisplayAddData ]; export const setup = function(forBlock: any) { diff --git a/src/blocks/tokens.ts b/src/blocks/tokens.ts index 711ec4d8..ba1d86c4 100644 --- a/src/blocks/tokens.ts +++ b/src/blocks/tokens.ts @@ -87,6 +87,9 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg { GET_ENUM_VALUE_TOOLTIP: t('BLOCKLY.TOOLTIP.GET_ENUM_VALUE'), SET: t('BLOCKLY.SET'), TO: t('BLOCKLY.TO'), + CAPTION: t('BLOCKLY.CAPTION'), + VALUE: t('BLOCKLY.VALUE'), + DISPLAY_ADD_DATA_TOOLTIP: t('BLOCKLY.TOOLTIP.DISPLAY_ADD_DATA'), SET_MODULE_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_MODULE_VARIABLE'), SET_CLASS_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_CLASS_VARIABLE'), SET_INSTANCE_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_INSTANCE_VARIABLE'), @@ -135,6 +138,9 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg { MRC_CATEGORY_ADD_MECHANISM: t('BLOCKLY.CATEGORY.ADD_MECHANISM'), MRC_CATEGORY_ADD_COMPONENT: t('BLOCKLY.CATEGORY.ADD_COMPONENT'), MRC_CATEGORY_TEST: t('BLOCKLY.CATEGORY.TEST'), + MRC_CATEGORY_DRIVER_STATION: t('BLOCKLY.CATEGORY.DRIVER_STATION'), + MRC_CATEGORY_DRIVER_STATION_DISPLAY: t('BLOCKLY.CATEGORY.DRIVER_STATION_DISPLAY'), + MRC_CATEGORY_DRIVER_STATION_GAMEPADS: t('BLOCKLY.CATEGORY.DRIVER_STATION_GAMEPADS'), MRC_PRINT: t('BLOCKLY.PRINT'), CUSTOM_EVENTS_LABEL: t('BLOCKLY.CUSTOM_EVENTS_LABEL'), CUSTOM_METHODS_LABEL: t('BLOCKLY.CUSTOM_METHODS_LABEL'), diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index b1eff889..74c942fe 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -167,6 +167,8 @@ "GET": "get", "SET": "set", "TO": "to", + "CAPTION": "caption", + "VALUE": "value", "STEPS": "steps", "REPEAT_UNTIL": "repeat until", "JUMP_TO": "jump to", @@ -179,6 +181,7 @@ "TOOLTIP":{ "EVALUATE_BUT_IGNORE_RESULT": "Executes the connected block and ignores the result. Allows you to call a function and ignore the return value.", "NONE": "Returns None.", + "DISPLAY_ADD_DATA": "Add data with a caption to the driver station display.", "OPMODE_TYPE": "What sort of OpMode this is", "OPMODE_ENABLED": "Whether the OpMode is shown on Driver Station", "OPMODE_NAME": "The name shown on the Driver Station. If blank will use the class name.", @@ -221,6 +224,9 @@ "EVENTS": "Events", "ADD_MECHANISM": "+ Mechanism", "ADD_COMPONENT": "+ Component", + "DRIVER_STATION": "Driver Station", + "DRIVER_STATION_DISPLAY": "Display", + "DRIVER_STATION_GAMEPADS": "Gamepads", "TEST": "Test", "PAGINATION_TOTAL": "{{start}}-{{end}} of {{total}} items" }, diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index f3e7e07a..19411c2a 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -168,6 +168,8 @@ "GET": "obtener", "SET": "establecer", "TO": "a", + "CAPTION": "título", + "VALUE": "valor", "STEPS": "pasos", "REPEAT_UNTIL": "repetir hasta", "JUMP_TO": "saltar a", @@ -180,6 +182,7 @@ "TOOLTIP": { "EVALUATE_BUT_IGNORE_RESULT": "Ejecuta el bloque conectado e ignora el resultado. Te permite llamar una función e ignorar el valor de retorno.", "NONE": "No devuelve ninguno.", + "DISPLAY_ADD_DATA": "Agregar datos con un título a la pantalla de la estación del conductor.", "OPMODE_TYPE": "Qué tipo de OpMode es este", "OPMODE_ENABLED": "Si el OpMode se muestra en la Estación del Conductor", "OPMODE_NAME": "El nombre mostrado en la Estación del Conductor. Si está en blanco usará el nombre de la clase.", @@ -222,6 +225,9 @@ "EVENTS": "Eventos", "ADD_MECHANISM": "+ Mecanismo", "ADD_COMPONENT": "+ Componente", + "DRIVER_STATION": "Estación del Conductor", + "DRIVER_STATION_DISPLAY": "Pantalla", + "DRIVER_STATION_GAMEPADS": "Mandos de Juego", "TEST": "Prueba", "PAGINATION_TOTAL": "{{start}}-{{end}} de {{total}} elementos" }, diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index 292efc68..ddd39012 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -166,8 +166,8 @@ "FIRE": "הפעל", "GET": "קבל", "SET": "הגדר", - "TO": "ל", - "STEPS": "צעדים", + "TO": "ל", "CAPTION": "כיתוב", + "VALUE": "ערך", "STEPS": "צעדים", "REPEAT_UNTIL": "לחזור על כך עד", "JUMP_TO": "לקפוץ אל", "CUSTOM_EVENTS_LABEL": "אירועים מותאמים אישית", @@ -179,6 +179,7 @@ "TOOLTIP": { "EVALUATE_BUT_IGNORE_RESULT": "מבצע את הבלוק המחובר ומתעלם מהתוצאה. מאפשר לקרוא לפונקציה מבלי להשתמש בערך שהיא מחזירה.", "NONE": "מחזיר כלום.", + "DISPLAY_ADD_DATA": "הוסף נתונים עם כיתוב לתצוגת תחנת הנהג.", "OPMODE_TYPE": "איזה סוג אופמוד זה", "OPMODE_ENABLED": "האם האופמוד מוצג באפליקציית ה־Driver Station", "OPMODE_NAME": "השם שמוצג באפליקציית ה־Driver Station. אם ריק, ישתמש בשם הכיתה.", @@ -221,6 +222,9 @@ "EVENTS": "אירועים", "ADD_MECHANISM": "+ מנגנון", "ADD_COMPONENT": "+ רכיב", + "DRIVER_STATION": "תחנת נהג", + "DRIVER_STATION_DISPLAY": "תצוגה", + "DRIVER_STATION_GAMEPADS": "בקרי משחק", "TEST": "בדיקה", "PAGINATION_TOTAL": "{{start}}-{{end}} מתוך {{total}} פריטים" }, diff --git a/src/themes/styles.ts b/src/themes/styles.ts index 28377a40..3b7e5562 100644 --- a/src/themes/styles.ts +++ b/src/themes/styles.ts @@ -35,6 +35,7 @@ export const MRC_STYLE_COMPONENTS = 'mrc_style_components'; export const MRC_STYLE_EVENTS = 'mrc_style_events'; export const MRC_STYLE_PORTS = 'mrc_style_ports'; export const MRC_STYLE_EVENT_HANDLER = 'mrc_style_event_handler'; +export const MRC_STYLE_DRIVER_STATION = 'mrc_style_driver_station'; export const MRC_CATEGORY_STYLE_COMPONENTS = 'mrc_category_style_components'; export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme { @@ -45,6 +46,9 @@ export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme { theme.setBlockStyle(MRC_STYLE_FUNCTIONS, { ...procedureStyle }); + theme.setBlockStyle(MRC_STYLE_DRIVER_STATION, { + ...procedureStyle + }); theme.setBlockStyle(MRC_STYLE_EVENT_HANDLER, { ...procedureStyle, hat: "cap" diff --git a/src/toolbox/driver_station_category.ts b/src/toolbox/driver_station_category.ts new file mode 100644 index 00000000..fd52bf35 --- /dev/null +++ b/src/toolbox/driver_station_category.ts @@ -0,0 +1,32 @@ +import * as Blockly from 'blockly/core'; + +import * as toolboxItems from './items'; +import { Editor } from '../editor/editor'; +import { BLOCK_NAME as MRC_DISPLAY_ADD_DATA } from '../blocks/mrc_display_add_data'; + +export function getDriverStationCategory(editor: Editor): toolboxItems.Category { + return new toolboxItems.Category( + Blockly.Msg['MRC_CATEGORY_DRIVER_STATION'], + [ + getDriverStationDisplayCategory(editor), + getDriverStationGamepadsCategory(editor), + ], + toolboxItems.ExpandedState.EXPANDED); +} + +function getDriverStationDisplayCategory(editor: Editor): toolboxItems.Category { + return new toolboxItems.Category( + Blockly.Msg['MRC_CATEGORY_DRIVER_STATION_DISPLAY'], + [ + new toolboxItems.Block(MRC_DISPLAY_ADD_DATA, null, null, null), + ], + toolboxItems.ExpandedState.EXPANDED); +} +function getDriverStationGamepadsCategory(editor: Editor): toolboxItems.Category { + return new toolboxItems.Category( + Blockly.Msg['MRC_CATEGORY_DRIVER_STATION_GAMEPADS'], + [ + + ], + toolboxItems.ExpandedState.EXPANDED); +} \ No newline at end of file diff --git a/src/toolbox/toolbox.ts b/src/toolbox/toolbox.ts index a3a539af..35df8271 100644 --- a/src/toolbox/toolbox.ts +++ b/src/toolbox/toolbox.ts @@ -5,6 +5,7 @@ import * as common from './toolbox_common' import { Editor } from '../editor/editor'; import { getHardwareCategory } from './hardware_category'; import { getCategory as getEventCategory } from './event_category'; +import { getDriverStationCategory } from './driver_station_category'; export function getToolboxJSON( shownPythonToolboxCategories: Set | null, @@ -24,6 +25,7 @@ export function getToolboxJSON( toolbox.contents.push(getEventCategory(editor)); break; case storageModule.ModuleType.OPMODE: + toolbox.contents.push(getDriverStationCategory(editor)); toolbox.contents.push(getHardwareCategory(editor)); toolbox.contents.push(new toolboxItems.Sep()); toolbox.contents.push(...common.getToolboxItems(shownPythonToolboxCategories, editor)); From b08b42b9d0c2318e205db20ac84c0474885560dc Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 10:16:36 -0500 Subject: [PATCH 02/12] Add beginning of gamepad --- src/blocks/mrc_gamepad_analog.ts | 62 ++++++++++++++++++++++ src/blocks/mrc_gamepad_boolean.ts | 71 ++++++++++++++++++++++++++ src/blocks/setup_custom_blocks.ts | 6 ++- src/toolbox/driver_station_category.ts | 5 +- 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/blocks/mrc_gamepad_analog.ts create mode 100644 src/blocks/mrc_gamepad_boolean.ts diff --git a/src/blocks/mrc_gamepad_analog.ts b/src/blocks/mrc_gamepad_analog.ts new file mode 100644 index 00000000..f7b7c807 --- /dev/null +++ b/src/blocks/mrc_gamepad_analog.ts @@ -0,0 +1,62 @@ +/** + * @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 A block for interfacing with the gamepad analog (sticks) + * @author alan@porpoiseful.com (Alan Smith) + */ + +/* NOTE: This will likely go away when we can parse the gamepad class + * but it doesn't exist yet, so this is a placeholder. + */ + +import * as Blockly from 'blockly/core'; +import { Order, PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; +import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; + + +export const BLOCK_NAME = 'mrc_gamepad_analog'; + +export const setup = function() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.appendDummyInput() + .appendField("Gamepad") + .appendField(createFieldNumberDropdown(0,7), "GAMEPAD") + .appendField(new + Blockly.FieldDropdown([ + ['Left stick X', 'LEFT_STICK_X'], + ['Left stick Y', 'LEFT_STICK_Y'], + ['Right stick X', 'RIGHT_STICK_X'], + ['Right stick Y', 'RIGHT_STICK_Y'], + ['Left trigger', 'LEFT_TRIGGER'], + ['Right trigger', 'RIGHT_TRIGGER'], + ]), "AXIS"); + this.setOutput(true, 'Number'); + this.setStyle(MRC_STYLE_DRIVER_STATION); + }, + }; +}; + +export const pythonFromBlock = function( + block: Blockly.Block, + generator: PythonGenerator, +) { + // TODO: Update this when the actual driver station display class is implemented + return ''; +}; diff --git a/src/blocks/mrc_gamepad_boolean.ts b/src/blocks/mrc_gamepad_boolean.ts new file mode 100644 index 00000000..aa25a1c2 --- /dev/null +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -0,0 +1,71 @@ +/** + * @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 A block for interfacing with the gamepad boolean (button) + * @author alan@porpoiseful.com (Alan Smith) + */ + +/* NOTE: This will likely go away when we can parse the gamepad class + * but it doesn't exist yet, so this is a placeholder. + */ + +import * as Blockly from 'blockly/core'; +import { Order, PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; +import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; + + +export const BLOCK_NAME = 'mrc_gamepad_boolean'; + +export const setup = function() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.appendDummyInput() + .appendField("Gamepad") + .appendField(createFieldNumberDropdown(0,7), "GAMEPAD") + .appendField(new + Blockly.FieldDropdown([ + ['A', 'A'], + ['B', 'B'], + ['X', 'X'], + ['Y', 'Y'], + ['Left Bumper', 'LEFT_BUMPER'], + ['Right Bumper', 'RIGHT_BUMPER'], + ['Back', 'BACK'], + ['Start', 'START'], + ['Left Stick Button', 'LEFT_STICK_BUTTON'], + ['Right Stick Button', 'RIGHT_STICK_BUTTON'] + ]), "BUTTON"); + this.appendDummyInput() + .appendField(new Blockly.FieldDropdown([ + ['is down', 'IS_DOWN'], + ['was pressed', 'WAS_PRESSED'], + ['was released', 'WAS_RELEASED']]), 'ACTION'); + this.setOutput(true, 'Boolean'); + this.setStyle(MRC_STYLE_DRIVER_STATION); + }, + }; +}; + +export const pythonFromBlock = function( + block: Blockly.Block, + generator: PythonGenerator, +) { + // TODO: Update this when the actual driver station display class is implemented + return ''; +}; diff --git a/src/blocks/setup_custom_blocks.ts b/src/blocks/setup_custom_blocks.ts index 79902b99..ee7d88ad 100644 --- a/src/blocks/setup_custom_blocks.ts +++ b/src/blocks/setup_custom_blocks.ts @@ -22,6 +22,8 @@ import * as Steps from './mrc_steps'; import * as StepContainer from './mrc_step_container'; import * as JumpToStep from './mrc_jump_to_step'; import * as DisplayAddData from './mrc_display_add_data'; +import * as GamepadBoolean from './mrc_gamepad_boolean'; +import * as GamepadAnalog from './mrc_gamepad_analog'; const customBlocks = [ CallPythonFunction, @@ -46,7 +48,9 @@ const customBlocks = [ Steps, StepContainer, JumpToStep, - DisplayAddData + DisplayAddData, + GamepadBoolean, + GamepadAnalog ]; export const setup = function(forBlock: any) { diff --git a/src/toolbox/driver_station_category.ts b/src/toolbox/driver_station_category.ts index fd52bf35..b11d09fa 100644 --- a/src/toolbox/driver_station_category.ts +++ b/src/toolbox/driver_station_category.ts @@ -3,6 +3,8 @@ import * as Blockly from 'blockly/core'; import * as toolboxItems from './items'; import { Editor } from '../editor/editor'; import { BLOCK_NAME as MRC_DISPLAY_ADD_DATA } from '../blocks/mrc_display_add_data'; +import { BLOCK_NAME as MRC_GAMEPAD_BOOLEAN } from '../blocks/mrc_gamepad_boolean'; +import { BLOCK_NAME as MRC_GAMEPAD_ANALOG } from '../blocks/mrc_gamepad_analog'; export function getDriverStationCategory(editor: Editor): toolboxItems.Category { return new toolboxItems.Category( @@ -26,7 +28,8 @@ function getDriverStationGamepadsCategory(editor: Editor): toolboxItems.Category return new toolboxItems.Category( Blockly.Msg['MRC_CATEGORY_DRIVER_STATION_GAMEPADS'], [ - + new toolboxItems.Block(MRC_GAMEPAD_BOOLEAN, null, null, null), + new toolboxItems.Block(MRC_GAMEPAD_ANALOG, null, null, null), ], toolboxItems.ExpandedState.EXPANDED); } \ No newline at end of file From 61d295a03f20777a5370b2530b0db9d94c36b78b Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 13:11:14 -0500 Subject: [PATCH 03/12] Add boolean gamepad events --- src/blocks/mrc_gamepad_boolean_event.ts | 77 +++++++++++++++++++++++++ src/blocks/setup_custom_blocks.ts | 4 +- src/toolbox/driver_station_category.ts | 3 + 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/blocks/mrc_gamepad_boolean_event.ts diff --git a/src/blocks/mrc_gamepad_boolean_event.ts b/src/blocks/mrc_gamepad_boolean_event.ts new file mode 100644 index 00000000..513a59a3 --- /dev/null +++ b/src/blocks/mrc_gamepad_boolean_event.ts @@ -0,0 +1,77 @@ +/** + * @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 A block for interfacing with the gamepad events + * @author alan@porpoiseful.com (Alan Smith) + */ + +/* NOTE: This will likely go away when we can parse the gamepad class + * but it doesn't exist yet, so this is a placeholder. + */ + +import * as Blockly from 'blockly/core'; +import { Order, PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION, MRC_STYLE_EVENT_HANDLER, MRC_STYLE_EVENTS } from '../themes/styles'; +import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; + + +export const BLOCK_NAME = 'mrc_gamepad_boolean_event'; + +export const setup = function () { + Blockly.Blocks[BLOCK_NAME] = { + init: function () { + this.appendDummyInput() + .appendField(new Blockly.FieldDropdown([ + ['On Pressed', 'GAMEPAD_EVENT_PRESSED'], + ['On Released', 'GAMEPAD_EVENT_RELEASED'], + ['On Changed', 'GAMEPAD_EVENT_CHANGED'] + ]), "EVENT"); + this.appendDummyInput() + .appendField("Gamepad") + .appendField(createFieldNumberDropdown(0, 7), "GAMEPAD") + .appendField(new + Blockly.FieldDropdown([ + ['A', 'A'], + ['B', 'B'], + ['X', 'X'], + ['Y', 'Y'], + ['Left Bumper', 'LEFT_BUMPER'], + ['Right Bumper', 'RIGHT_BUMPER'], + ['Back', 'BACK'], + ['Start', 'START'], + ['Left Stick Button', 'LEFT_STICK_BUTTON'], + ['Right Stick Button', 'RIGHT_STICK_BUTTON'] + ]), "BUTTON"); + this.setOutput(false); + this.setStyle(MRC_STYLE_EVENT_HANDLER); + this.appendStatementInput('STACK').appendField(''); + this.setPreviousStatement(false); + this.setNextStatement(false); + + this.setStyle(MRC_STYLE_EVENTS); + }, + }; +}; + +export const pythonFromBlock = function ( + block: Blockly.Block, + generator: PythonGenerator, +) { + // TODO: Update this when the actual driver station display class is implemented + return ''; +}; diff --git a/src/blocks/setup_custom_blocks.ts b/src/blocks/setup_custom_blocks.ts index ee7d88ad..df57bfe2 100644 --- a/src/blocks/setup_custom_blocks.ts +++ b/src/blocks/setup_custom_blocks.ts @@ -24,6 +24,7 @@ import * as JumpToStep from './mrc_jump_to_step'; import * as DisplayAddData from './mrc_display_add_data'; import * as GamepadBoolean from './mrc_gamepad_boolean'; import * as GamepadAnalog from './mrc_gamepad_analog'; +import * as GamepadBooleanEvent from './mrc_gamepad_boolean_event'; const customBlocks = [ CallPythonFunction, @@ -50,7 +51,8 @@ const customBlocks = [ JumpToStep, DisplayAddData, GamepadBoolean, - GamepadAnalog + GamepadAnalog, + GamepadBooleanEvent, ]; export const setup = function(forBlock: any) { diff --git a/src/toolbox/driver_station_category.ts b/src/toolbox/driver_station_category.ts index b11d09fa..b389b531 100644 --- a/src/toolbox/driver_station_category.ts +++ b/src/toolbox/driver_station_category.ts @@ -5,6 +5,7 @@ import { Editor } from '../editor/editor'; import { BLOCK_NAME as MRC_DISPLAY_ADD_DATA } from '../blocks/mrc_display_add_data'; import { BLOCK_NAME as MRC_GAMEPAD_BOOLEAN } from '../blocks/mrc_gamepad_boolean'; import { BLOCK_NAME as MRC_GAMEPAD_ANALOG } from '../blocks/mrc_gamepad_analog'; +import { BLOCK_NAME as MRC_GAMEPAD_BOOLEAN_EVENT } from '../blocks/mrc_gamepad_boolean_event'; export function getDriverStationCategory(editor: Editor): toolboxItems.Category { return new toolboxItems.Category( @@ -30,6 +31,8 @@ function getDriverStationGamepadsCategory(editor: Editor): toolboxItems.Category [ new toolboxItems.Block(MRC_GAMEPAD_BOOLEAN, null, null, null), new toolboxItems.Block(MRC_GAMEPAD_ANALOG, null, null, null), + new toolboxItems.Label("Events"), + new toolboxItems.Block(MRC_GAMEPAD_BOOLEAN_EVENT, null, null, null), ], toolboxItems.ExpandedState.EXPANDED); } \ No newline at end of file From fb341d008fb28008c1f0a99d01c5d75a00e54a3c Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 13:28:00 -0500 Subject: [PATCH 04/12] Clean up warnings --- src/blocks/mrc_gamepad_analog.ts | 29 ++++++++++++-- src/blocks/mrc_gamepad_boolean.ts | 51 ++++++++++++++++++++++--- src/blocks/mrc_gamepad_boolean_event.ts | 10 +++-- src/toolbox/driver_station_category.ts | 4 +- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/src/blocks/mrc_gamepad_analog.ts b/src/blocks/mrc_gamepad_analog.ts index f7b7c807..1e5f2b52 100644 --- a/src/blocks/mrc_gamepad_analog.ts +++ b/src/blocks/mrc_gamepad_analog.ts @@ -25,19 +25,20 @@ */ import * as Blockly from 'blockly/core'; -import { Order, PythonGenerator } from 'blockly/python'; +import { PythonGenerator } from 'blockly/python'; import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; export const BLOCK_NAME = 'mrc_gamepad_analog'; +const GAMEPAD_NUMBER_FIELD = 'GAMEPAD_NUM'; export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() .appendField("Gamepad") - .appendField(createFieldNumberDropdown(0,7), "GAMEPAD") + .appendField(createFieldNumberDropdown(0,7), GAMEPAD_NUMBER_FIELD) .appendField(new Blockly.FieldDropdown([ ['Left stick X', 'LEFT_STICK_X'], @@ -53,10 +54,30 @@ export const setup = function() { }; }; +function getMethodFromAxis(axis: string): string { + switch (axis) { + case 'LEFT_STICK_X': + return 'getLeftX'; + case 'LEFT_STICK_Y': + return 'getLeftY'; + case 'RIGHT_STICK_X': + return 'getRightX'; + case 'RIGHT_STICK_Y': + return 'getRightY'; + case 'LEFT_TRIGGER': + return 'getLeftTrigger'; + case 'RIGHT_TRIGGER': + return 'getRightTrigger'; + default: + return 'getLeftX'; + } +} + export const pythonFromBlock = function( block: Blockly.Block, - generator: PythonGenerator, + _: PythonGenerator, ) { // TODO: Update this when the actual driver station display class is implemented - return ''; + return 'DriverStation.gamepads[' + block.getFieldValue(GAMEPAD_NUMBER_FIELD) + '].' + + getMethodFromAxis(block.getFieldValue('AXIS')) + '()'; }; diff --git a/src/blocks/mrc_gamepad_boolean.ts b/src/blocks/mrc_gamepad_boolean.ts index aa25a1c2..16e4002a 100644 --- a/src/blocks/mrc_gamepad_boolean.ts +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -25,19 +25,20 @@ */ import * as Blockly from 'blockly/core'; -import { Order, PythonGenerator } from 'blockly/python'; +import { PythonGenerator } from 'blockly/python'; import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; export const BLOCK_NAME = 'mrc_gamepad_boolean'; +const GAMEPAD_NUMBER_FIELD = 'GAMEPAD_NUM'; export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() .appendField("Gamepad") - .appendField(createFieldNumberDropdown(0,7), "GAMEPAD") + .appendField(createFieldNumberDropdown(0,7), GAMEPAD_NUMBER_FIELD) .appendField(new Blockly.FieldDropdown([ ['A', 'A'], @@ -62,10 +63,50 @@ export const setup = function() { }; }; +function getMethodFromButton(button: string): string { + switch (button) { + case 'A': + return 'getA'; + case 'B': + return 'getB'; + case 'X': + return 'getX'; + case 'Y': + return 'getY'; + case 'LEFT_BUMPER': + return 'getLeftBumper'; + case 'RIGHT_BUMPER': + return 'getRightBumper'; + case 'BACK': + return 'getBack'; + case 'START': + return 'getStart'; + case 'LEFT_STICK_BUTTON': + return 'getLeftStickButton'; + case 'RIGHT_STICK_BUTTON': + return 'getRightStickButton'; + default: + return ''; + } +} + export const pythonFromBlock = function( block: Blockly.Block, - generator: PythonGenerator, + _: PythonGenerator, ) { // TODO: Update this when the actual driver station display class is implemented - return ''; -}; + let code = 'DriverStation.gamepads[' + block.getFieldValue(GAMEPAD_NUMBER_FIELD) + '].' + + getMethodFromButton(block.getFieldValue('BUTTON')); + switch (block.getFieldValue('ACTION')) { + case 'IS_DOWN': + code += ''; + break; + case 'WAS_PRESSED': + code += 'WasPressed'; + break; + case 'WAS_RELEASED': + code += 'WasReleased'; + break; + } + return code + '()' +} \ No newline at end of file diff --git a/src/blocks/mrc_gamepad_boolean_event.ts b/src/blocks/mrc_gamepad_boolean_event.ts index 513a59a3..fe8ccb67 100644 --- a/src/blocks/mrc_gamepad_boolean_event.ts +++ b/src/blocks/mrc_gamepad_boolean_event.ts @@ -25,8 +25,8 @@ */ import * as Blockly from 'blockly/core'; -import { Order, PythonGenerator } from 'blockly/python'; -import { MRC_STYLE_DRIVER_STATION, MRC_STYLE_EVENT_HANDLER, MRC_STYLE_EVENTS } from '../themes/styles'; +import { PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_EVENT_HANDLER, MRC_STYLE_EVENTS } from '../themes/styles'; import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; @@ -70,8 +70,10 @@ export const setup = function () { export const pythonFromBlock = function ( block: Blockly.Block, - generator: PythonGenerator, + _: PythonGenerator, ) { // TODO: Update this when the actual driver station display class is implemented - return ''; + return '# ' + block.getFieldValue("EVENT") + ' for button ' + + block.getFieldValue("BUTTON") + ' on gamepad ' + + block.getFieldValue("GAMEPAD") + '\n'; }; diff --git a/src/toolbox/driver_station_category.ts b/src/toolbox/driver_station_category.ts index b389b531..2d05de3a 100644 --- a/src/toolbox/driver_station_category.ts +++ b/src/toolbox/driver_station_category.ts @@ -17,7 +17,7 @@ export function getDriverStationCategory(editor: Editor): toolboxItems.Category toolboxItems.ExpandedState.EXPANDED); } -function getDriverStationDisplayCategory(editor: Editor): toolboxItems.Category { +function getDriverStationDisplayCategory(_editor: Editor): toolboxItems.Category { return new toolboxItems.Category( Blockly.Msg['MRC_CATEGORY_DRIVER_STATION_DISPLAY'], [ @@ -25,7 +25,7 @@ function getDriverStationDisplayCategory(editor: Editor): toolboxItems.Category ], toolboxItems.ExpandedState.EXPANDED); } -function getDriverStationGamepadsCategory(editor: Editor): toolboxItems.Category { +function getDriverStationGamepadsCategory(_editor: Editor): toolboxItems.Category { return new toolboxItems.Category( Blockly.Msg['MRC_CATEGORY_DRIVER_STATION_GAMEPADS'], [ From af00229945a76401344eece464aaff40ed9b5f47 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 13:37:33 -0500 Subject: [PATCH 05/12] Make a little less ugly --- src/blocks/mrc_display_add_data.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/blocks/mrc_display_add_data.ts b/src/blocks/mrc_display_add_data.ts index 4d17b3a0..974c58c8 100644 --- a/src/blocks/mrc_display_add_data.ts +++ b/src/blocks/mrc_display_add_data.ts @@ -35,8 +35,7 @@ export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() - .appendField(Blockly.Msg.CALL) - .appendField("display.add_data"); + .appendField("Display.add_data"); this.appendValueInput("CAPTION") .setCheck("String") .setAlign(Blockly.inputs.Align.RIGHT) From 39825089386fa459df7d6e4212821e0e4dc4c736 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 13:38:36 -0500 Subject: [PATCH 06/12] Updating to match 2027 Gamepad class --- src/blocks/mrc_gamepad_boolean.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blocks/mrc_gamepad_boolean.ts b/src/blocks/mrc_gamepad_boolean.ts index 16e4002a..13eb7da4 100644 --- a/src/blocks/mrc_gamepad_boolean.ts +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -55,8 +55,8 @@ export const setup = function() { this.appendDummyInput() .appendField(new Blockly.FieldDropdown([ ['is down', 'IS_DOWN'], - ['was pressed', 'WAS_PRESSED'], - ['was released', 'WAS_RELEASED']]), 'ACTION'); + ['Pressed', 'WAS_PRESSED'], + ['Released', 'WAS_RELEASED']]), 'ACTION'); this.setOutput(true, 'Boolean'); this.setStyle(MRC_STYLE_DRIVER_STATION); }, @@ -102,10 +102,10 @@ export const pythonFromBlock = function( code += ''; break; case 'WAS_PRESSED': - code += 'WasPressed'; + code += 'Pressed'; break; case 'WAS_RELEASED': - code += 'WasReleased'; + code += 'Released'; break; } return code + '()' From 4765af720bae8db711a906d833a7c96be3b1826e Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:38:33 -0500 Subject: [PATCH 07/12] Pull gamepad pieces into field_gamepads --- src/blocks/mrc_gamepad_analog.ts | 45 ++------- src/blocks/mrc_gamepad_boolean.ts | 73 ++------------- src/blocks/mrc_gamepad_boolean_event.ts | 33 ++----- src/fields/field_gamepads.ts | 119 ++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 125 deletions(-) create mode 100644 src/fields/field_gamepads.ts diff --git a/src/blocks/mrc_gamepad_analog.ts b/src/blocks/mrc_gamepad_analog.ts index 1e5f2b52..7d039a7e 100644 --- a/src/blocks/mrc_gamepad_analog.ts +++ b/src/blocks/mrc_gamepad_analog.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2025 Porpoiseful LLC + * Copyright 2026 Porpoiseful LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,57 +27,30 @@ import * as Blockly from 'blockly/core'; import { PythonGenerator } from 'blockly/python'; import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; -import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; - +import * as Gamepad from '../fields/field_gamepads'; export const BLOCK_NAME = 'mrc_gamepad_analog'; -const GAMEPAD_NUMBER_FIELD = 'GAMEPAD_NUM'; + export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() .appendField("Gamepad") - .appendField(createFieldNumberDropdown(0,7), GAMEPAD_NUMBER_FIELD) - .appendField(new - Blockly.FieldDropdown([ - ['Left stick X', 'LEFT_STICK_X'], - ['Left stick Y', 'LEFT_STICK_Y'], - ['Right stick X', 'RIGHT_STICK_X'], - ['Right stick Y', 'RIGHT_STICK_Y'], - ['Left trigger', 'LEFT_TRIGGER'], - ['Right trigger', 'RIGHT_TRIGGER'], - ]), "AXIS"); + .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) + .appendField(Gamepad.createAnalogAxisField(), Gamepad.AXIS_FIELD_NAME); this.setOutput(true, 'Number'); this.setStyle(MRC_STYLE_DRIVER_STATION); }, }; }; -function getMethodFromAxis(axis: string): string { - switch (axis) { - case 'LEFT_STICK_X': - return 'getLeftX'; - case 'LEFT_STICK_Y': - return 'getLeftY'; - case 'RIGHT_STICK_X': - return 'getRightX'; - case 'RIGHT_STICK_Y': - return 'getRightY'; - case 'LEFT_TRIGGER': - return 'getLeftTrigger'; - case 'RIGHT_TRIGGER': - return 'getRightTrigger'; - default: - return 'getLeftX'; - } -} - export const pythonFromBlock = function( block: Blockly.Block, _: PythonGenerator, ) { - // TODO: Update this when the actual driver station display class is implemented - return 'DriverStation.gamepads[' + block.getFieldValue(GAMEPAD_NUMBER_FIELD) + '].' - + getMethodFromAxis(block.getFieldValue('AXIS')) + '()'; + return Gamepad.methodForAxis( + block.getFieldValue(Gamepad.PORT_FIELD_NAME), + block.getFieldValue(Gamepad.AXIS_FIELD_NAME) + ); }; diff --git a/src/blocks/mrc_gamepad_boolean.ts b/src/blocks/mrc_gamepad_boolean.ts index 13eb7da4..8acbc057 100644 --- a/src/blocks/mrc_gamepad_boolean.ts +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2025 Porpoiseful LLC + * Copyright 2026 Porpoiseful LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,86 +27,31 @@ import * as Blockly from 'blockly/core'; import { PythonGenerator } from 'blockly/python'; import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; -import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; - +import * as Gamepad from '../fields/field_gamepads'; export const BLOCK_NAME = 'mrc_gamepad_boolean'; -const GAMEPAD_NUMBER_FIELD = 'GAMEPAD_NUM'; export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() .appendField("Gamepad") - .appendField(createFieldNumberDropdown(0,7), GAMEPAD_NUMBER_FIELD) - .appendField(new - Blockly.FieldDropdown([ - ['A', 'A'], - ['B', 'B'], - ['X', 'X'], - ['Y', 'Y'], - ['Left Bumper', 'LEFT_BUMPER'], - ['Right Bumper', 'RIGHT_BUMPER'], - ['Back', 'BACK'], - ['Start', 'START'], - ['Left Stick Button', 'LEFT_STICK_BUTTON'], - ['Right Stick Button', 'RIGHT_STICK_BUTTON'] - ]), "BUTTON"); + .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) + .appendField(Gamepad.createButtonField(), Gamepad.BUTTON_FIELD_NAME) this.appendDummyInput() - .appendField(new Blockly.FieldDropdown([ - ['is down', 'IS_DOWN'], - ['Pressed', 'WAS_PRESSED'], - ['Released', 'WAS_RELEASED']]), 'ACTION'); + .appendField(Gamepad.createActionField(), Gamepad.ACTION_FIELD_NAME); this.setOutput(true, 'Boolean'); this.setStyle(MRC_STYLE_DRIVER_STATION); }, }; }; -function getMethodFromButton(button: string): string { - switch (button) { - case 'A': - return 'getA'; - case 'B': - return 'getB'; - case 'X': - return 'getX'; - case 'Y': - return 'getY'; - case 'LEFT_BUMPER': - return 'getLeftBumper'; - case 'RIGHT_BUMPER': - return 'getRightBumper'; - case 'BACK': - return 'getBack'; - case 'START': - return 'getStart'; - case 'LEFT_STICK_BUTTON': - return 'getLeftStickButton'; - case 'RIGHT_STICK_BUTTON': - return 'getRightStickButton'; - default: - return ''; - } -} - export const pythonFromBlock = function( block: Blockly.Block, _: PythonGenerator, ) { - // TODO: Update this when the actual driver station display class is implemented - let code = 'DriverStation.gamepads[' + block.getFieldValue(GAMEPAD_NUMBER_FIELD) + '].' - + getMethodFromButton(block.getFieldValue('BUTTON')); - switch (block.getFieldValue('ACTION')) { - case 'IS_DOWN': - code += ''; - break; - case 'WAS_PRESSED': - code += 'Pressed'; - break; - case 'WAS_RELEASED': - code += 'Released'; - break; - } - return code + '()' + return Gamepad.methodForButton( + block.getFieldValue(Gamepad.PORT_FIELD_NAME), + block.getFieldValue(Gamepad.BUTTON_FIELD_NAME), + block.getFieldValue(Gamepad.ACTION_FIELD_NAME)); } \ No newline at end of file diff --git a/src/blocks/mrc_gamepad_boolean_event.ts b/src/blocks/mrc_gamepad_boolean_event.ts index fe8ccb67..d86b6a16 100644 --- a/src/blocks/mrc_gamepad_boolean_event.ts +++ b/src/blocks/mrc_gamepad_boolean_event.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2025 Porpoiseful LLC + * Copyright 2026 Porpoiseful LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,7 @@ import * as Blockly from 'blockly/core'; import { PythonGenerator } from 'blockly/python'; import { MRC_STYLE_EVENT_HANDLER, MRC_STYLE_EVENTS } from '../themes/styles'; -import { createFieldNumberDropdown } from '../fields/field_number_dropdown'; - +import * as Gamepad from '../fields/field_gamepads'; export const BLOCK_NAME = 'mrc_gamepad_boolean_event'; @@ -36,27 +35,11 @@ export const setup = function () { Blockly.Blocks[BLOCK_NAME] = { init: function () { this.appendDummyInput() - .appendField(new Blockly.FieldDropdown([ - ['On Pressed', 'GAMEPAD_EVENT_PRESSED'], - ['On Released', 'GAMEPAD_EVENT_RELEASED'], - ['On Changed', 'GAMEPAD_EVENT_CHANGED'] - ]), "EVENT"); + .appendField(Gamepad.createEventField(), Gamepad.EVENT_FIELD_NAME); this.appendDummyInput() .appendField("Gamepad") - .appendField(createFieldNumberDropdown(0, 7), "GAMEPAD") - .appendField(new - Blockly.FieldDropdown([ - ['A', 'A'], - ['B', 'B'], - ['X', 'X'], - ['Y', 'Y'], - ['Left Bumper', 'LEFT_BUMPER'], - ['Right Bumper', 'RIGHT_BUMPER'], - ['Back', 'BACK'], - ['Start', 'START'], - ['Left Stick Button', 'LEFT_STICK_BUTTON'], - ['Right Stick Button', 'RIGHT_STICK_BUTTON'] - ]), "BUTTON"); + .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) + .appendField(Gamepad.createButtonField(), Gamepad.BUTTON_FIELD_NAME) this.setOutput(false); this.setStyle(MRC_STYLE_EVENT_HANDLER); this.appendStatementInput('STACK').appendField(''); @@ -73,7 +56,7 @@ export const pythonFromBlock = function ( _: PythonGenerator, ) { // TODO: Update this when the actual driver station display class is implemented - return '# ' + block.getFieldValue("EVENT") + ' for button ' - + block.getFieldValue("BUTTON") + ' on gamepad ' - + block.getFieldValue("GAMEPAD") + '\n'; + return '# ' + block.getFieldValue(Gamepad.EVENT_FIELD_NAME) + ' for button ' + + block.getFieldValue(Gamepad.BUTTON_FIELD_NAME) + ' on gamepad ' + + block.getFieldValue(Gamepad.PORT_FIELD_NAME) + '\n'; }; diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts new file mode 100644 index 00000000..0179461f --- /dev/null +++ b/src/fields/field_gamepads.ts @@ -0,0 +1,119 @@ +/** + * @license + * Copyright 2026 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 has the fields for selecting gamepad ports and buttons + * @author alan@porpoiseful.com (Alan Smith) + */ + +import * as Blockly from 'blockly/core'; +import { createFieldNumberDropdown } from './field_number_dropdown'; + +const MIN_GAMEPAD_PORT = 0; +const MAX_GAMEPAD_PORT = 7; + +export const PORT_FIELD_NAME = 'GAMEPAD_PORT'; +export const BUTTON_FIELD_NAME = 'GAMEPAD_BUTTON'; +export const ACTION_FIELD_NAME = 'GAMEPAD_ACTION'; +export const AXIS_FIELD_NAME = 'GAMEPAD_AXIS'; +export const EVENT_FIELD_NAME = 'GAMEPAD_EVENT'; + +const BUTTON_CONFIG = new Map([ + ['A', { display: 'A', method: 'getA' }], + ['B', { display: 'B', method: 'getB' }], + ['X', { display: 'X', method: 'getX' }], + ['Y', { display: 'Y', method: 'getY' }], + ['LEFT_BUMPER', { display: 'Left Bumper', method: 'getLeftBumper' }], + ['RIGHT_BUMPER', { display: 'Right Bumper', method: 'getRightBumper' }], + ['BACK', { display: 'Back', method: 'getBack' }], + ['START', { display: 'Start', method: 'getStart' }], + ['LEFT_STICK_BUTTON', { display: 'Left Stick Button', method: 'getLeftStickButton' }], + ['RIGHT_STICK_BUTTON', { display: 'Right Stick Button', method: 'getRightStickButton' }] +]); + +const AXIS_CONFIG = new Map([ + ['LEFT_STICK_X', { display: 'Left stick X', method: 'getLeftX' }], + ['LEFT_STICK_Y', { display: 'Left stick Y', method: 'getLeftY' }], + ['RIGHT_STICK_X', { display: 'Right stick X', method: 'getRightX' }], + ['RIGHT_STICK_Y', { display: 'Right stick Y', method: 'getRightY' }], + ['LEFT_TRIGGER', { display: 'Left trigger', method: 'getLeftTrigger' }], + ['RIGHT_TRIGGER', { display: 'Right trigger', method: 'getRightTrigger' }] +]); + +const ACTION_CONFIG = new Map([ + ['IS_DOWN', { display: 'is down', suffix: '' }], + ['WAS_PRESSED', { display: 'Pressed', suffix: 'Pressed' }], + ['WAS_RELEASED', { display: 'Released', suffix: 'Released' }] +]); + +const EVENT_CONFIG = new Map([ + ['GAMEPAD_EVENT_PRESSED', { display: 'On Pressed' }], + ['GAMEPAD_EVENT_RELEASED', { display: 'On Released' }], + ['GAMEPAD_EVENT_CHANGED', { display: 'On Changed' }] +]); + +export function createPortField(): Blockly.Field { + return createFieldNumberDropdown(MIN_GAMEPAD_PORT, MAX_GAMEPAD_PORT) +} + +export function createButtonField(): Blockly.Field { + return new Blockly.FieldDropdown( + Array.from(BUTTON_CONFIG.entries()).map(([key, config]) => [config.display, key]) + ) +} +export function createAnalogAxisField(): Blockly.Field { + return new Blockly.FieldDropdown( + Array.from(AXIS_CONFIG.entries()).map(([key, config]) => [config.display, key]) + ) +} + +export function createActionField(): Blockly.Field { + return new Blockly.FieldDropdown( + Array.from(ACTION_CONFIG.entries()).map(([key, config]) => [config.display, key]) + ) +} + +export function createEventField(): Blockly.Field { + return new Blockly.FieldDropdown( + Array.from(EVENT_CONFIG.entries()).map(([key, config]) => [config.display, key]) + ) +} + +function getGamepad(gamepad: number): string { + // TODO: Update this when the actual driver station display class is implemented + return 'DriverStation.gamepads[' + gamepad + ']'; +} + +export function methodForButton(gamepad: number, button: string, action: string): string { + // TODO: Update this when the actual driver station display class is implemented + const config = BUTTON_CONFIG.get(button); + const suffix = ACTION_CONFIG.get(action); + + if (config === undefined || suffix === undefined) { + return ''; + } + + return getGamepad(gamepad) + '.' + + config.method + + suffix.suffix + '()'; +} + +export function methodForAxis(gamepad: number, axis: string): string { + // TODO: Update this when the actual driver station display class is implemented + return getGamepad(gamepad) + '.' + + (AXIS_CONFIG.get(axis)?.method ?? '') + '()'; +} \ No newline at end of file From b257bbbc565dac76ef72d7e89ffa4c6e6774dc72 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:40:46 -0500 Subject: [PATCH 08/12] Move title to field_gamepads --- src/blocks/mrc_gamepad_analog.ts | 2 +- src/blocks/mrc_gamepad_boolean.ts | 2 +- src/blocks/mrc_gamepad_boolean_event.ts | 2 +- src/fields/field_gamepads.ts | 4 ++++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/blocks/mrc_gamepad_analog.ts b/src/blocks/mrc_gamepad_analog.ts index 7d039a7e..5be35af3 100644 --- a/src/blocks/mrc_gamepad_analog.ts +++ b/src/blocks/mrc_gamepad_analog.ts @@ -36,7 +36,7 @@ export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() - .appendField("Gamepad") + .appendField(Gamepad.createTitleField()) .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) .appendField(Gamepad.createAnalogAxisField(), Gamepad.AXIS_FIELD_NAME); this.setOutput(true, 'Number'); diff --git a/src/blocks/mrc_gamepad_boolean.ts b/src/blocks/mrc_gamepad_boolean.ts index 8acbc057..f2eb077f 100644 --- a/src/blocks/mrc_gamepad_boolean.ts +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -35,7 +35,7 @@ export const setup = function() { Blockly.Blocks[BLOCK_NAME] = { init: function() { this.appendDummyInput() - .appendField("Gamepad") + .appendField(Gamepad.createTitleField()) .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) .appendField(Gamepad.createButtonField(), Gamepad.BUTTON_FIELD_NAME) this.appendDummyInput() diff --git a/src/blocks/mrc_gamepad_boolean_event.ts b/src/blocks/mrc_gamepad_boolean_event.ts index d86b6a16..84cd859b 100644 --- a/src/blocks/mrc_gamepad_boolean_event.ts +++ b/src/blocks/mrc_gamepad_boolean_event.ts @@ -37,7 +37,7 @@ export const setup = function () { this.appendDummyInput() .appendField(Gamepad.createEventField(), Gamepad.EVENT_FIELD_NAME); this.appendDummyInput() - .appendField("Gamepad") + .appendField(Gamepad.createTitleField()) .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) .appendField(Gamepad.createButtonField(), Gamepad.BUTTON_FIELD_NAME) this.setOutput(false); diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts index 0179461f..0576d3fa 100644 --- a/src/fields/field_gamepads.ts +++ b/src/fields/field_gamepads.ts @@ -66,6 +66,10 @@ const EVENT_CONFIG = new Map([ ['GAMEPAD_EVENT_CHANGED', { display: 'On Changed' }] ]); +export function createTitleField(): Blockly.Field { + return new Blockly.FieldLabel('Gamepad'); +} + export function createPortField(): Blockly.Field { return createFieldNumberDropdown(MIN_GAMEPAD_PORT, MAX_GAMEPAD_PORT) } From 78558035a8ba318836eabfc05644d9c9c5433853 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:44:28 -0500 Subject: [PATCH 09/12] Add PS equivalents --- src/fields/field_gamepads.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts index 0576d3fa..3029a67c 100644 --- a/src/fields/field_gamepads.ts +++ b/src/fields/field_gamepads.ts @@ -33,14 +33,15 @@ export const AXIS_FIELD_NAME = 'GAMEPAD_AXIS'; export const EVENT_FIELD_NAME = 'GAMEPAD_EVENT'; const BUTTON_CONFIG = new Map([ - ['A', { display: 'A', method: 'getA' }], - ['B', { display: 'B', method: 'getB' }], - ['X', { display: 'X', method: 'getX' }], - ['Y', { display: 'Y', method: 'getY' }], + ['A', { display: 'A (cross)', method: 'getA' }], + ['B', { display: 'B (circle)', method: 'getB' }], + ['X', { display: 'X (square)', method: 'getX' }], + ['Y', { display: 'Y (triangle)', method: 'getY' }], ['LEFT_BUMPER', { display: 'Left Bumper', method: 'getLeftBumper' }], ['RIGHT_BUMPER', { display: 'Right Bumper', method: 'getRightBumper' }], - ['BACK', { display: 'Back', method: 'getBack' }], - ['START', { display: 'Start', method: 'getStart' }], + ['BACK', { display: 'Back (Share)', method: 'getBack' }], + ['START', { display: 'Start (Options)', method: 'getStart' }], + ['GUIDE', { display: 'Guide (PS)', method: 'getGuide' }], ['LEFT_STICK_BUTTON', { display: 'Left Stick Button', method: 'getLeftStickButton' }], ['RIGHT_STICK_BUTTON', { display: 'Right Stick Button', method: 'getRightStickButton' }] ]); From e38874c245cbe08086a3fa858dbcba2ccf7cf094 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:45:13 -0500 Subject: [PATCH 10/12] Adding Dpad ones --- src/fields/field_gamepads.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts index 3029a67c..b68785b9 100644 --- a/src/fields/field_gamepads.ts +++ b/src/fields/field_gamepads.ts @@ -37,6 +37,10 @@ const BUTTON_CONFIG = new Map([ ['B', { display: 'B (circle)', method: 'getB' }], ['X', { display: 'X (square)', method: 'getX' }], ['Y', { display: 'Y (triangle)', method: 'getY' }], + ['DPAD_UP', { display: 'D-pad Up', method: 'getDpadUp' }], + ['DPAD_DOWN', { display: 'D-pad Down', method: 'getDpadDown' }], + ['DPAD_LEFT', { display: 'D-pad Left', method: 'getDpadLeft' }], + ['DPAD_RIGHT', { display: 'D-pad Right', method: 'getDpadRight' }], ['LEFT_BUMPER', { display: 'Left Bumper', method: 'getLeftBumper' }], ['RIGHT_BUMPER', { display: 'Right Bumper', method: 'getRightBumper' }], ['BACK', { display: 'Back (Share)', method: 'getBack' }], From 1b5bca4fae1b31db6ef971357b2a3a9c353c6f78 Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:51:49 -0500 Subject: [PATCH 11/12] Change to use i18n system for Gamepad items --- src/blocks/tokens.ts | 28 ++++++++++++ src/fields/field_gamepads.ts | 64 ++++++++++++++-------------- src/i18n/locales/en/translation.json | 30 +++++++++++++ src/i18n/locales/es/translation.json | 30 +++++++++++++ src/i18n/locales/he/translation.json | 33 +++++++++++++- 5 files changed, 152 insertions(+), 33 deletions(-) diff --git a/src/blocks/tokens.ts b/src/blocks/tokens.ts index ba1d86c4..4acf9cea 100644 --- a/src/blocks/tokens.ts +++ b/src/blocks/tokens.ts @@ -90,6 +90,34 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg { CAPTION: t('BLOCKLY.CAPTION'), VALUE: t('BLOCKLY.VALUE'), DISPLAY_ADD_DATA_TOOLTIP: t('BLOCKLY.TOOLTIP.DISPLAY_ADD_DATA'), + GAMEPAD: t('BLOCKLY.GAMEPAD.TITLE'), + GAMEPAD_BUTTON_A: t('BLOCKLY.GAMEPAD.BUTTON_A'), + GAMEPAD_BUTTON_B: t('BLOCKLY.GAMEPAD.BUTTON_B'), + GAMEPAD_BUTTON_X: t('BLOCKLY.GAMEPAD.BUTTON_X'), + GAMEPAD_BUTTON_Y: t('BLOCKLY.GAMEPAD.BUTTON_Y'), + GAMEPAD_DPAD_UP: t('BLOCKLY.GAMEPAD.DPAD_UP'), + GAMEPAD_DPAD_DOWN: t('BLOCKLY.GAMEPAD.DPAD_DOWN'), + GAMEPAD_DPAD_LEFT: t('BLOCKLY.GAMEPAD.DPAD_LEFT'), + GAMEPAD_DPAD_RIGHT: t('BLOCKLY.GAMEPAD.DPAD_RIGHT'), + GAMEPAD_LEFT_BUMPER: t('BLOCKLY.GAMEPAD.LEFT_BUMPER'), + GAMEPAD_RIGHT_BUMPER: t('BLOCKLY.GAMEPAD.RIGHT_BUMPER'), + GAMEPAD_BACK: t('BLOCKLY.GAMEPAD.BACK'), + GAMEPAD_START: t('BLOCKLY.GAMEPAD.START'), + GAMEPAD_GUIDE: t('BLOCKLY.GAMEPAD.GUIDE'), + GAMEPAD_LEFT_STICK_BUTTON: t('BLOCKLY.GAMEPAD.LEFT_STICK_BUTTON'), + GAMEPAD_RIGHT_STICK_BUTTON: t('BLOCKLY.GAMEPAD.RIGHT_STICK_BUTTON'), + GAMEPAD_LEFT_STICK_X: t('BLOCKLY.GAMEPAD.LEFT_STICK_X'), + GAMEPAD_LEFT_STICK_Y: t('BLOCKLY.GAMEPAD.LEFT_STICK_Y'), + GAMEPAD_RIGHT_STICK_X: t('BLOCKLY.GAMEPAD.RIGHT_STICK_X'), + GAMEPAD_RIGHT_STICK_Y: t('BLOCKLY.GAMEPAD.RIGHT_STICK_Y'), + GAMEPAD_LEFT_TRIGGER: t('BLOCKLY.GAMEPAD.LEFT_TRIGGER'), + GAMEPAD_RIGHT_TRIGGER: t('BLOCKLY.GAMEPAD.RIGHT_TRIGGER'), + GAMEPAD_IS_DOWN: t('BLOCKLY.GAMEPAD.IS_DOWN'), + GAMEPAD_PRESSED: t('BLOCKLY.GAMEPAD.PRESSED'), + GAMEPAD_RELEASED: t('BLOCKLY.GAMEPAD.RELEASED'), + GAMEPAD_EVENT_PRESSED: t('BLOCKLY.GAMEPAD.EVENT_PRESSED'), + GAMEPAD_EVENT_RELEASED: t('BLOCKLY.GAMEPAD.EVENT_RELEASED'), + GAMEPAD_EVENT_CHANGED: t('BLOCKLY.GAMEPAD.EVENT_CHANGED'), SET_MODULE_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_MODULE_VARIABLE'), SET_CLASS_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_CLASS_VARIABLE'), SET_INSTANCE_VARIABLE_TOOLTIP: t('BLOCKLY.TOOLTIP.SET_INSTANCE_VARIABLE'), diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts index b68785b9..41a92568 100644 --- a/src/fields/field_gamepads.ts +++ b/src/fields/field_gamepads.ts @@ -33,46 +33,46 @@ export const AXIS_FIELD_NAME = 'GAMEPAD_AXIS'; export const EVENT_FIELD_NAME = 'GAMEPAD_EVENT'; const BUTTON_CONFIG = new Map([ - ['A', { display: 'A (cross)', method: 'getA' }], - ['B', { display: 'B (circle)', method: 'getB' }], - ['X', { display: 'X (square)', method: 'getX' }], - ['Y', { display: 'Y (triangle)', method: 'getY' }], - ['DPAD_UP', { display: 'D-pad Up', method: 'getDpadUp' }], - ['DPAD_DOWN', { display: 'D-pad Down', method: 'getDpadDown' }], - ['DPAD_LEFT', { display: 'D-pad Left', method: 'getDpadLeft' }], - ['DPAD_RIGHT', { display: 'D-pad Right', method: 'getDpadRight' }], - ['LEFT_BUMPER', { display: 'Left Bumper', method: 'getLeftBumper' }], - ['RIGHT_BUMPER', { display: 'Right Bumper', method: 'getRightBumper' }], - ['BACK', { display: 'Back (Share)', method: 'getBack' }], - ['START', { display: 'Start (Options)', method: 'getStart' }], - ['GUIDE', { display: 'Guide (PS)', method: 'getGuide' }], - ['LEFT_STICK_BUTTON', { display: 'Left Stick Button', method: 'getLeftStickButton' }], - ['RIGHT_STICK_BUTTON', { display: 'Right Stick Button', method: 'getRightStickButton' }] + ['A', { display: () => Blockly.Msg['GAMEPAD_BUTTON_A'], method: 'getA' }], + ['B', { display: () => Blockly.Msg['GAMEPAD_BUTTON_B'], method: 'getB' }], + ['X', { display: () => Blockly.Msg['GAMEPAD_BUTTON_X'], method: 'getX' }], + ['Y', { display: () => Blockly.Msg['GAMEPAD_BUTTON_Y'], method: 'getY' }], + ['DPAD_UP', { display: () => Blockly.Msg['GAMEPAD_DPAD_UP'], method: 'getDpadUp' }], + ['DPAD_DOWN', { display: () => Blockly.Msg['GAMEPAD_DPAD_DOWN'], method: 'getDpadDown' }], + ['DPAD_LEFT', { display: () => Blockly.Msg['GAMEPAD_DPAD_LEFT'], method: 'getDpadLeft' }], + ['DPAD_RIGHT', { display: () => Blockly.Msg['GAMEPAD_DPAD_RIGHT'], method: 'getDpadRight' }], + ['LEFT_BUMPER', { display: () => Blockly.Msg['GAMEPAD_LEFT_BUMPER'], method: 'getLeftBumper' }], + ['RIGHT_BUMPER', { display: () => Blockly.Msg['GAMEPAD_RIGHT_BUMPER'], method: 'getRightBumper' }], + ['BACK', { display: () => Blockly.Msg['GAMEPAD_BACK'], method: 'getBack' }], + ['START', { display: () => Blockly.Msg['GAMEPAD_START'], method: 'getStart' }], + ['GUIDE', { display: () => Blockly.Msg['GAMEPAD_GUIDE'], method: 'getGuide' }], + ['LEFT_STICK_BUTTON', { display: () => Blockly.Msg['GAMEPAD_LEFT_STICK_BUTTON'], method: 'getLeftStickButton' }], + ['RIGHT_STICK_BUTTON', { display: () => Blockly.Msg['GAMEPAD_RIGHT_STICK_BUTTON'], method: 'getRightStickButton' }] ]); const AXIS_CONFIG = new Map([ - ['LEFT_STICK_X', { display: 'Left stick X', method: 'getLeftX' }], - ['LEFT_STICK_Y', { display: 'Left stick Y', method: 'getLeftY' }], - ['RIGHT_STICK_X', { display: 'Right stick X', method: 'getRightX' }], - ['RIGHT_STICK_Y', { display: 'Right stick Y', method: 'getRightY' }], - ['LEFT_TRIGGER', { display: 'Left trigger', method: 'getLeftTrigger' }], - ['RIGHT_TRIGGER', { display: 'Right trigger', method: 'getRightTrigger' }] + ['LEFT_STICK_X', { display: () => Blockly.Msg['GAMEPAD_LEFT_STICK_X'], method: 'getLeftX' }], + ['LEFT_STICK_Y', { display: () => Blockly.Msg['GAMEPAD_LEFT_STICK_Y'], method: 'getLeftY' }], + ['RIGHT_STICK_X', { display: () => Blockly.Msg['GAMEPAD_RIGHT_STICK_X'], method: 'getRightX' }], + ['RIGHT_STICK_Y', { display: () => Blockly.Msg['GAMEPAD_RIGHT_STICK_Y'], method: 'getRightY' }], + ['LEFT_TRIGGER', { display: () => Blockly.Msg['GAMEPAD_LEFT_TRIGGER'], method: 'getLeftTrigger' }], + ['RIGHT_TRIGGER', { display: () => Blockly.Msg['GAMEPAD_RIGHT_TRIGGER'], method: 'getRightTrigger' }] ]); const ACTION_CONFIG = new Map([ - ['IS_DOWN', { display: 'is down', suffix: '' }], - ['WAS_PRESSED', { display: 'Pressed', suffix: 'Pressed' }], - ['WAS_RELEASED', { display: 'Released', suffix: 'Released' }] + ['IS_DOWN', { display: () => Blockly.Msg['GAMEPAD_IS_DOWN'], suffix: '' }], + ['WAS_PRESSED', { display: () => Blockly.Msg['GAMEPAD_PRESSED'], suffix: 'Pressed' }], + ['WAS_RELEASED', { display: () => Blockly.Msg['GAMEPAD_RELEASED'], suffix: 'Released' }] ]); const EVENT_CONFIG = new Map([ - ['GAMEPAD_EVENT_PRESSED', { display: 'On Pressed' }], - ['GAMEPAD_EVENT_RELEASED', { display: 'On Released' }], - ['GAMEPAD_EVENT_CHANGED', { display: 'On Changed' }] + ['GAMEPAD_EVENT_PRESSED', { display: () => Blockly.Msg['GAMEPAD_EVENT_PRESSED'] }], + ['GAMEPAD_EVENT_RELEASED', { display: () => Blockly.Msg['GAMEPAD_EVENT_RELEASED'] }], + ['GAMEPAD_EVENT_CHANGED', { display: () => Blockly.Msg['GAMEPAD_EVENT_CHANGED'] }] ]); export function createTitleField(): Blockly.Field { - return new Blockly.FieldLabel('Gamepad'); + return new Blockly.FieldLabel(Blockly.Msg['GAMEPAD']); } export function createPortField(): Blockly.Field { @@ -81,24 +81,24 @@ export function createPortField(): Blockly.Field { export function createButtonField(): Blockly.Field { return new Blockly.FieldDropdown( - Array.from(BUTTON_CONFIG.entries()).map(([key, config]) => [config.display, key]) + Array.from(BUTTON_CONFIG.entries()).map(([key, config]) => [config.display(), key]) ) } export function createAnalogAxisField(): Blockly.Field { return new Blockly.FieldDropdown( - Array.from(AXIS_CONFIG.entries()).map(([key, config]) => [config.display, key]) + Array.from(AXIS_CONFIG.entries()).map(([key, config]) => [config.display(), key]) ) } export function createActionField(): Blockly.Field { return new Blockly.FieldDropdown( - Array.from(ACTION_CONFIG.entries()).map(([key, config]) => [config.display, key]) + Array.from(ACTION_CONFIG.entries()).map(([key, config]) => [config.display(), key]) ) } export function createEventField(): Blockly.Field { return new Blockly.FieldDropdown( - Array.from(EVENT_CONFIG.entries()).map(([key, config]) => [config.display, key]) + Array.from(EVENT_CONFIG.entries()).map(([key, config]) => [config.display(), key]) ) } diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 74c942fe..5623e028 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -178,6 +178,36 @@ "MORE_MECHANISM_METHODS_LABEL": "More Mechanism Methods", "MORE_OPMODE_METHODS_LABEL": "More OpMode Methods", "COMMENT_DEFAULT_TEXT": "Enter your comment here!", + "GAMEPAD": { + "TITLE": "Gamepad", + "BUTTON_A": "A (cross)", + "BUTTON_B": "B (circle)", + "BUTTON_X": "X (square)", + "BUTTON_Y": "Y (triangle)", + "DPAD_UP": "D-pad Up", + "DPAD_DOWN": "D-pad Down", + "DPAD_LEFT": "D-pad Left", + "DPAD_RIGHT": "D-pad Right", + "LEFT_BUMPER": "Left Bumper", + "RIGHT_BUMPER": "Right Bumper", + "BACK": "Back (Share)", + "START": "Start (Options)", + "GUIDE": "Guide (PS)", + "LEFT_STICK_BUTTON": "Left Stick Button", + "RIGHT_STICK_BUTTON": "Right Stick Button", + "LEFT_STICK_X": "Left stick X", + "LEFT_STICK_Y": "Left stick Y", + "RIGHT_STICK_X": "Right stick X", + "RIGHT_STICK_Y": "Right stick Y", + "LEFT_TRIGGER": "Left trigger", + "RIGHT_TRIGGER": "Right trigger", + "IS_DOWN": "is down", + "PRESSED": "Pressed", + "RELEASED": "Released", + "EVENT_PRESSED": "On Pressed", + "EVENT_RELEASED": "On Released", + "EVENT_CHANGED": "On Changed" + }, "TOOLTIP":{ "EVALUATE_BUT_IGNORE_RESULT": "Executes the connected block and ignores the result. Allows you to call a function and ignore the return value.", "NONE": "Returns None.", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 19411c2a..435b15bd 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -179,6 +179,36 @@ "MORE_MECHANISM_METHODS_LABEL": "Más Métodos del Mecanismo", "MORE_OPMODE_METHODS_LABEL": "Más Métodos del OpMode", "COMMENT_DEFAULT_TEXT": "¡Ingresa tu comentario aquí!", + "GAMEPAD": { + "TITLE": "Mando de Juego", + "BUTTON_A": "A (cruz)", + "BUTTON_B": "B (círculo)", + "BUTTON_X": "X (cuadrado)", + "BUTTON_Y": "Y (triángulo)", + "DPAD_UP": "D-pad Arriba", + "DPAD_DOWN": "D-pad Abajo", + "DPAD_LEFT": "D-pad Izquierda", + "DPAD_RIGHT": "D-pad Derecha", + "LEFT_BUMPER": "Gatillo Izquierdo Superior", + "RIGHT_BUMPER": "Gatillo Derecho Superior", + "BACK": "Atrás (Compartir)", + "START": "Inicio (Opciones)", + "GUIDE": "Guía (PS)", + "LEFT_STICK_BUTTON": "Botón Palanca Izquierda", + "RIGHT_STICK_BUTTON": "Botón Palanca Derecha", + "LEFT_STICK_X": "Palanca izquierda X", + "LEFT_STICK_Y": "Palanca izquierda Y", + "RIGHT_STICK_X": "Palanca derecha X", + "RIGHT_STICK_Y": "Palanca derecha Y", + "LEFT_TRIGGER": "Gatillo izquierdo", + "RIGHT_TRIGGER": "Gatillo derecho", + "IS_DOWN": "está presionado", + "PRESSED": "Presionado", + "RELEASED": "Liberado", + "EVENT_PRESSED": "Al Presionar", + "EVENT_RELEASED": "Al Liberar", + "EVENT_CHANGED": "Al Cambiar" + }, "TOOLTIP": { "EVALUATE_BUT_IGNORE_RESULT": "Ejecuta el bloque conectado e ignora el resultado. Te permite llamar una función e ignorar el valor de retorno.", "NONE": "No devuelve ninguno.", diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index ddd39012..eba0da4d 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -167,7 +167,8 @@ "GET": "קבל", "SET": "הגדר", "TO": "ל", "CAPTION": "כיתוב", - "VALUE": "ערך", "STEPS": "צעדים", + "VALUE": "ערך", + "STEPS": "צעדים", "REPEAT_UNTIL": "לחזור על כך עד", "JUMP_TO": "לקפוץ אל", "CUSTOM_EVENTS_LABEL": "אירועים מותאמים אישית", @@ -176,6 +177,36 @@ "MORE_MECHANISM_METHODS_LABEL": "מתודות נוספות למנגנון", "MORE_OPMODE_METHODS_LABEL": "מתודות נוספות לאופמוד", "COMMENT_DEFAULT_TEXT": "הזן את ההערה שלך כאן!", + "GAMEPAD": { + "TITLE": "בקר משחק", + "BUTTON_A": "A (צלב)", + "BUTTON_B": "B (עיגול)", + "BUTTON_X": "X (ריבוע)", + "BUTTON_Y": "Y (משולש)", + "DPAD_UP": "D-pad למעלה", + "DPAD_DOWN": "D-pad למטה", + "DPAD_LEFT": "D-pad שמאלה", + "DPAD_RIGHT": "D-pad ימינה", + "LEFT_BUMPER": "כפתור שמאלי עליון", + "RIGHT_BUMPER": "כפתור ימני עליון", + "BACK": "חזור (שיתוף)", + "START": "התחל (אפשרויות)", + "GUIDE": "מדריך (PS)", + "LEFT_STICK_BUTTON": "כפתור ידית שמאלית", + "RIGHT_STICK_BUTTON": "כפתור ידית ימנית", + "LEFT_STICK_X": "ידית שמאלית X", + "LEFT_STICK_Y": "ידית שמאלית Y", + "RIGHT_STICK_X": "ידית ימנית X", + "RIGHT_STICK_Y": "ידית ימנית Y", + "LEFT_TRIGGER": "הדק שמאלי", + "RIGHT_TRIGGER": "הדק ימני", + "IS_DOWN": "לחוץ", + "PRESSED": "נלחץ", + "RELEASED": "שוחרר", + "EVENT_PRESSED": "בלחיצה", + "EVENT_RELEASED": "בשחרור", + "EVENT_CHANGED": "בשינוי" + }, "TOOLTIP": { "EVALUATE_BUT_IGNORE_RESULT": "מבצע את הבלוק המחובר ומתעלם מהתוצאה. מאפשר לקרוא לפונקציה מבלי להשתמש בערך שהיא מחזירה.", "NONE": "מחזיר כלום.", From c5a107cd565f23674f31801b1425a1a60f1104ce Mon Sep 17 00:00:00 2001 From: Alan Smith Date: Sat, 31 Jan 2026 14:55:59 -0500 Subject: [PATCH 12/12] Changed to be 6 gamepad ports --- src/fields/field_gamepads.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fields/field_gamepads.ts b/src/fields/field_gamepads.ts index 41a92568..22e74c09 100644 --- a/src/fields/field_gamepads.ts +++ b/src/fields/field_gamepads.ts @@ -24,7 +24,7 @@ import * as Blockly from 'blockly/core'; import { createFieldNumberDropdown } from './field_number_dropdown'; const MIN_GAMEPAD_PORT = 0; -const MAX_GAMEPAD_PORT = 7; +const MAX_GAMEPAD_PORT = 5; export const PORT_FIELD_NAME = 'GAMEPAD_PORT'; export const BUTTON_FIELD_NAME = 'GAMEPAD_BUTTON';