diff --git a/src/blocks/mrc_display_add_data.ts b/src/blocks/mrc_display_add_data.ts new file mode 100644 index 00000000..974c58c8 --- /dev/null +++ b/src/blocks/mrc_display_add_data.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 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("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/mrc_gamepad_analog.ts b/src/blocks/mrc_gamepad_analog.ts new file mode 100644 index 00000000..5be35af3 --- /dev/null +++ b/src/blocks/mrc_gamepad_analog.ts @@ -0,0 +1,56 @@ +/** + * @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 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 { PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; +import * as Gamepad from '../fields/field_gamepads'; + +export const BLOCK_NAME = 'mrc_gamepad_analog'; + + +export const setup = function() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.appendDummyInput() + .appendField(Gamepad.createTitleField()) + .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) + .appendField(Gamepad.createAnalogAxisField(), Gamepad.AXIS_FIELD_NAME); + this.setOutput(true, 'Number'); + this.setStyle(MRC_STYLE_DRIVER_STATION); + }, + }; +}; + +export const pythonFromBlock = function( + block: Blockly.Block, + _: PythonGenerator, +) { + 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 new file mode 100644 index 00000000..f2eb077f --- /dev/null +++ b/src/blocks/mrc_gamepad_boolean.ts @@ -0,0 +1,57 @@ +/** + * @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 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 { PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_DRIVER_STATION } from '../themes/styles'; +import * as Gamepad from '../fields/field_gamepads'; + +export const BLOCK_NAME = 'mrc_gamepad_boolean'; + +export const setup = function() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.appendDummyInput() + .appendField(Gamepad.createTitleField()) + .appendField(Gamepad.createPortField(), Gamepad.PORT_FIELD_NAME) + .appendField(Gamepad.createButtonField(), Gamepad.BUTTON_FIELD_NAME) + this.appendDummyInput() + .appendField(Gamepad.createActionField(), Gamepad.ACTION_FIELD_NAME); + this.setOutput(true, 'Boolean'); + this.setStyle(MRC_STYLE_DRIVER_STATION); + }, + }; +}; + +export const pythonFromBlock = function( + block: Blockly.Block, + _: PythonGenerator, +) { + 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 new file mode 100644 index 00000000..84cd859b --- /dev/null +++ b/src/blocks/mrc_gamepad_boolean_event.ts @@ -0,0 +1,62 @@ +/** + * @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 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 { PythonGenerator } from 'blockly/python'; +import { MRC_STYLE_EVENT_HANDLER, MRC_STYLE_EVENTS } from '../themes/styles'; +import * as Gamepad from '../fields/field_gamepads'; + +export const BLOCK_NAME = 'mrc_gamepad_boolean_event'; + +export const setup = function () { + Blockly.Blocks[BLOCK_NAME] = { + init: function () { + this.appendDummyInput() + .appendField(Gamepad.createEventField(), Gamepad.EVENT_FIELD_NAME); + this.appendDummyInput() + .appendField(Gamepad.createTitleField()) + .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(''); + this.setPreviousStatement(false); + this.setNextStatement(false); + + this.setStyle(MRC_STYLE_EVENTS); + }, + }; +}; + +export const pythonFromBlock = function ( + block: Blockly.Block, + _: PythonGenerator, +) { + // TODO: Update this when the actual driver station display class is implemented + 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/blocks/setup_custom_blocks.ts b/src/blocks/setup_custom_blocks.ts index fecdb7bc..df57bfe2 100644 --- a/src/blocks/setup_custom_blocks.ts +++ b/src/blocks/setup_custom_blocks.ts @@ -21,6 +21,10 @@ 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'; +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, @@ -44,7 +48,11 @@ const customBlocks = [ SetPythonVariable, Steps, StepContainer, - JumpToStep + JumpToStep, + DisplayAddData, + GamepadBoolean, + GamepadAnalog, + GamepadBooleanEvent, ]; export const setup = function(forBlock: any) { diff --git a/src/blocks/tokens.ts b/src/blocks/tokens.ts index 711ec4d8..4acf9cea 100644 --- a/src/blocks/tokens.ts +++ b/src/blocks/tokens.ts @@ -87,6 +87,37 @@ 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'), + 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'), @@ -135,6 +166,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/fields/field_gamepads.ts b/src/fields/field_gamepads.ts new file mode 100644 index 00000000..22e74c09 --- /dev/null +++ b/src/fields/field_gamepads.ts @@ -0,0 +1,128 @@ +/** + * @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 = 5; + +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: () => 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: () => 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: () => 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: () => 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(Blockly.Msg['GAMEPAD']); +} + +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 diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index b1eff889..5623e028 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", @@ -176,9 +178,40 @@ "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.", + "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 +254,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..435b15bd 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", @@ -177,9 +179,40 @@ "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.", + "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 +255,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..eba0da4d 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -166,7 +166,8 @@ "FIRE": "הפעל", "GET": "קבל", "SET": "הגדר", - "TO": "ל", + "TO": "ל", "CAPTION": "כיתוב", + "VALUE": "ערך", "STEPS": "צעדים", "REPEAT_UNTIL": "לחזור על כך עד", "JUMP_TO": "לקפוץ אל", @@ -176,9 +177,40 @@ "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": "מחזיר כלום.", + "DISPLAY_ADD_DATA": "הוסף נתונים עם כיתוב לתצוגת תחנת הנהג.", "OPMODE_TYPE": "איזה סוג אופמוד זה", "OPMODE_ENABLED": "האם האופמוד מוצג באפליקציית ה־Driver Station", "OPMODE_NAME": "השם שמוצג באפליקציית ה־Driver Station. אם ריק, ישתמש בשם הכיתה.", @@ -221,6 +253,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..2d05de3a --- /dev/null +++ b/src/toolbox/driver_station_category.ts @@ -0,0 +1,38 @@ +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'; +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( + 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'], + [ + 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 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));