diff --git a/src/blocks/mrc_none.ts b/src/blocks/mrc_none.ts new file mode 100644 index 00000000..7ec05135 --- /dev/null +++ b/src/blocks/mrc_none.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright 2025 Google 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 Block for Python's None keyword. + * @author lizlooney@google.com (Liz Looney) + */ + + +import * as Blockly from 'blockly'; +import { MRC_STYLE_NONE } from '../themes/styles' +import { Order, PythonGenerator } from 'blockly/python'; + +export const BLOCK_NAME = 'mrc_none'; + +export function setup() { + Blockly.Blocks[BLOCK_NAME] = { + init: function() { + this.setOutput(true); // no type for None + this.appendDummyInput() + .appendField(Blockly.Msg.NONE); + this.setStyle(MRC_STYLE_NONE); + this.setTooltip(Blockly.Msg.NONE_TOOLTIP); + }, + }; +} + +export function pythonFromBlock( + _block: Blockly.Block, + _generator: PythonGenerator, +): [string, Order] { + return ['None', Order.ATOMIC]; +} + +export function createNoneShadowValue(): any { + return { + 'shadow': { + 'type': 'mrc_none', + }, + } +} diff --git a/src/blocks/setup_custom_blocks.ts b/src/blocks/setup_custom_blocks.ts index ddc23917..352182f3 100644 --- a/src/blocks/setup_custom_blocks.ts +++ b/src/blocks/setup_custom_blocks.ts @@ -1,42 +1,44 @@ import * as CallPythonFunction from './mrc_call_python_function'; +import * as ClassMethodDef from './mrc_class_method_def'; +import * as Component from './mrc_component'; +import * as Event from './mrc_event'; +import * as EventHandler from './mrc_event_handler'; +import * as GetParameter from './mrc_get_parameter'; import * as GetPythonEnumValue from './mrc_get_python_enum_value'; import * as GetPythonVariable from './mrc_get_python_variable'; import * as ListAddItem from './mrc_list_add_item'; import * as MathMinMax from './mrc_math_min_max'; +import * as Mechanism from './mrc_mechanism'; +import * as MechanismComponentHolder from './mrc_mechanism_component_holder'; import * as MiscComment from './mrc_misc_comment'; import * as MiscEvaluateButIgnoreResult from './mrc_misc_evaluate_but_ignore_result'; -import * as SetPythonVariable from './mrc_set_python_variable'; -import * as ClassMethodDef from './mrc_class_method_def'; -import * as Mechanism from './mrc_mechanism'; -import * as Component from './mrc_component'; -import * as MechanismContainerHolder from './mrc_mechanism_component_holder'; -import * as Port from './mrc_port'; +import * as None from './mrc_none'; import * as OpModeDetails from './mrc_opmode_details'; -import * as Event from './mrc_event'; -import * as GetParameter from './mrc_get_parameter'; import * as ParameterMutator from './mrc_param_container' -import * as EventHandler from './mrc_event_handler'; +import * as Port from './mrc_port'; +import * as SetPythonVariable from './mrc_set_python_variable'; const customBlocks = [ CallPythonFunction, ClassMethodDef, + Component, + Event, + EventHandler, + GetParameter, GetPythonEnumValue, GetPythonVariable, ListAddItem, MathMinMax, + Mechanism, + MechanismComponentHolder, MiscComment, MiscEvaluateButIgnoreResult, - SetPythonVariable, - Mechanism, - Component, - MechanismContainerHolder, - Port, + None, OpModeDetails, - Event, - GetParameter, ParameterMutator, - EventHandler, + Port, + SetPythonVariable, ]; export const setup = function(forBlock: any) { diff --git a/src/blocks/tokens.ts b/src/blocks/tokens.ts index 9e43b349..45946aac 100644 --- a/src/blocks/tokens.ts +++ b/src/blocks/tokens.ts @@ -42,6 +42,8 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg { EVALUATE_BUT_IGNORE_RESULT: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT'), EVALUATE_BUT_IGNORE_RESULT_TOOLTIP: t('BLOCKLY.TOOLTIP.EVALUATE_BUT_IGNORE_RESULT'), + NONE: t('BLOCKLY.NONE'), + NONE_TOOLTIP: t('BLOCKLY.TOOLTIP.NONE'), AUTO: t('BLOCKLY.AUTO'), TELEOP: t('BLOCKLY.TELEOP'), TEST: t('BLOCKLY.TEST'), diff --git a/src/blocks/utils/value.ts b/src/blocks/utils/value.ts index 0a4283e5..f321d034 100644 --- a/src/blocks/utils/value.ts +++ b/src/blocks/utils/value.ts @@ -21,6 +21,7 @@ import { getAlias } from './python'; import * as variable from './variable'; +import { createNoneShadowValue } from '../mrc_none'; export function valueForFunctionArgInput(argType: string, argDefaultValue: string): any { @@ -50,9 +51,7 @@ export function valueForFunctionArgInput(argType: string, argDefaultValue: strin break; case 'str': if (argDefaultValue === 'None') { - // TODO(lizlooney): Make a block for python None - // In RobotPy function hal.report(), the arg named feature has a default value of None. - return null; + return createNoneShadowValue() } // If argDefaultValue is surrounded by single or double quotes, it's a literal string. if (argDefaultValue.startsWith("'") && argDefaultValue.endsWith("'") || diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index faacb516..728edbf7 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -49,6 +49,7 @@ "COMPONENTS": "Components", "EVENTS": "Events", "EVALUATE_BUT_IGNORE_RESULT": "evaluate but ignore result", + "NONE": "None", "TYPE": "Type", "ENABLED": "Enabled", "DISPLAY_NAME": "Display Name", @@ -57,6 +58,7 @@ "NO_MECHANISM_CONTENTS": "No Mechanism Contents", "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.", "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.", diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 94924c8e..25d9c420 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -50,6 +50,7 @@ "COMPONENTS": "Componentes", "EVENTS": "Eventos", "EVALUATE_BUT_IGNORE_RESULT": "evaluar pero ignorar resultado", + "NONE": "None", "TYPE": "Tipo", "ENABLED": "Habilitado", "DISPLAY_NAME": "Nombre a Mostrar", @@ -58,6 +59,7 @@ "NO_MECHANISM_CONTENTS": "Sin Contenido de Mecanismo", "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.", "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.", @@ -82,4 +84,4 @@ "TEST": "Prueba" } } -} \ No newline at end of file +} diff --git a/src/i18n/locales/he/translation.json b/src/i18n/locales/he/translation.json index 461d42bf..9b0acff2 100644 --- a/src/i18n/locales/he/translation.json +++ b/src/i18n/locales/he/translation.json @@ -49,6 +49,7 @@ "COMPONENTS": "רכיבים", "EVENTS": "אירועים", "EVALUATE_BUT_IGNORE_RESULT": "הערך אך התעלם מהתוצאה", + "NONE": "אַף לֹא אֶחָד", "TYPE": "סוג", "ENABLED": "מופעל", "DISPLAY_NAME": "שם תצוגה", @@ -57,6 +58,7 @@ "NO_MECHANISM_CONTENTS": "אין תוכן מנגנון", "TOOLTIP":{ "EVALUATE_BUT_IGNORE_RESULT": "מריץ את הבלוק המחובר ומתעלם מהתוצאה. מאפשר לך לקרוא לפונקציה ולהתעלם מערך ההחזרה.", + "NONE": "החזרות אין.", "OPMODE_TYPE": "איזה סוג של מצב פעולה זה", "OPMODE_ENABLED": "האם מצב הפעולה מוצג בתחנת הנהג", "OPMODE_NAME": "השם המוצג בתחנת הנהג. אם ריק ישתמש בשם הכיתה.", diff --git a/src/themes/styles.ts b/src/themes/styles.ts index 55da8136..c8beecd4 100644 --- a/src/themes/styles.ts +++ b/src/themes/styles.ts @@ -26,6 +26,7 @@ export const MRC_STYLE_ENUM = 'mrc_style_enum'; export const MRC_STYLE_VARIABLES = 'mrc_style_variables'; export const MRC_STYLE_COMMENTS = 'mrc_style_comments'; export const MRC_STYLE_MISC = 'mrc_style_misc'; +export const MRC_STYLE_NONE = 'mrc_style_none'; export const MRC_STYLE_CLASS_BLOCKS = 'mrc_style_class_blocks'; export const MRC_CATEGORY_STYLE_METHODS = 'mrc_category_style_methods'; export const MRC_STYLE_MECHANISMS = 'mrc_style_mechanisms'; @@ -38,6 +39,7 @@ export const MRC_CATEGORY_STYLE_COMPONENTS = 'mrc_category_style_components'; export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme { const procedureStyle = theme.blockStyles['procedure_blocks']; const variableStyle = theme.blockStyles['variable_blocks']; + const logicStyle = theme.blockStyles['logic_blocks']; theme.setBlockStyle(MRC_STYLE_FUNCTIONS, { ...procedureStyle @@ -74,12 +76,14 @@ export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme { colourTertiary: "#494984", hat: "" }); + theme.setBlockStyle(MRC_STYLE_NONE, { + ...logicStyle + }); theme.setBlockStyle(MRC_STYLE_MECHANISMS, { colourPrimary: "#5b61a5", colourSecondary: "#dedfed", colourTertiary: "#494e84", hat: "" - }); theme.setBlockStyle(MRC_STYLE_COMPONENTS, { colourPrimary: "#5b80a5", diff --git a/src/toolbox/logic_category.ts b/src/toolbox/logic_category.ts index 34996df6..1b0bf5c0 100644 --- a/src/toolbox/logic_category.ts +++ b/src/toolbox/logic_category.ts @@ -43,7 +43,7 @@ export const getCategory = () => ( }, { kind: 'block', - type: 'logic_null', + type: 'mrc_none', }, { kind: 'block',