diff --git a/Binaries/Output/Release_Windows/res/ribbon_default/function32.png b/Binaries/Output/Release_Windows/res/ribbon_default/function32.png new file mode 100644 index 000000000000..536a12cc5144 Binary files /dev/null and b/Binaries/Output/Release_Windows/res/ribbon_default/function32.png differ diff --git a/Core/GDCore/Extensions/Metadata/ParameterMetadataTools.cpp b/Core/GDCore/Extensions/Metadata/ParameterMetadataTools.cpp index 579cb8639097..9b305943be85 100644 --- a/Core/GDCore/Extensions/Metadata/ParameterMetadataTools.cpp +++ b/Core/GDCore/Extensions/Metadata/ParameterMetadataTools.cpp @@ -15,6 +15,7 @@ void ParameterMetadataTools::ParametersToObjectsContainer( gd::Project& project, const std::vector& parameters, gd::ObjectsContainer& outputObjectsContainer) { + outputObjectsContainer.GetObjects().clear(); for (std::size_t i = 0; i < parameters.size(); ++i) { const auto& parameter = parameters[i]; if (parameter.GetName().empty()) continue; diff --git a/newIDE/app/flow-typed/libGD.js b/newIDE/app/flow-typed/libGD.js index 6f749c56488e..c028dfaa2a7b 100644 --- a/newIDE/app/flow-typed/libGD.js +++ b/newIDE/app/flow-typed/libGD.js @@ -23,6 +23,7 @@ declare type gdObject = EmscriptenObject; declare type gdObjectGroup = EmscriptenObject; declare type gdResourcesManager = EmscriptenObject; declare type gdEventsList = EmscriptenObject; +declare type gdEventsFunction = EmscriptenObject; declare type gdInstruction = EmscriptenObject; declare type gdInstructionMetadata = EmscriptenObject; @@ -44,6 +45,8 @@ declare type gdDirection = EmscriptenObject; declare type gdAnimation = EmscriptenObject; declare type gdPoint = EmscriptenObject; +declare type gdEventsFunctionsExtension = EmscriptenObject; + declare type gdVectorEventsSearchResult = EmscriptenObject; declare type gdMapStringPropertyDescriptor = EmscriptenObject; diff --git a/newIDE/app/src/Debugger/DebuggerContent.js b/newIDE/app/src/Debugger/DebuggerContent.js index f1b80fb219cd..487b6fb2b881 100644 --- a/newIDE/app/src/Debugger/DebuggerContent.js +++ b/newIDE/app/src/Debugger/DebuggerContent.js @@ -192,7 +192,6 @@ export default class DebuggerContent extends React.Component { second: 'profiler', splitPercentage: 65, }} - initialSplitPercentage={32} /> ); } diff --git a/newIDE/app/src/EventsFunctionEditor/ParametersEditor.js b/newIDE/app/src/EventsFunctionEditor/ParametersEditor.js new file mode 100644 index 000000000000..b342f7639de3 --- /dev/null +++ b/newIDE/app/src/EventsFunctionEditor/ParametersEditor.js @@ -0,0 +1,91 @@ +// @flow +import * as React from 'react'; +import TextField from 'material-ui/TextField'; +import { Column, Line } from '../UI/Grid'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; +import { mapVector } from '../Utils/MapFor'; +import { FlatButton } from 'material-ui'; +const gd = global.gd; + +type Props = {| + eventsFunction: gdEventsFunction, + onParametersUpdated: () => void, +|}; + +type State = {||}; + +const styles = { + list: { + overflowY: 'scroll', + }, +}; + +export default class EventsFunctionEditor extends React.Component< + Props, + State +> { + _addParameter = () => { + const { eventsFunction } = this.props; + const parameters = eventsFunction.getParameters(); + + const newParameter = new gd.ParameterMetadata(); + newParameter.setType('objectList'); + parameters.push_back(newParameter); + newParameter.delete(); + this.forceUpdate(); + this.props.onParametersUpdated(); + }; + + render() { + const { eventsFunction } = this.props; + + const parameters = eventsFunction.getParameters(); + + return ( + +
+ {mapVector(parameters, (parameter: gdParameterMetadata, i: number) => { + return ( + + { + parameter.setName(text); + this.forceUpdate(); + }} + onBlur={() => { + this.props.onParametersUpdated(); + }} + /> + { + parameter.setType(value); + this.forceUpdate(); + this.props.onParametersUpdated(); + }} + > + + + + + { + parameter.setDescription(text); + this.forceUpdate(); + }} + /> + + ); + })} + +
+
+ ); + } +} diff --git a/newIDE/app/src/EventsFunctionEditor/index.js b/newIDE/app/src/EventsFunctionEditor/index.js new file mode 100644 index 000000000000..93c0b1eaf64c --- /dev/null +++ b/newIDE/app/src/EventsFunctionEditor/index.js @@ -0,0 +1,140 @@ +// @flow +import * as React from 'react'; +import EventsSheet from '../EventsSheet'; +import EditorMosaic, { MosaicWindow } from '../UI/EditorMosaic'; +import ParametersEditor from './ParametersEditor'; +import Paper from 'material-ui/Paper'; +const gd = global.gd; + +type Props = {| + project: gdProject, + eventsFunction: gdEventsFunction, +|}; + +type State = {||}; + +const styles = { + container: { flex: 1, display: 'flex' }, +}; + +export default class EventsFunctionEditor extends React.Component< + Props, + State +> { + editor: ?EventsSheet; + _editors: ?EditorMosaic; + _globalObjectsContainer: ?gdObjectsContainer; + _objectsContainer: ?gdObjectsContainer; + + componentDidMount() { + this._loadFrom(this.props); + } + + UNSAFE_componentWillReceiveProps(nextProps: Props) { + if ( + nextProps.eventsFunction !== this.props.eventsFunction || + nextProps.project !== this.props.project + ) { + this._loadFrom(nextProps); + } + } + + _loadFrom(props: Props) { + if (this._globalObjectsContainer) this._globalObjectsContainer.delete(); + this._globalObjectsContainer = new gd.ObjectsContainer(); + + if (this._objectsContainer) this._objectsContainer.delete(); + this._objectsContainer = new gd.ObjectsContainer(); + + this.updateFromParameters(props.project, props.eventsFunction); + } + + componentWillUnmount() { + if (this._globalObjectsContainer) this._globalObjectsContainer.delete(); + if (this._objectsContainer) this._objectsContainer.delete(); + } + + updateFromParameters = ( + project: gdProject, + eventsFunction: gdEventsFunction + ) => { + gd.ParameterMetadataTools.parametersToObjectsContainer( + project, + eventsFunction.getParameters(), + this._objectsContainer + ); + this.forceUpdate(); + }; + + updateToolbar() { + if (this.editor) this.editor.updateToolbar(); + } + + render() { + const { project, eventsFunction } = this.props; + + if (!this._globalObjectsContainer || !this._objectsContainer) return null; + + return ( + (this._editors = editors)} + editors={{ + parameters: ( + + + + this.updateFromParameters(project, eventsFunction)} + /> + + + ), + 'events-sheet': ( + (this.editor = editor)} + project={project} + layout={null} + globalObjectsContainer={this._globalObjectsContainer} + objectsContainer={this._objectsContainer} + events={eventsFunction.getEvents()} + showPreviewButton={false} + onPreview={options => { + /*TODO*/ + }} + showNetworkPreviewButton={false} + onOpenExternalEvents={() => { + /*TODO*/ + }} + onOpenLayout={() => { + /*TODO*/ + }} + resourceSources={[]} + onChooseResource={() => { + /*TODO*/ + return Promise.reject(new Error('Unimplemented')); + }} + resourceExternalEditors={[]} + setToolbar={() => { + /*TODO*/ + }} + updateToolbar={() => { + /*TODO*/ + }} + onOpenDebugger={() => { + /*TODO*/ + }} + onOpenSettings={() => {}} + /> + ), + }} + initialNodes={{ + direction: 'column', + first: 'parameters', + second: 'events-sheet', + splitPercentage: 25, + }} + /> + ); + } +} diff --git a/newIDE/app/src/EventsFunctionsExtensionsLoader/LocalEventsFunctionsExtensionsLoader.js b/newIDE/app/src/EventsFunctionsExtensionsLoader/LocalEventsFunctionsExtensionsLoader.js new file mode 100644 index 000000000000..0af2b128443d --- /dev/null +++ b/newIDE/app/src/EventsFunctionsExtensionsLoader/LocalEventsFunctionsExtensionsLoader.js @@ -0,0 +1,16 @@ +// @flow +import { generateEventsFunctionExtension } from "."; +const gd = global.gd; + +export const loadProjectEventsFunctionExtensions = (project: gdProject) => { + + //TODO + // const extension = generateEventsFunctionExtension(eventsFunctionsExtension, { + // project, + // getIncludeFileFor: () => "TODO.js", //TODO + // onFunctionGeneratedCode: () => {}, + // }); + // gd.JsPlatform.get().addNewExtension(extension); + // extension.delete(); +} + diff --git a/newIDE/app/src/EventsFunctionsExtensionsLoader/index.js b/newIDE/app/src/EventsFunctionsExtensionsLoader/index.js new file mode 100644 index 000000000000..86d6e1e66f35 --- /dev/null +++ b/newIDE/app/src/EventsFunctionsExtensionsLoader/index.js @@ -0,0 +1,70 @@ +// @flow +import { mapVector } from '../Utils/MapFor'; + +const gd = global.gd; + +export type EventsFunctionExtensionContext = {| + project: gdProject, + getIncludeFileFor: (functionName: string) => string, + onFunctionGeneratedCode: (functionName: string, code: string) => void, +|}; + +export const generateEventsFunctionExtension = ( + eventsFunctionsExtension: gdEventsFunctionsExtension, + context: EventsFunctionExtensionContext +) => { + const extension = new gd.PlatformExtension(); + + extension.setExtensionInformation( + eventsFunctionsExtension.getName(), + eventsFunctionsExtension.getFullName(), + eventsFunctionsExtension.getDescription(), + '', + '' //TODO + ); + + mapVector( + extension.getEventsFunctions(), + (eventsFunction: gdEventsFunction) => { + const isCondition = false; //TODO + const instruction = extension.addAction( + eventsFunction.getName(), + eventsFunction.getFullName(), + eventsFunction.getDescription(), + 'TODO: Sentence', + eventsFunction.getName(), + 'res/function.png', + 'res/function24.png' + ); + + mapVector( + eventsFunction.getParameters(), + (parameter: gdParameterMetadata) => { + instruction.addParameter( + parameter.getType(), + parameter.getDescription(), + parameter.getExtraInfo(), + parameter.isOptional() + ); + } + ); + + + const code = gd.EventsCodeGenerator.generateEventsFunctionCode( + context.project, + eventsFunction.getParameters(), + eventsFunction.getEvents(), + true //TODO + ); + context.onFunctionGeneratedCode(instruction.getName(), code); + + //TODO: code generation + instruction + .getCodeExtraInformation() + .setIncludeFile(context.getIncludeFileFor(instruction.getName())) + .setFunctionName('todoFunction'); //TODO: get namespace function + } + ); + + return extension; +}; diff --git a/newIDE/app/src/MainFrame/index.js b/newIDE/app/src/MainFrame/index.js index bb99547d3f69..fb698042c542 100644 --- a/newIDE/app/src/MainFrame/index.js +++ b/newIDE/app/src/MainFrame/index.js @@ -994,6 +994,7 @@ export default class MainFrame extends React.Component { { /> )} /> + + + } + initiallyOpen={false} + open={forceOpen} + primaryTogglesNestedList={true} + autoGenerateNestedIndicator={!forceOpen} + // nestedItems={filterProjectItemsList( + // enumerateExternalLayouts(project), + // searchText + // ) + // .map((externalLayout, i) => { + // const name = externalLayout.getName(); + // return ( + // this.props.onOpenExternalLayout(name)} + // onDelete={() => + // this.props.onDeleteExternalLayout(externalLayout)} + // onRename={newName => { + // this.props.onRenameExternalLayout(name, newName); + // this._onEditName(null, ''); + // }} + // onEditName={() => this._onEditName('external-layout', name)} + // onCopy={() => this._copyExternalLayout(externalLayout)} + // onCut={() => this._cutExternalLayout(externalLayout)} + // onPaste={() => this._pasteExternalLayout(i)} + // canPaste={() => + // Clipboard.has(EXTERNAL_LAYOUT_CLIPBOARD_KIND)} + // canMoveUp={i !== 0} + // onMoveUp={() => this._moveUpExternalLayout(i)} + // canMoveDown={i !== project.getExternalLayoutsCount() - 1} + // onMoveDown={() => this._moveDownExternalLayout(i)} + // /> + // ); + // }) + // .concat( + // + // )} + /> { // Empty layout const emptyLayout = project.insertNewLayout('EmptyLayout', 1); + // Events function + const testEventsFunction = new gd.EventsFunction(); + testEventsFunction.setName('MyTestFunction'); + + const parameter1 = new gd.ParameterMetadata(); + parameter1.setType('objectList'); + parameter1.setName('MyObjectWithoutType'); + parameter1.setDescription('The first object to be used'); + const parameter2 = new gd.ParameterMetadata(); + parameter2.setType('expression'); + parameter2.setName('MyNumber'); + parameter2.setDescription('Some number'); + const parameter3 = new gd.ParameterMetadata(); + parameter3.setType('string'); + parameter3.setName('MyString'); + parameter3.setDescription('Some string'); + const parameter4 = new gd.ParameterMetadata(); + parameter4.setType('objectList'); + parameter4.setName('MySpriteObject'); + parameter4.setDescription('The second object to be used, a sprite'); + parameter4.setExtraInfo('Sprite'); + testEventsFunction.getParameters().push_back(parameter1); + testEventsFunction.getParameters().push_back(parameter2); + testEventsFunction.getParameters().push_back(parameter3); + testEventsFunction.getParameters().push_back(parameter4); + + testEventsFunction.getEvents().insertNewEvent( + project, + 'BuiltinCommonInstructions::Standard', + 0 + ); + return { project, shapePainterObject, @@ -300,5 +332,6 @@ export const makeTestProject = gd => { testExternalEvents1, testExternalEvents2, emptyLayout, + testEventsFunction, }; }; diff --git a/newIDE/app/src/stories/index.js b/newIDE/app/src/stories/index.js index 8aaa5b40c311..4dc69b856fda 100644 --- a/newIDE/app/src/stories/index.js +++ b/newIDE/app/src/stories/index.js @@ -103,6 +103,7 @@ import BackgroundText from '../UI/BackgroundText'; import i18n from '../UI/i18n'; import ObjectField from '../EventsSheet/InstructionEditor/ParameterFields/ObjectField'; import { getInitialSelection } from '../EventsSheet/SelectionHandler'; +import EventsFunctionEditor from '../EventsFunctionEditor'; const gd = global.gd; const { @@ -119,6 +120,7 @@ const { spriteObjectWithBehaviors, group2, emptyLayout, + testEventsFunction, } = makeTestProject(gd); const Placeholder = () =>
Placeholder component
; @@ -872,7 +874,9 @@ storiesOf('EventsTree', module) onInstructionClick={action('instruction click')} onInstructionDoubleClick={action('instruction double click')} onInstructionContextMenu={action('instruction context menu')} - onInstructionsListContextMenu={action('instruction list context menu')} + onInstructionsListContextMenu={action( + 'instruction list context menu' + )} onParameterClick={action('parameter click')} onEventClick={action('event click')} onEventContextMenu={action('event context menu')} @@ -1551,3 +1555,16 @@ storiesOf('ResourcesList', module) /> )); + +storiesOf('EventsFunctionEditor', module) + .addDecorator(muiDecorator) + .add('default', () => ( + +
+ +
+
+ ));