Skip to content

Commit dd1cd0b

Browse files
authored
Merge pull request #37 from lizlooney/methods_liz
Create the block for the init method when creating a new OpMode.
2 parents 29909ba + 4cfdfe4 commit dd1cd0b

File tree

478 files changed

+3496
-1060
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

478 files changed

+3496
-1060
lines changed

LICENSE.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2025 FIRST and other WPILib contributors
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* Neither the name of FIRST, WPILib, nor the names of other WPILib
12+
contributors may be used to endorse or promote products derived from
13+
this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY FIRST AND OTHER WPILIB CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR
18+
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR
19+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

src/App.tsx

Lines changed: 102 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ import * as commonStorage from './storage/common_storage';
5757
import * as ChangeFramework from './blocks/utils/change_framework'
5858
import {mutatorOpenListener} from './blocks/mrc_class_method_def'
5959

60-
import {create as createOpMode} from './modules/mrc_module_opmode'
61-
6260

6361
type NewWorkspaceNameModalProps = {
6462
isOpen: boolean;
@@ -253,9 +251,11 @@ const App: React.FC = () => {
253251
const [newOpModeNameModalInitialValue, setNewOpModeNameModalInitialValue] = useState('');
254252
const [newOpModeNameModalIsOpen, setNewOpModeNameModalIsOpen] = useState(false);
255253
const [toolboxSettingsModalIsOpen, setToolboxSettingsModalIsOpen] = useState(false);
256-
const [openAskToSave, setOpenAskToSave] = useState(false);
257-
const afterAskToSaveOk = useRef<() => void>(() => {});
258-
const [askToSaveSaving, setAskToSaveSaving] = useState(false);
254+
const [popconfirmTitle, setPopconfirmTitle] = useState('');
255+
const [popconfirmDescription, setPopconfirmDescription] = useState('');
256+
const [openPopconfirm, setOpenPopconfirm] = useState(false);
257+
const afterPopconfirmOk = useRef<() => void>(() => {});
258+
const [popconfirmLoading, setPopconfirmLoading] = useState(false);
259259

260260
const ignoreEffect = () => {
261261
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
@@ -470,7 +470,7 @@ const App: React.FC = () => {
470470
blocklyWorkspace.addChangeListener(mutatorOpenListener);
471471

472472
// Show generated python code.
473-
blocklyWorkspace.addChangeListener(handleBlocksChanged);
473+
blocklyWorkspace.addChangeListener(handleBlocksChanged);
474474
}
475475

476476
blocksEditor.current = new editor.Editor(blocklyWorkspace);
@@ -498,9 +498,9 @@ const App: React.FC = () => {
498498
setGeneratedCode(code);
499499
};
500500

501-
const handleAskToSaveOk = () => {
502-
const callback = afterAskToSaveOk.current;
503-
afterAskToSaveOk.current = () => {};
501+
const handlePopconfirmOk = () => {
502+
const callback = afterPopconfirmOk.current;
503+
afterPopconfirmOk.current = () => {};
504504
callback();
505505
};
506506

@@ -516,18 +516,20 @@ const App: React.FC = () => {
516516
}
517517

518518
// Show a bubble confirmation box to ask the user if they want to save the blocks.
519-
// Set the function to be executed if the user clicks "ok".
520-
afterAskToSaveOk.current = () => {
521-
setAskToSaveSaving(true);
519+
setPopconfirmTitle('Blocks have been modified!');
520+
setPopconfirmDescription('Press ok to save and continue');
521+
// Set the function to be executed if the user clicks 'ok'.
522+
afterPopconfirmOk.current = () => {
523+
setPopconfirmLoading(true);
522524
saveModule((success) => {
523-
setOpenAskToSave(false);
524-
setAskToSaveSaving(false);
525+
setOpenPopconfirm(false);
526+
setPopconfirmLoading(false);
525527
if (success) {
526528
callback();
527529
}
528530
});
529531
};
530-
setOpenAskToSave(true);
532+
setOpenPopconfirm(true);
531533
}
532534
};
533535

@@ -548,9 +550,9 @@ const App: React.FC = () => {
548550
};
549551

550552
const handleNewWorkspaceNameOk = (newWorkspaceName: string) => {
551-
const newWorkspacePath = commonStorage.makeModulePath(newWorkspaceName, newWorkspaceName);
553+
const newWorkspacePath = commonStorage.makeWorkspacePath(newWorkspaceName);
552554
if (newWorkspaceNameModalPurpose === 'NewWorkspace') {
553-
const workspaceContent = commonStorage.newModuleContent(commonStorage.MODULE_TYPE_WORKSPACE);
555+
const workspaceContent = commonStorage.newWorkspaceContent(newWorkspaceName);
554556
storage.createModule(
555557
commonStorage.MODULE_TYPE_WORKSPACE, newWorkspacePath, workspaceContent,
556558
(success: boolean, errorMessage: string) => {
@@ -606,7 +608,7 @@ const App: React.FC = () => {
606608
// Provide a callback so the NewOpModeNameModal will know what the current
607609
// workspace name is.
608610
const getCurrentWorkspaceName = (): string => {
609-
return (currentModule) ? currentModule.workspaceName : '';
611+
return currentModule ? currentModule.workspaceName : '';
610612
};
611613

612614
// Provide a callback so the NewOpModeNameModal will know what the existing
@@ -627,7 +629,7 @@ const App: React.FC = () => {
627629
const handleNewOpModeNameOk = (workspaceName: string, newOpModeName: string) => {
628630
const newOpModePath = commonStorage.makeModulePath(workspaceName, newOpModeName);
629631
if (newOpModeNameModalPurpose === 'NewOpMode') {
630-
const opModeContent = commonStorage.newModuleContent(commonStorage.MODULE_TYPE_OPMODE);
632+
const opModeContent = commonStorage.newOpModeContent(workspaceName, newOpModeName);
631633
storage.createModule(
632634
commonStorage.MODULE_TYPE_OPMODE, newOpModePath, opModeContent,
633635
(success: boolean, errorMessage: string) => {
@@ -640,14 +642,7 @@ const App: React.FC = () => {
640642
setAlertErrorMessage('Failed to create a new OpMode: ' + errorMessage);
641643
setAlertErrorVisible(true);
642644
}
643-
});
644-
// TODO: This needs to be changed based off the type
645-
if (blocklyComponent.current){
646-
const blocklyWorkspace = blocklyComponent.current.getBlocklyWorkspace();
647-
if(blocklyComponent){
648-
createOpMode(blocklyWorkspace);
649-
}
650-
}
645+
});
651646
} else if (newOpModeNameModalPurpose === 'RenameOpMode') {
652647
const workspaceName = commonStorage.getWorkspaceName(currentModulePath);
653648
const oldOpModeName = commonStorage.getModuleName(currentModulePath);
@@ -729,62 +724,80 @@ const App: React.FC = () => {
729724
if (currentModule.moduleType == commonStorage.MODULE_TYPE_WORKSPACE) {
730725
// This is a workspace.
731726
setNewWorkspaceNameModalPurpose('CopyWorkspace');
732-
setNewWorkspaceNameModalInitialValue(currentModule.workspaceName + "_copy");
727+
setNewWorkspaceNameModalInitialValue(currentModule.workspaceName + '_copy');
733728
setNewWorkspaceNameModalIsOpen(true);
734729
} else if (currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
735730
// This is an OpMode.
736731
setNewOpModeNameModalPurpose('CopyOpMode');
737-
setNewOpModeNameModalInitialValue(currentModule.moduleName + "_copy");
732+
setNewOpModeNameModalInitialValue(currentModule.moduleName + '_copy');
738733
setNewOpModeNameModalIsOpen(true);
739734
}
740735
});
741736
};
742737

743738
const handleDeleteClicked = () => {
744-
checkIfBlocksWereModified(() => {
745-
if (!currentModule) {
746-
return;
747-
}
748-
if (currentModule.moduleType == commonStorage.MODULE_TYPE_WORKSPACE) {
749-
// This is a workspace.
750-
// Before deleting it, select another workspace, if there is one.
751-
const workspaceNameToDelete = currentModule.workspaceName;
752-
let foundAnotherWorkspace = false;
753-
for (const workspace of modules) {
754-
if (workspace.workspaceName !== workspaceNameToDelete) {
755-
setCurrentModulePath(workspace.modulePath);
756-
foundAnotherWorkspace = true;
757-
break;
758-
}
759-
}
760-
if (!foundAnotherWorkspace) {
761-
setCurrentModulePath('');
739+
if (!currentModule) {
740+
return;
741+
}
742+
// Show a bubble confirmation box to ask the user if they are sure.
743+
setPopconfirmTitle('Are you sure?');
744+
if (currentModule.moduleType == commonStorage.MODULE_TYPE_WORKSPACE) {
745+
setPopconfirmDescription('Press ok to delete this Workspace');
746+
} else if (currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
747+
setPopconfirmDescription('Press ok to delete this OpMode');
748+
} else if (currentModule.moduleType == commonStorage.MODULE_TYPE_MECHANISM) {
749+
// TODO: delete the mechanism.
750+
return;
751+
}
752+
// Set the function to be executed if the user clicks 'ok'.
753+
afterPopconfirmOk.current = () => {
754+
setOpenPopconfirm(false);
755+
checkIfBlocksWereModified(() => {
756+
if (!currentModule) {
757+
return;
762758
}
763-
storage.deleteWorkspace(workspaceNameToDelete,
764-
(success: boolean, errorMessage: string) => {
765-
if (success) {
766-
setTriggerListModules(!triggerListModules);
767-
} else if (errorMessage) {
768-
setAlertErrorMessage('Failed to rename the Workspace: ' + errorMessage);
769-
setAlertErrorVisible(true);
759+
if (currentModule.moduleType == commonStorage.MODULE_TYPE_WORKSPACE) {
760+
// This is a workspace.
761+
// Before deleting it, select another workspace, if there is one.
762+
const workspaceNameToDelete = currentModule.workspaceName;
763+
let foundAnotherWorkspace = false;
764+
for (const workspace of modules) {
765+
if (workspace.workspaceName !== workspaceNameToDelete) {
766+
setCurrentModulePath(workspace.modulePath);
767+
foundAnotherWorkspace = true;
768+
break;
770769
}
771-
});
772-
} else if (currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
773-
// This is an OpMode.
774-
const modulePathToDelete = currentModulePath;
775-
const workspacePath = commonStorage.makeWorkspacePath(currentModule.workspaceName);
776-
setCurrentModulePath(workspacePath);
777-
storage.deleteOpMode(modulePathToDelete,
778-
(success: boolean, errorMessage: string) => {
779-
if (success) {
780-
setTriggerListModules(!triggerListModules);
781-
} else if (errorMessage) {
782-
setAlertErrorMessage('Failed to rename the Workspace: ' + errorMessage);
783-
setAlertErrorVisible(true);
784-
}
785-
});
786-
}
787-
});
770+
}
771+
if (!foundAnotherWorkspace) {
772+
setCurrentModulePath('');
773+
}
774+
storage.deleteWorkspace(workspaceNameToDelete,
775+
(success: boolean, errorMessage: string) => {
776+
if (success) {
777+
setTriggerListModules(!triggerListModules);
778+
} else if (errorMessage) {
779+
setAlertErrorMessage('Failed to delete the Workspace: ' + errorMessage);
780+
setAlertErrorVisible(true);
781+
}
782+
});
783+
} else if (currentModule.moduleType == commonStorage.MODULE_TYPE_OPMODE) {
784+
// This is an OpMode.
785+
const modulePathToDelete = currentModulePath;
786+
const workspacePath = commonStorage.makeWorkspacePath(currentModule.workspaceName);
787+
setCurrentModulePath(workspacePath);
788+
storage.deleteOpMode(modulePathToDelete,
789+
(success: boolean, errorMessage: string) => {
790+
if (success) {
791+
setTriggerListModules(!triggerListModules);
792+
} else if (errorMessage) {
793+
setAlertErrorMessage('Failed to delete the OpMode: ' + errorMessage);
794+
setAlertErrorVisible(true);
795+
}
796+
});
797+
}
798+
});
799+
};
800+
setOpenPopconfirm(true);
788801
};
789802

790803
const handleUploadClicked = () => {
@@ -901,9 +914,11 @@ const App: React.FC = () => {
901914
>
902915
<Flex vertical gap="small">
903916
<Space>
904-
<Tooltip title="New Workspace">
917+
<Tooltip title="New Workspace"
918+
placement="bottomRight"
919+
>
905920
<Button
906-
icon={<FolderAddOutlined />}
921+
icon={<FolderAddOutlined />}
907922
size="small"
908923
onClick={handleNewWorkspaceClicked}
909924
style={{ color: 'white' }}
@@ -912,7 +927,7 @@ const App: React.FC = () => {
912927
</Tooltip>
913928
<Tooltip title="New OpMode">
914929
<Button
915-
icon={<FileAddOutlined />}
930+
icon={<FileAddOutlined />}
916931
size="small"
917932
disabled={!currentModulePath}
918933
onClick={handleNewOpModeClicked}
@@ -922,7 +937,7 @@ const App: React.FC = () => {
922937
</Tooltip>
923938
<Tooltip title="Save">
924939
<Button
925-
icon={<SaveOutlined />}
940+
icon={<SaveOutlined />}
926941
size="small"
927942
disabled={!currentModulePath}
928943
onClick={handleSaveClicked}
@@ -932,9 +947,11 @@ const App: React.FC = () => {
932947
</Tooltip>
933948
</Space>
934949
<Space>
935-
<Tooltip title={renameTooltip}>
950+
<Tooltip title={renameTooltip}
951+
placement="topRight"
952+
>
936953
<Button
937-
icon={<EditOutlined />}
954+
icon={<EditOutlined />}
938955
size="small"
939956
disabled={!currentModulePath}
940957
onClick={handleRenameClicked}
@@ -944,7 +961,7 @@ const App: React.FC = () => {
944961
</Tooltip>
945962
<Tooltip title={copyTooltip}>
946963
<Button
947-
icon={<CopyOutlined />}
964+
icon={<CopyOutlined />}
948965
size="small"
949966
disabled={!currentModulePath}
950967
onClick={handleCopyClicked}
@@ -954,7 +971,7 @@ const App: React.FC = () => {
954971
</Tooltip>
955972
<Tooltip title={deleteTooltip}>
956973
<Button
957-
icon={<DeleteOutlined />}
974+
icon={<DeleteOutlined />}
958975
size="small"
959976
disabled={!currentModulePath}
960977
onClick={handleDeleteClicked}
@@ -1011,12 +1028,12 @@ const App: React.FC = () => {
10111028
</Tooltip>
10121029
</Space>
10131030
<Popconfirm
1014-
title="Blocks have been modified!"
1015-
description="Press ok to save and continue"
1016-
open={openAskToSave}
1017-
onConfirm={handleAskToSaveOk}
1018-
okButtonProps={{ loading: askToSaveSaving }}
1019-
onCancel={() => setOpenAskToSave(false)}
1031+
title={popconfirmTitle}
1032+
description={popconfirmDescription}
1033+
open={openPopconfirm}
1034+
onConfirm={handlePopconfirmOk}
1035+
okButtonProps={{ loading: popconfirmLoading }}
1036+
onCancel={() => setOpenPopconfirm(false)}
10201037
>
10211038
<Flex
10221039
style={{

src/blocks/generated/class_hal.AddressableLEDData.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export function initialize() {
1212
}
1313

1414
export function getToolboxCategory(subcategories: toolboxItems.Category[] = []): toolboxItems.Category {
15+
16+
// There are 7 blocks.
1517
const contents: toolboxItems.ContentsType[] = [
1618
{"kind": "block", "type": "mrc_get_python_variable", "extraState": {"varKind": "instance", "moduleOrClassName": "hal.AddressableLEDData", "varType": "int", "importModule": "", "selfLabel": "addressableLEDData", "selfType": "hal.AddressableLEDData"}, "fields": {"MODULE_OR_CLASS": "hal.AddressableLEDData", "VAR": "b"}, "inputs": {"SELF": {"block": {"type": "variables_get", "fields": {"VAR": {"name": "myAddressableLEDData"}}}}}},
1719
{"kind": "block", "type": "mrc_set_python_variable", "extraState": {"varKind": "instance", "moduleOrClassName": "hal.AddressableLEDData", "varType": "int", "importModule": "", "selfLabel": "addressableLEDData", "selfType": "hal.AddressableLEDData"}, "fields": {"MODULE_OR_CLASS": "hal.AddressableLEDData", "VAR": "b"}, "inputs": {"SELF": {"block": {"type": "variables_get", "fields": {"VAR": {"name": "myAddressableLEDData"}}}}}},
@@ -21,12 +23,15 @@ export function getToolboxCategory(subcategories: toolboxItems.Category[] = []):
2123
{"kind": "block", "type": "mrc_set_python_variable", "extraState": {"varKind": "instance", "moduleOrClassName": "hal.AddressableLEDData", "varType": "int", "importModule": "", "selfLabel": "addressableLEDData", "selfType": "hal.AddressableLEDData"}, "fields": {"MODULE_OR_CLASS": "hal.AddressableLEDData", "VAR": "r"}, "inputs": {"SELF": {"block": {"type": "variables_get", "fields": {"VAR": {"name": "myAddressableLEDData"}}}}}},
2224
{"kind": "block", "type": "variables_set", "fields": {"VAR": {"name": "myAddressableLEDData"}}, "inputs": {"VALUE": {"block": {"kind": "block", "type": "mrc_call_python_function", "extraState": {"functionKind": "constructor", "returnType": "hal._wpiHal.AddressableLEDData", "args": [], "tooltip": "", "importModule": "hal"}, "fields": {"MODULE_OR_CLASS": "hal.AddressableLEDData"}}}}},
2325
];
26+
2427
contents.push(...subcategories);
28+
2529
const category: toolboxItems.PythonClassCategory = {
2630
kind: "category",
27-
className: "hal.AddressableLEDData",
2831
name: "AddressableLEDData",
29-
contents: contents,
32+
contents: contents,
33+
className: "hal.AddressableLEDData",
3034
};
35+
3136
return category;
3237
}

0 commit comments

Comments
 (0)