Add editor for properties of events based behaviors

This commit is contained in:
Florian Rival
2019-06-11 18:23:04 +01:00
committed by Florian Rival
parent 52489c3cc9
commit b1f7660ffa
11 changed files with 562 additions and 171 deletions

View File

@@ -261,6 +261,21 @@
}
],
"events": [
{
"disabled": false,
"folded": false,
"type": "BuiltinCommonInstructions::Comment",
"color": {
"b": 109,
"g": 230,
"r": 255,
"textB": 0,
"textG": 0,
"textR": 0
},
"comment": "Should display Hello World 42!",
"comment2": ""
},
{
"disabled": false,
"folded": false,
@@ -711,4 +726,4 @@
],
"externalLayouts": [],
"externalSourceFiles": []
}
}

View File

@@ -59,6 +59,9 @@ declare type gdEventsFunctionsExtension = gdEventsFunctionsContainer & gdEmscrip
declare type gdVectorEventsSearchResult = gdEmscriptenObject;
declare type gdMapStringPropertyDescriptor = gdEmscriptenObject;
declare type gdPropertyDescriptor = gdEmscriptenObject;
declare type gdNamedPropertyDescriptor = gdEmscriptenObject;
declare type gdNamedPropertyDescriptorsList = gdEmscriptenObject;
declare type gdEventsContext = gdEmscriptenObject;

View File

@@ -1,6 +1,6 @@
// @flow
import { Trans } from '@lingui/macro';
import React from 'react';
import * as React from 'react';
import FlatButton from 'material-ui/FlatButton';
import Dialog from '../UI/Dialog';
import EventsBasedBehaviorEditor from './index';
@@ -13,41 +13,56 @@ type Props = {|
eventsBasedBehavior: gdEventsBasedBehavior,
|};
export default function EventsBasedBehaviorEditorDialog({
onApply,
eventsBasedBehavior,
eventsFunctionsExtension,
project,
}: Props) {
return (
<Dialog
noMargin
secondaryActions={[
<HelpButton
key="help"
helpPagePath="/behaviors/events-based-behaviors"
/>,
]}
actions={[
<FlatButton
label={<Trans>Apply</Trans>}
primary
keyboardFocused
onClick={onApply}
key={'Apply'}
/>,
]}
modal
open
onRequestClose={onApply}
autoScrollBodyContent
title={<Trans>Edit the behavior</Trans>}
>
<EventsBasedBehaviorEditor
project={project}
eventsFunctionsExtension={eventsFunctionsExtension}
eventsBasedBehavior={eventsBasedBehavior}
/>
</Dialog>
);
export default class EventsBasedBehaviorEditorDialog extends React.Component<
Props,
{||}
> {
render() {
const {
onApply,
eventsBasedBehavior,
eventsFunctionsExtension,
project,
} = this.props;
return (
<Dialog
noMargin
secondaryActions={[
<HelpButton
key="help"
helpPagePath="/behaviors/events-based-behaviors"
/>,
]}
actions={[
<FlatButton
label={<Trans>Apply</Trans>}
primary
keyboardFocused
onClick={onApply}
key={'Apply'}
/>,
]}
modal
open
onRequestClose={onApply}
autoScrollBodyContent
title={<Trans>Edit the behavior</Trans>}
>
<EventsBasedBehaviorEditor
project={project}
eventsFunctionsExtension={eventsFunctionsExtension}
eventsBasedBehavior={eventsBasedBehavior}
onTabChanged={
() =>
this.forceUpdate() /*Force update to ensure dialog is properly positioned*/
}
onPropertiesUpdated={
() =>
this.forceUpdate() /*Force update to ensure dialog is properly positioned*/
}
/>
</Dialog>
);
}
}

View File

@@ -0,0 +1,287 @@
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import { I18n } from '@lingui/react';
import { type I18n as I18nType } from '@lingui/core';
import * as React from 'react';
import { Column, Line } from '../UI/Grid';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import { mapVector } from '../Utils/MapFor';
import RaisedButton from 'material-ui/RaisedButton';
import IconButton from 'material-ui/IconButton';
import EmptyMessage from '../UI/EmptyMessage';
import IconMenu from '../UI/Menu/IconMenu';
import MoreVertIcon from 'material-ui/svg-icons/navigation/more-vert';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import MiniToolbar from '../UI/MiniToolbar';
import { showWarningBox } from '../UI/Messages/MessageBox';
import newNameGenerator from '../Utils/NewNameGenerator';
const gd = global.gd;
type Props = {|
project: gdProject,
eventsBasedBehavior: gdEventsBasedBehavior,
onPropertiesUpdated: () => void,
|};
const styles = {
propertiesContainer: {
flex: 1,
},
};
const validatePropertyName = (
i18n: I18nType,
properties: gdNamedPropertyDescriptorsList,
newName: string
) => {
if (!newName) {
showWarningBox(i18n._(t`The name of a property cannot be empty.`));
return false;
}
if (newName === 'name' || newName === 'type') {
showWarningBox(
i18n._(
t`The name of a property cannot be "name" or "type", as they are used by GDevelop internally.`
)
);
return false;
}
if (properties.has(newName)) {
showWarningBox(
i18n._(
t`This name is already used by another property. Choose a unique name for each property.`
)
);
return false;
}
if (!gd.Project.validateObjectName(newName)) {
showWarningBox(
i18n._(
t`This name contains forbidden characters: please only use alphanumeric characters (0-9, a-z) and underscores in your parameter name.`
)
);
return false;
}
return true;
};
export default class EventsBasedBehaviorPropertiesEditor extends React.Component<
Props,
{||}
> {
_addProperty = () => {
const { eventsBasedBehavior } = this.props;
const properties = eventsBasedBehavior.getPropertyDescriptors();
const newName = newNameGenerator('Property', name => properties.has(name));
const property = properties.insertNew(newName, properties.getCount());
property.setType('Number');
this.forceUpdate();
this.props.onPropertiesUpdated();
};
_removeProperty = (name: string) => {
const { eventsBasedBehavior } = this.props;
const properties = eventsBasedBehavior.getPropertyDescriptors();
properties.remove(name);
this.forceUpdate();
this.props.onPropertiesUpdated();
};
_moveProperty = (oldIndex: number, newIndex: number) => {
const { eventsBasedBehavior } = this.props;
const properties = eventsBasedBehavior.getPropertyDescriptors();
properties.move(oldIndex, newIndex);
this.forceUpdate();
this.props.onPropertiesUpdated();
};
render() {
const { eventsBasedBehavior } = this.props;
const properties = eventsBasedBehavior.getPropertyDescriptors();
return (
<I18n>
{({ i18n }) => (
<Column noMargin>
<Line noMargin>
<div style={styles.propertiesContainer}>
{mapVector(
properties,
(property: gdNamedPropertyDescriptor, i: number) => (
<React.Fragment key={i}>
<MiniToolbar>
<Column expand noMargin>
<SemiControlledTextField
commitOnBlur
hintText={<Trans>Enter the property name</Trans>}
value={property.getName()}
onChange={newName => {
if (newName === property.getName()) return;
if (
!validatePropertyName(i18n, properties, newName)
)
return;
// TODO: refactor with WholeProjectRefactorer?
property.setName(newName);
this.forceUpdate();
this.props.onPropertiesUpdated();
}}
fullWidth
/>
</Column>
<IconMenu
iconButtonElement={
<IconButton>
<MoreVertIcon />
</IconButton>
}
buildMenuTemplate={() => [
{
label: i18n._(t`Delete`),
click: () =>
this._removeProperty(property.getName()),
},
{ type: 'separator' },
{
label: i18n._(t`Move up`),
click: () => this._moveProperty(i, i - 1),
enabled: i - 1 >= 0,
},
{
label: i18n._(t`Move down`),
click: () => this._moveProperty(i, i + 1),
enabled: i + 1 < properties.getCount(),
},
]}
/>
</MiniToolbar>
<Line expand noMargin>
<Column expand>
<SelectField
floatingLabelText={<Trans>Type</Trans>}
value={property.getType()}
onChange={(e, i, value) => {
property.setType(value);
this.forceUpdate();
this.props.onPropertiesUpdated();
}}
fullWidth
>
<MenuItem
value="Number"
primaryText={<Trans>Number</Trans>}
/>
<MenuItem
value="String"
primaryText={<Trans>String</Trans>}
/>
<MenuItem
value="Boolean"
primaryText={<Trans>Boolean (checkbox)</Trans>}
/>
</SelectField>
</Column>
<Column expand>
{(property.getType() === 'String' ||
property.getType() === 'Number') && (
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Default value</Trans>}
hintText={
property.getType() === 'Number' ? '123' : 'ABC'
}
value={property.getValue()}
onChange={newValue => {
property.setValue(newValue);
this.forceUpdate();
this.props.onPropertiesUpdated();
}}
fullWidth
/>
)}
{property.getType() === 'Boolean' && (
<SelectField
floatingLabelText={<Trans>Default value</Trans>}
value={
property.getValue() === 'true'
? 'true'
: 'false'
}
onChange={(e, i, value) => {
property.setValue(value);
this.forceUpdate();
this.props.onPropertiesUpdated();
}}
fullWidth
>
<MenuItem
value="true"
primaryText={<Trans>True (checked)</Trans>}
/>
<MenuItem
value="false"
primaryText={<Trans>False (not checked)</Trans>}
/>
</SelectField>
)}
</Column>
</Line>
<Line expand noMargin>
<Column expand>
<SemiControlledTextField
commitOnBlur
floatingLabelText={
<Trans>Label, shown in the editor</Trans>
}
hintText={
<Trans>
This should make the purpose of the property
easy to understand
</Trans>
}
floatingLabelFixed
value={property.getLabel()}
onChange={text => {
property.setLabel(text);
this.forceUpdate();
}}
fullWidth
/>
</Column>
</Line>
</React.Fragment>
)
)}
{properties.getCount() === 0 ? (
<EmptyMessage>
<Trans>
No properties for this behavior. Add one to store data
inside this behavior (for example: health, ammo, speed,
etc...)
</Trans>
</EmptyMessage>
) : null}
<Line justifyContent="center">
<RaisedButton
primary
label={<Trans>Add a property</Trans>}
onClick={this._addProperty}
/>
</Line>
</div>
</Line>
</Column>
)}
</I18n>
);
}
}

View File

@@ -6,20 +6,34 @@ import TextField from 'material-ui/TextField';
import { Column, Spacer } from '../UI/Grid';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import ObjectTypeSelector from '../ObjectTypeSelector';
import { Tabs, Tab } from 'material-ui/Tabs';
import DismissableAlertMessage from '../UI/DismissableAlertMessage';
import AlertMessage from '../UI/AlertMessage';
import EventsBasedBehaviorPropertiesEditor from './EventsBasedBehaviorPropertiesEditor';
const gd = global.gd;
type TabName = 'configuration' | 'properties';
type Props = {|
project: gdProject,
eventsFunctionsExtension: gdEventsFunctionsExtension,
eventsBasedBehavior: gdEventsBasedBehavior,
onPropertiesUpdated: () => void,
onTabChanged: () => void,
|};
type State = {|
currentTab: TabName,
|};
export default class EventsBasedBehaviorEditor extends React.Component<
Props,
{||}
State
> {
state = {
currentTab: 'configuration',
};
// An array containing all the object types that are using the behavior
_allObjectTypes: Array<string> = gd.WholeProjectRefactorer.getAllObjectTypesUsingEventsBasedBehavior(
this.props.project,
@@ -29,102 +43,124 @@ export default class EventsBasedBehaviorEditor extends React.Component<
.toNewVectorString()
.toJSArray();
_changeTab = (newTab: TabName) =>
this.setState(
{
currentTab: newTab,
},
() => this.props.onTabChanged()
);
render() {
const { currentTab } = this.state;
const { eventsBasedBehavior, project } = this.props;
return (
<Column>
<DismissableAlertMessage
identifier="events-based-behavior-explanation"
kind="info"
>
This is the configuration of your behavior. Make sure to choose a
proper internal name as it's hard to change it later. Enter a
description explaining what the behavior is doing to the object.
</DismissableAlertMessage>
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedBehavior.getName()}
disabled
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedBehavior.getFullName()}
onChange={text => {
eventsBasedBehavior.setFullName(text);
this.forceUpdate();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
floatingLabelFixed
hintText={
<Trans>
The description of the behavior should explain what the behavior
is doing to the object, and, briefly, how to use it.
</Trans>
}
value={eventsBasedBehavior.getDescription()}
onChange={text => {
eventsBasedBehavior.setDescription(text);
this.forceUpdate();
}}
multiLine
fullWidth
rows={3}
/>
<ObjectTypeSelector
floatingLabelText={
<Trans>Object on which this behavior can be used</Trans>
}
project={project}
value={eventsBasedBehavior.getObjectType()}
onChange={(objectType: string) => {
eventsBasedBehavior.setObjectType(objectType);
this.forceUpdate();
}}
allowedObjectTypes={
this._allObjectTypes.length === 0
? undefined /* Allow anything as the behavior is not used */
: this._allObjectTypes.length === 1
? [
'',
this._allObjectTypes[0],
] /* Allow only the type of the objects using the behavior */
: [
'',
] /* More than one type of object are using the behavior. Only "any object" can be used on this behavior */
}
/>
{this._allObjectTypes.length > 1 && (
<AlertMessage kind="info">
<Trans>
This behavior is being used by multiple types of objects. Thus,
you can't restrict its usage to any particular object type. All
the object types using this behavior are listed here:
{this._allObjectTypes.join(', ')}
</Trans>
</AlertMessage>
)}
{eventsBasedBehavior.getEventsFunctions().getEventsFunctionsCount() ===
0 && (
<DismissableAlertMessage
identifier="empty-events-based-behavior-explanation"
kind="info"
>
<Trans>
Once you're done, close this dialog and start adding some
functions to the behavior. Then, test the behavior by adding it to
an object in a scene.
</Trans>
</DismissableAlertMessage>
)}
<Spacer />
</Column>
<Tabs value={currentTab} onChange={this._changeTab}>
<Tab label={<Trans>Configuration</Trans>} value="configuration">
<Column>
<DismissableAlertMessage
identifier="events-based-behavior-explanation"
kind="info"
>
This is the configuration of your behavior. Make sure to choose a
proper internal name as it's hard to change it later. Enter a
description explaining what the behavior is doing to the object.
</DismissableAlertMessage>
<TextField
floatingLabelText={<Trans>Internal Name</Trans>}
value={eventsBasedBehavior.getName()}
disabled
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Name displayed in editor</Trans>}
value={eventsBasedBehavior.getFullName()}
onChange={text => {
eventsBasedBehavior.setFullName(text);
this.forceUpdate();
}}
fullWidth
/>
<SemiControlledTextField
commitOnBlur
floatingLabelText={<Trans>Description</Trans>}
floatingLabelFixed
hintText={
<Trans>
The description of the behavior should explain what the
behavior is doing to the object, and, briefly, how to use it.
</Trans>
}
value={eventsBasedBehavior.getDescription()}
onChange={text => {
eventsBasedBehavior.setDescription(text);
this.forceUpdate();
}}
multiLine
fullWidth
rows={3}
/>
<ObjectTypeSelector
floatingLabelText={
<Trans>Object on which this behavior can be used</Trans>
}
project={project}
value={eventsBasedBehavior.getObjectType()}
onChange={(objectType: string) => {
eventsBasedBehavior.setObjectType(objectType);
this.forceUpdate();
}}
allowedObjectTypes={
this._allObjectTypes.length === 0
? undefined /* Allow anything as the behavior is not used */
: this._allObjectTypes.length === 1
? [
'',
this._allObjectTypes[0],
] /* Allow only the type of the objects using the behavior */
: [
'',
] /* More than one type of object are using the behavior. Only "any object" can be used on this behavior */
}
/>
{this._allObjectTypes.length > 1 && (
<AlertMessage kind="info">
<Trans>
This behavior is being used by multiple types of objects.
Thus, you can't restrict its usage to any particular object
type. All the object types using this behavior are listed
here:
{this._allObjectTypes.join(', ')}
</Trans>
</AlertMessage>
)}
{eventsBasedBehavior
.getEventsFunctions()
.getEventsFunctionsCount() === 0 && (
<DismissableAlertMessage
identifier="empty-events-based-behavior-explanation"
kind="info"
>
<Trans>
Once you're done, close this dialog and start adding some
functions to the behavior. Then, test the behavior by adding
it to an object in a scene.
</Trans>
</DismissableAlertMessage>
)}
<Spacer />
</Column>
</Tab>
<Tab label={<Trans>Properties</Trans>} value="properties">
<EventsBasedBehaviorPropertiesEditor
project={project}
eventsBasedBehavior={eventsBasedBehavior}
onPropertiesUpdated={this.props.onPropertiesUpdated}
/>
</Tab>
</Tabs>
);
}
}

View File

@@ -42,6 +42,7 @@ type Props = {|
extensionName: string,
eventsFunction: gdEventsFunction
) => void,
onBehaviorEdited?: () => void,
initiallyFocusedFunctionName: ?string,
initiallyFocusedBehaviorName: ?string,
|};
@@ -349,24 +350,30 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
});
};
_editBehaviorProperties = (
editedEventsBasedBehavior: ?gdEventsBasedBehavior
) => {
this.setState(state => {
// If we're closing the properties of a behavior, ensure parameters
// are up-to-date in all event functions of the behavior (the object
// type might have changed).
if (state.editedEventsBasedBehavior && !editedEventsBasedBehavior) {
gd.WholeProjectRefactorer.ensureBehaviorEventsFunctionsProperParameters(
this.props.eventsFunctionsExtension,
state.editedEventsBasedBehavior
);
}
_editBehavior = (editedEventsBasedBehavior: ?gdEventsBasedBehavior) => {
this.setState(
state => {
// If we're closing the properties of a behavior, ensure parameters
// are up-to-date in all event functions of the behavior (the object
// type might have changed).
if (state.editedEventsBasedBehavior && !editedEventsBasedBehavior) {
gd.WholeProjectRefactorer.ensureBehaviorEventsFunctionsProperParameters(
this.props.eventsFunctionsExtension,
state.editedEventsBasedBehavior
);
}
return {
editedEventsBasedBehavior,
};
});
return {
editedEventsBasedBehavior,
};
},
() => {
// If we're closing the properties of a behavior, notify parent
// that a behavior was edited (to trigger reload of extensions)
if (!editedEventsBasedBehavior && this.props.onBehaviorEdited)
this.props.onBehaviorEdited();
}
);
};
render() {
@@ -556,9 +563,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
label={<Trans>Edit behavior properties</Trans>}
primary
onClick={() =>
this._editBehaviorProperties(
selectedEventsBasedBehavior
)
this._editBehavior(selectedEventsBasedBehavior)
}
/>
</Line>
@@ -597,7 +602,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
onRenameEventsBasedBehavior={this._makeRenameEventsBasedBehavior(
i18n
)}
onEditProperties={this._editBehaviorProperties}
onEditProperties={this._editBehavior}
/>
</MosaicWindow>
),
@@ -643,7 +648,7 @@ export default class EventsFunctionsExtensionEditor extends React.Component<
project={project}
eventsFunctionsExtension={eventsFunctionsExtension}
eventsBasedBehavior={editedEventsBasedBehavior}
onApply={() => this._editBehaviorProperties(null)}
onApply={() => this._editBehavior(null)}
/>
)}
</React.Fragment>

View File

@@ -301,13 +301,14 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
const setterName = gd.BehaviorCodeGenerator.getBehaviorPropertySetterName(
propertyName
);
const propertyLabel = property.getLabel() || propertyName;
if (propertyType === 'String' || propertyType === 'Choice') {
addObjectAndBehaviorParameters(
behaviorMetadata.addStrExpression(
propertyName,
property.getLabel(),
property.getLabel(),
propertyLabel,
propertyLabel,
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png'
)
@@ -318,8 +319,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedCondition(
propertyName,
property.getLabel(),
i18n._(t`Compare the content of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Compare the content of ${propertyLabel}`),
i18n._(t`Property ${propertyName} of _PARAM0_ is _PARAM2__PARAM3_`),
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png'
@@ -339,8 +340,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedAction(
propertyName,
property.getLabel(),
i18n._(t`Update the content of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Update the content of ${propertyLabel}`),
i18n._(
t`Do _PARAM2__PARAM3_ to property ${propertyName} of _PARAM0_`
),
@@ -358,8 +359,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addExpression(
propertyName,
property.getLabel(),
property.getLabel(),
propertyLabel,
propertyLabel,
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png'
)
@@ -370,8 +371,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedCondition(
propertyName,
property.getLabel(),
i18n._(t`Compare the value of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Compare the value of ${propertyLabel}`),
i18n._(t`Property ${propertyName} of _PARAM0_ is _PARAM2__PARAM3_`),
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png'
@@ -396,8 +397,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedAction(
propertyName,
property.getLabel(),
i18n._(t`Update the value of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Update the value of ${propertyLabel}`),
i18n._(
t`Do _PARAM2__PARAM3_ to property ${propertyName} of _PARAM0_`
),
@@ -415,8 +416,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedCondition(
propertyName,
property.getLabel(),
i18n._(t`Check the value of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Check the value of ${propertyLabel}`),
i18n._(t`Property ${propertyName} of _PARAM0_ is true`),
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png',
@@ -429,8 +430,8 @@ export const declareBehaviorPropertiesInstructionAndExpressions = (
addObjectAndBehaviorParameters(
behaviorMetadata.addScopedAction(
propertyName,
property.getLabel(),
i18n._(t`Update the value of ${property.getLabel()}`),
propertyLabel,
i18n._(t`Update the value of ${propertyLabel}`),
i18n._(t`Set property ${propertyName} of _PARAM0_ to _PARAM2_`),
eventsBasedBehavior.getFullName() || eventsBasedBehavior.getName(),
'res/function.png'

View File

@@ -232,7 +232,7 @@ function generateBehavior(
declareBehaviorPropertiesInstructionAndExpressions(
options.i18n,
behaviorMetadata,
eventsBasedBehavior,
eventsBasedBehavior
);
// Declare all the behavior functions

View File

@@ -33,6 +33,13 @@ export default class EventsFunctionsExtensionEditorWrapper extends BaseEditor {
}
}
_onBehaviorEdited = () => {
// Immediately trigger the reload/regeneration of extensions
// as a change in the properties of a behavior can create changes
// in actions/conditions/expressions to manipulate these properties.
this.props.onLoadEventsFunctionsExtensions();
};
getEventsFunctionsExtension(): ?gdEventsFunctionsExtension {
const { project, eventsFunctionsExtensionName } = this.props;
if (
@@ -76,6 +83,7 @@ export default class EventsFunctionsExtensionEditorWrapper extends BaseEditor {
onCreateEventsFunction={this.props.onCreateEventsFunction}
initiallyFocusedFunctionName={this.props.initiallyFocusedFunctionName}
initiallyFocusedBehaviorName={this.props.initiallyFocusedBehaviorName}
onBehaviorEdited={this._onBehaviorEdited}
ref={editor => (this.editor = editor)}
/>
</div>

View File

@@ -387,6 +387,26 @@ export const makeTestProject = gd => {
);
testEventsBasedBehavior.setObjectType('Sprite');
// Add some properties
testEventsBasedBehavior
.getPropertyDescriptors()
.insertNew('NumberProperty', 0)
.setType('Number')
.setValue('123')
.setLabel('My number property');
testEventsBasedBehavior
.getPropertyDescriptors()
.insertNew('StringProperty', 1)
.setType('String')
.setValue('Hello World')
.setLabel('My string property');
testEventsBasedBehavior
.getPropertyDescriptors()
.insertNew('BooleanProperty', 2)
.setType('Boolean')
.setValue('true')
.setLabel('My boolean property');
// Add a function
const testBehaviorEventsFunction = testEventsBasedBehavior
.getEventsFunctions()

View File

@@ -2086,6 +2086,7 @@ storiesOf('EventsFunctionsExtensionEditor/OptionsEditorDialog', module)
));
storiesOf('EventsBasedBehaviorEditor', module)
.addDecorator(paperDecorator)
.addDecorator(muiDecorator)
.addDecorator(i18nProviderDecorator)
.add('default', () => (