Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import * as CustomBlocks from './blocks/setup_custom_blocks';

import { initialize as initializePythonBlocks } from './blocks/utils/python';
import * as ChangeFramework from './blocks/utils/change_framework'
import { registerToolboxButton } from './blocks/mrc_event_handler'
import { mutatorOpenListener } from './blocks/mrc_param_container'
import { TOOLBOX_UPDATE_EVENT } from './blocks/mrc_mechanism_component_holder';
import { antdThemeFromString } from './reactComponents/ThemeModal';
Expand Down Expand Up @@ -432,6 +433,9 @@ const AppContent: React.FC<AppContentProps> = ({ project, setProject }): React.J
ChangeFramework.setup(newWorkspace);
newWorkspace.addChangeListener(mutatorOpenListener);
newWorkspace.addChangeListener(handleBlocksChanged);

registerToolboxButton(newWorkspace, messageApi);

generatorContext.current = createGeneratorContext();

if (currentModule) {
Expand Down
52 changes: 49 additions & 3 deletions src/blocks/mrc_event_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import * as Blockly from 'blockly';
import {Order} from 'blockly/python';

import type { MessageInstance } from 'antd/es/message/interface';
import { Editor } from '../editor/editor';
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
import { createFieldFlydown } from '../fields/field_flydown';
Expand All @@ -34,6 +35,9 @@ import * as storageModuleContent from '../storage/module_content';

export const BLOCK_NAME = 'mrc_event_handler';

const BUTTON_CALLBACK_KEY = 'EVENT_HANDLER_ALREADY_ON_WORKSPACE';
const BUTTON_STYLE_PREFIX = 'eventHandlerButtonStyle_';

const FIELD_SENDER = 'SENDER';
const FIELD_EVENT_NAME = 'EVENT_NAME';

Expand All @@ -48,6 +52,7 @@ export interface Parameter {
type?: string;
}

const SENDER_VALUE_ROBOT = 'robot';
const WARNING_ID_EVENT_CHANGED = 'event changed';

export type EventHandlerBlock = Blockly.Block & EventHandlerMixin & Blockly.BlockSvg;
Expand Down Expand Up @@ -404,10 +409,22 @@ function generateRegisterEventHandler(
// Functions used for creating blocks for the toolbox.

export function addRobotEventHandlerBlocks(
workspace: Blockly.WorkspaceSvg,
events: storageModuleContent.Event[],
eventHandlerBlocks: EventHandlerBlock[],
contents: toolboxItems.ContentsType[]) {
// Collect the ids of events for which there is already an event handler.
const eventIds: string[] = [];
eventHandlerBlocks.forEach(eventHandlerBlock => {
eventIds.push(eventHandlerBlock.getEventId());
});
events.forEach(event => {
contents.push(createRobotEventHandlerBlock(event));
if (eventIds.includes(event.eventId)) {
// If there is already an event handler for this event, put a button in the toolbox.
contents.push(createButton(workspace, SENDER_VALUE_ROBOT, event.name));
} else {
contents.push(createRobotEventHandlerBlock(event));
}
});
}

Expand All @@ -425,21 +442,44 @@ function createRobotEventHandlerBlock(
});
});
const fields: {[key: string]: any} = {};
fields[FIELD_SENDER] = 'robot';
fields[FIELD_SENDER] = SENDER_VALUE_ROBOT;
fields[FIELD_EVENT_NAME] = event.name;
const inputs: {[key: string]: any} = {};
return new toolboxItems.Block(BLOCK_NAME, extraState, fields, Object.keys(inputs).length ? inputs : null);
}

export function addMechanismEventHandlerBlocks(
workspace: Blockly.WorkspaceSvg,
mechanismInRobot: storageModuleContent.MechanismInRobot,
events: storageModuleContent.Event[],
eventHandlerBlocks: EventHandlerBlock[],
contents: toolboxItems.ContentsType[]) {
// Collect the ids of events for which there is already an event handler.
const eventIds: string[] = [];
eventHandlerBlocks.forEach(eventHandlerBlock => {
eventIds.push(eventHandlerBlock.getEventId());
});
events.forEach(event => {
contents.push(createMechanismEventHandlerBlock(mechanismInRobot, event));
if (eventIds.includes(event.eventId)) {
// If there is already an event handler for this event, put a button in the toolbox.
contents.push(createButton(workspace, mechanismInRobot.name, event.name));
} else {
contents.push(createMechanismEventHandlerBlock(mechanismInRobot, event));
}
});
}

function createButton(
workspace: Blockly.WorkspaceSvg, senderName: string, eventName: string): toolboxItems.Button {
// Use non-breakable spaces so it looks more like an event handler block.
const spaces = '\u00A0\u00A0';
const text = workspace.RTL
? (spaces + eventName + spaces + senderName + spaces + Blockly.Msg.WHEN + spaces)
: (spaces + Blockly.Msg.WHEN + spaces + senderName + spaces + eventName + spaces);
const style = BUTTON_STYLE_PREFIX + workspace.getTheme().name;
return new toolboxItems.Button(text, BUTTON_CALLBACK_KEY, style);
}

function createMechanismEventHandlerBlock(
mechanismInRobot: storageModuleContent.MechanismInRobot,
event: storageModuleContent.Event): toolboxItems.Block {
Expand Down Expand Up @@ -502,3 +542,9 @@ export function renameMechanismName(workspace: Blockly.Workspace, mechanismId: s
(block as EventHandlerBlock).renameMechanismName(mechanismId, newName);
});
}

export function registerToolboxButton(workspace: Blockly.WorkspaceSvg, messageApi: MessageInstance) {
workspace.registerButtonCallback(BUTTON_CALLBACK_KEY, function (_button) {
messageApi.info(Blockly.Msg.EVENT_HANDLER_ALREADY_ON_WORKSPACE);
});
}
2 changes: 2 additions & 0 deletions src/blocks/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
PARAMETER: t('BLOCKLY.PARAMETER'),
PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK:
t('BLOCKLY.PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK'),
EVENT_HANDLER_ALREADY_ON_WORKSPACE:
t('BLOCKLY.EVENT_HANDLER_ALREADY_ON_WORKSPACE'),
MECHANISMS: t('MECHANISMS'),
OPMODES: t('OPMODES'),
COMPONENTS: t('BLOCKLY.COMPONENTS'),
Expand Down
1 change: 1 addition & 0 deletions src/editor/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ export class Editor {
private clearBlocklyWorkspace() {
if (this.bindedOnChange) {
this.blocklyWorkspace.removeChangeListener(this.bindedOnChange);
this.bindedOnChange = null;
}
this.blocklyWorkspace.hideChaff();
this.blocklyWorkspace.clear();
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"WHEN": "when",
"PARAMETER": "parameter",
"PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK": "Parameters can only go in their method's block",
"EVENT_HANDLER_ALREADY_ON_WORKSPACE": "This event handler is already on the workspace.",
"COMPONENTS": "Components",
"EVENTS": "Events",
"EVALUATE_BUT_IGNORE_RESULT": "evaluate but ignore result",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"WHEN": "cuando",
"PARAMETER": "parámetro",
"PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK": "Los parámetros solo pueden ir en el bloque de su método",
"EVENT_HANDLER_ALREADY_ON_WORKSPACE": "Este controlador de eventos ya está en el área de trabajo.",
"COMPONENTS": "Componentes",
"EVENTS": "Eventos",
"EVALUATE_BUT_IGNORE_RESULT": "evaluar pero ignorar resultado",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/he/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"WHEN": "כאשר",
"PARAMETER": "פרמטר",
"PARAMETERS_CAN_ONLY_GO_IN_THEIR_METHODS_BLOCK": "פרמטרים יכולים להיכנס רק בבלוק השיטה שלהם",
"EVENT_HANDLER_ALREADY_ON_WORKSPACE": "מטפל אירועים זה כבר נמצא בסביבת העבודה.",
"COMPONENTS": "רכיבים",
"EVENTS": "אירועים",
"EVALUATE_BUT_IGNORE_RESULT": "הערך אך התעלם מהתוצאה",
Expand Down
30 changes: 27 additions & 3 deletions src/themes/mrc_themes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DeuteranopiaTheme from '@blockly/theme-deuteranopia';
import TritanopiaTheme from '@blockly/theme-tritanopia';
import HighContrastTheme from '@blockly/theme-highcontrast';

import { add_mrc_styles } from './styles';
import { add_mrc_styles, MRC_STYLE_EVENT_HANDLER } from './styles';

export const DARK_THEME_NAME = 'mrc_theme_dark';
export const LIGHT_THEME_NAME = 'mrc_theme_light';
Expand Down Expand Up @@ -39,7 +39,7 @@ const create_theme = function (name: string, base: Blockly.Theme, dark: boolean
};

const create_themes = function (): Blockly.Theme[] {
return [
const themes: Blockly.Theme[] = [
create_theme(DARK_THEME_NAME, Blockly.Themes.Classic, true),
create_theme(LIGHT_THEME_NAME, Blockly.Themes.Classic),
create_theme(DEUTERANOPIA_THEME_NAME, DeuteranopiaTheme),
Expand All @@ -49,6 +49,30 @@ const create_themes = function (): Blockly.Theme[] {
create_theme(TRITANOPIA_DARK_THEME_NAME, TritanopiaTheme, true),
create_theme(HIGHCONTRAST_DARK_THEME_NAME, HighContrastTheme, true),
];

// Create CSS classes for event handler buttons, which are placed in the toolbox when an event
// handler already exists on the workspace.
let cssClasses = '';
themes.forEach(theme => {
let fill = theme.blockStyles[MRC_STYLE_EVENT_HANDLER].colourPrimary;
if (!fill.startsWith('#')) {
try {
fill = Blockly.utils.colour.hueToHex(Number(fill));
} catch (e) {
console.error(
'Unable to determine event handler block color for theme ' + theme.name);
}
}
cssClasses +=
'.eventHandlerButtonStyle_' + theme.name + ' {\n' +
' fill: ' + fill + ';\n' +
'}\n';
});
const styleElement = document.createElement('style');
styleElement.innerHTML = cssClasses;
document.head.appendChild(styleElement);

return themes;
};

export const themes = create_themes();
export const themes = create_themes();
21 changes: 3 additions & 18 deletions src/toolbox/event_handlers_category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,8 @@ class EventHandlersCategory {

// Get the list of events from the robot.
const eventsFromRobot = editor.getEventsFromRobot();
// Remove events if there is already a corresponding handler in the workspace.
const eventHandlerBlocks = editor.getRobotEventHandlersAlreadyInWorkspace();
const eventIds: string[] = [];
eventHandlerBlocks.forEach(eventHandlerBlock => {
eventIds.push(eventHandlerBlock.getEventId());
});
const eventsToShow = eventsFromRobot.filter(event => {
return !eventIds.includes(event.eventId);
});
addRobotEventHandlerBlocks(eventsToShow, contents);
addRobotEventHandlerBlocks(workspace, eventsFromRobot, eventHandlerBlocks, contents);

const toolboxInfo = {
contents: contents,
Expand All @@ -129,17 +121,10 @@ class EventHandlersCategory {
const mechanism = editor.getMechanism(this.mechanismInRobot);
if (mechanism) {
const eventsFromMechanism = editor.getEventsFromMechanism(mechanism);
// Remove events if there is already a corresponding handler in the workspace.
const eventHandlerBlocks = editor.getMechanismEventHandlersAlreadyInWorkspace(
this.mechanismInRobot);
const eventIds: string[] = [];
eventHandlerBlocks.forEach(eventHandlerBlock => {
eventIds.push(eventHandlerBlock.getEventId());
});
const eventsToShow = eventsFromMechanism.filter(event => {
return !eventIds.includes(event.eventId);
});
addMechanismEventHandlerBlocks(this.mechanismInRobot, eventsToShow, contents);
addMechanismEventHandlerBlocks(
workspace, this.mechanismInRobot, eventsFromMechanism, eventHandlerBlocks, contents);
if (contents.length === 0) {
const label : toolboxItems.Label = new toolboxItems.Label(Blockly.Msg['NO_MECHANISM_CONTENTS']);
contents.push(label);
Expand Down
38 changes: 17 additions & 21 deletions src/toolbox/hardware_category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,29 @@ import {
addInstanceMechanismBlocks } from '../blocks/mrc_call_python_function';
import { Editor } from '../editor/editor';

export function getHardwareCategory(
editor: Editor): toolboxItems.Category {

export function getHardwareCategory(editor: Editor): toolboxItems.Category {
const moduleType = editor.getCurrentModuleType();
switch (moduleType) {
case storageModule.ModuleType.ROBOT:
return {
kind: 'category',
name: Blockly.Msg['MRC_CATEGORY_HARDWARE'],
contents: [
getRobotMechanismsCategory(editor),
getComponentsCategory(editor, moduleType),
],
};
return new toolboxItems.Category(
Blockly.Msg['MRC_CATEGORY_HARDWARE'],
[
getRobotMechanismsCategory(editor),
getComponentsCategory(editor, moduleType),
],
toolboxItems.ExpandedState.EXPANDED);
case storageModule.ModuleType.MECHANISM:
return getComponentsCategory(editor, moduleType);
case storageModule.ModuleType.OPMODE:
return {
kind: 'category',
name: Blockly.Msg['MRC_CATEGORY_ROBOT'],
contents: [
getRobotMechanismsCategory(editor),
getRobotComponentsCategory(editor),
getRobotMethodsCategory(editor),
getRobotEventHandlersCategory(editor),
],
};
return new toolboxItems.Category(
Blockly.Msg['MRC_CATEGORY_ROBOT'],
[
getRobotMechanismsCategory(editor),
getRobotComponentsCategory(editor),
getRobotMethodsCategory(editor),
getRobotEventHandlersCategory(editor),
],
toolboxItems.ExpandedState.EXPANDED);
}
throw new Error('moduleType has unexpected value: ' + moduleType);
}
Expand Down
Loading