mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Add support for private functions to extensions made in the editor (#2305)
* Actions/conditions/expressions made with events in the editor can be marked "private": they can be used inside the extension (or inside the behavior), but can't be used in the scene events sheet. * This is useful to share some "internal" logic that you don't want to make available from the events sheet.
This commit is contained in:
@@ -17,6 +17,7 @@ void EventsFunction::SerializeTo(SerializerElement& element) const {
|
||||
element.SetAttribute("fullName", fullName);
|
||||
element.SetAttribute("description", description);
|
||||
element.SetAttribute("sentence", sentence);
|
||||
element.SetBoolAttribute("private", isPrivate);
|
||||
events.SerializeTo(element.AddChild("events"));
|
||||
|
||||
gd::String functionTypeStr = "Action";
|
||||
@@ -43,6 +44,7 @@ void EventsFunction::UnserializeFrom(gd::Project& project,
|
||||
fullName = element.GetStringAttribute("fullName");
|
||||
description = element.GetStringAttribute("description");
|
||||
sentence = element.GetStringAttribute("sentence");
|
||||
isPrivate = element.GetBoolAttribute("private");
|
||||
events.UnserializeFrom(project, element.GetChild("events"));
|
||||
|
||||
gd::String functionTypeStr = element.GetStringAttribute("functionType");
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define GDCORE_EVENTSFUNCTION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "GDCore/Events/EventsList.h"
|
||||
#include "GDCore/Project/ObjectGroupsContainer.h"
|
||||
#include "GDCore/String.h"
|
||||
@@ -116,6 +117,19 @@ class GD_CORE_API EventsFunction {
|
||||
*/
|
||||
FunctionType GetFunctionType() const { return functionType; };
|
||||
|
||||
/**
|
||||
* \brief Returns true if the function is private.
|
||||
*/
|
||||
bool IsPrivate() { return isPrivate; }
|
||||
|
||||
/**
|
||||
* \brief Sets the privateness of the function.
|
||||
*/
|
||||
EventsFunction& SetPrivate(bool _isPrivate) {
|
||||
isPrivate = _isPrivate;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the events.
|
||||
*/
|
||||
@@ -128,7 +142,7 @@ class GD_CORE_API EventsFunction {
|
||||
|
||||
/**
|
||||
* \brief Return the parameters of the function.
|
||||
*
|
||||
*
|
||||
* \note During code/extension generation, new parameters are added
|
||||
* to the generated function, like "runtimeScene" and "eventsFunctionContext".
|
||||
* This should be transparent to the user.
|
||||
@@ -143,12 +157,14 @@ class GD_CORE_API EventsFunction {
|
||||
std::vector<gd::ParameterMetadata>& GetParameters() { return parameters; };
|
||||
|
||||
/**
|
||||
* \brief Return a reference to the object groups that can be used in the function.
|
||||
* \brief Return a reference to the object groups that can be used in the
|
||||
* function.
|
||||
*/
|
||||
ObjectGroupsContainer& GetObjectGroups() { return objectGroups; }
|
||||
|
||||
/**
|
||||
* \brief Return a const reference to the object groups that can be used in the function.
|
||||
* \brief Return a const reference to the object groups that can be used in
|
||||
* the function.
|
||||
*/
|
||||
const ObjectGroupsContainer& GetObjectGroups() const { return objectGroups; }
|
||||
|
||||
@@ -176,6 +192,7 @@ class GD_CORE_API EventsFunction {
|
||||
FunctionType functionType;
|
||||
std::vector<gd::ParameterMetadata> parameters;
|
||||
gd::ObjectGroupsContainer objectGroups;
|
||||
bool isPrivate = false;
|
||||
};
|
||||
|
||||
} // namespace gd
|
||||
|
@@ -1857,6 +1857,8 @@ interface EventsFunction {
|
||||
[Const, Ref] DOMString GetFullName();
|
||||
[Ref] EventsFunction SetSentence([Const] DOMString sentence);
|
||||
[Const, Ref] DOMString GetSentence();
|
||||
[Ref] EventsFunction SetPrivate(boolean isPrivate);
|
||||
boolean IsPrivate();
|
||||
[Ref] EventsFunction SetFunctionType(EventsFunction_FunctionType type);
|
||||
EventsFunction_FunctionType GetFunctionType();
|
||||
|
||||
|
@@ -14,6 +14,8 @@ declare class gdEventsFunction {
|
||||
getFullName(): string;
|
||||
setSentence(sentence: string): gdEventsFunction;
|
||||
getSentence(): string;
|
||||
setPrivate(isPrivate: boolean): gdEventsFunction;
|
||||
isPrivate(): boolean;
|
||||
setFunctionType(type: EventsFunction_FunctionType): gdEventsFunction;
|
||||
getFunctionType(): EventsFunction_FunctionType;
|
||||
getEvents(): gdEventsList;
|
||||
|
@@ -222,9 +222,10 @@ const generateFreeFunction = (
|
||||
|
||||
// Hide "lifecycle" functions as they are called automatically by
|
||||
// the game engine.
|
||||
if (isExtensionLifecycleEventsFunction(eventsFunction.getName())) {
|
||||
if (isExtensionLifecycleEventsFunction(eventsFunction.getName()))
|
||||
instructionOrExpression.setHidden();
|
||||
}
|
||||
|
||||
if (eventsFunction.isPrivate()) instructionOrExpression.setPrivate();
|
||||
|
||||
const codeNamespace = getFreeFunctionCodeNamespace(
|
||||
eventsFunction,
|
||||
@@ -348,6 +349,8 @@ function generateBehavior(
|
||||
instructionOrExpression.setHidden();
|
||||
}
|
||||
|
||||
if (eventsFunction.isPrivate()) instructionOrExpression.setPrivate();
|
||||
|
||||
const codeExtraInformation = instructionOrExpression.getCodeExtraInformation();
|
||||
codeExtraInformation
|
||||
.setIncludeFile(includeFile)
|
||||
|
@@ -37,7 +37,7 @@ export type EventsFunctionCreationParameters = {|
|
||||
|};
|
||||
|
||||
const getEventsFunctionName = (eventsFunction: gdEventsFunction) =>
|
||||
eventsFunction.getName();
|
||||
eventsFunction.getName() + (eventsFunction.isPrivate() ? ' (private)' : '');
|
||||
|
||||
type State = {|
|
||||
renamedEventsFunction: ?gdEventsFunction,
|
||||
@@ -86,6 +86,11 @@ export default class EventsFunctionsList extends React.Component<Props, State> {
|
||||
searchText: '',
|
||||
};
|
||||
|
||||
_togglePrivate = (eventsFunction: gdEventsFunction) => {
|
||||
eventsFunction.setPrivate(!eventsFunction.isPrivate());
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
_deleteEventsFunction = (
|
||||
eventsFunction: gdEventsFunction,
|
||||
{ askForConfirmation }: {| askForConfirmation: boolean |}
|
||||
@@ -223,6 +228,12 @@ export default class EventsFunctionsList extends React.Component<Props, State> {
|
||||
click: () => this._editName(eventsFunction),
|
||||
enabled: this.props.canRename(eventsFunction),
|
||||
},
|
||||
{
|
||||
label: eventsFunction.isPrivate()
|
||||
? i18n._(t`Make public`)
|
||||
: i18n._(t`Make private`),
|
||||
click: () => this._togglePrivate(eventsFunction),
|
||||
},
|
||||
{
|
||||
label: i18n._(t`Remove`),
|
||||
click: () =>
|
||||
|
@@ -16,7 +16,10 @@ import {
|
||||
filterInstructionsList,
|
||||
deduplicateInstructionsList,
|
||||
} from '../../InstructionOrExpression/EnumerateInstructions';
|
||||
import { type EnumeratedInstructionMetadata } from '../../InstructionOrExpression/EnumeratedInstructionOrExpressionMetadata.js';
|
||||
import {
|
||||
type EnumeratedInstructionMetadata,
|
||||
filterEnumeratedInstructionOrExpressionMetadataByScope,
|
||||
} from '../../InstructionOrExpression/EnumeratedInstructionOrExpressionMetadata.js';
|
||||
import { List, type ListItemRefType, ListItem } from '../../UI/List';
|
||||
import SearchBar, { useShouldAutofocusSearchbar } from '../../UI/SearchBar';
|
||||
import ThemeConsumer from '../../UI/Theme/ThemeConsumer';
|
||||
@@ -43,6 +46,7 @@ import {
|
||||
getObjectOrObjectGroupListItemValue,
|
||||
getInstructionListItemValue,
|
||||
} from './SelectorListItems/Keys';
|
||||
import { type EventsScope } from '../../InstructionOrExpression/EventsScope.flow';
|
||||
|
||||
const styles = {
|
||||
searchBar: {
|
||||
@@ -63,6 +67,7 @@ type Props = {|
|
||||
project: gdProject,
|
||||
globalObjectsContainer: gdObjectsContainer,
|
||||
objectsContainer: gdObjectsContainer,
|
||||
scope: EventsScope,
|
||||
currentTab: TabName,
|
||||
onChangeTab: TabName => void,
|
||||
isCondition: boolean,
|
||||
@@ -88,8 +93,9 @@ export default class InstructionOrObjectSelector extends React.PureComponent<
|
||||
_selectedItem = React.createRef<ListItemRefType>();
|
||||
|
||||
// Free instructions, to be displayed in a tab next to the objects.
|
||||
freeInstructionsInfo: Array<EnumeratedInstructionMetadata> = enumerateFreeInstructions(
|
||||
this.props.isCondition
|
||||
freeInstructionsInfo: Array<EnumeratedInstructionMetadata> = filterEnumeratedInstructionOrExpressionMetadataByScope(
|
||||
enumerateFreeInstructions(this.props.isCondition),
|
||||
this.props.scope
|
||||
);
|
||||
freeInstructionsInfoTree: InstructionOrExpressionTreeNode = createTree(
|
||||
this.freeInstructionsInfo
|
||||
@@ -101,8 +107,9 @@ export default class InstructionOrObjectSelector extends React.PureComponent<
|
||||
|
||||
// All the instructions, to be used when searching, so that the search is done
|
||||
// across all the instructions (including object and behaviors instructions).
|
||||
allInstructionsInfo: Array<EnumeratedInstructionMetadata> = enumerateAllInstructions(
|
||||
this.props.isCondition
|
||||
allInstructionsInfo: Array<EnumeratedInstructionMetadata> = filterEnumeratedInstructionOrExpressionMetadataByScope(
|
||||
enumerateAllInstructions(this.props.isCondition),
|
||||
this.props.scope
|
||||
);
|
||||
|
||||
componentDidMount() {
|
||||
|
@@ -176,6 +176,7 @@ export default function NewInstructionEditorDialog({
|
||||
<InstructionOrObjectSelector
|
||||
style={styles.fullHeightSelector}
|
||||
project={project}
|
||||
scope={scope}
|
||||
currentTab={currentInstructionOrObjectSelectorTab}
|
||||
onChangeTab={setCurrentInstructionOrObjectSelectorTab}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
|
@@ -142,6 +142,7 @@ export default function NewInstructionEditorMenu({
|
||||
key="instruction-or-object-selector"
|
||||
style={styles.fullHeightSelector}
|
||||
project={project}
|
||||
scope={scope}
|
||||
currentTab={currentInstructionOrObjectSelectorTab}
|
||||
onChangeTab={setCurrentInstructionOrObjectSelectorTab}
|
||||
globalObjectsContainer={globalObjectsContainer}
|
||||
|
@@ -75,6 +75,7 @@ export const enumerateFreeExpressions = (
|
||||
? extension.getAllStrExpressions()
|
||||
: extension.getAllExpressions(),
|
||||
{
|
||||
extension,
|
||||
objectMetadata: undefined,
|
||||
behaviorMetadata: undefined,
|
||||
}
|
||||
@@ -94,7 +95,7 @@ export const enumerateObjectExpressions = (
|
||||
);
|
||||
const extension = extensionAndObjectMetadata.getExtension();
|
||||
const objectMetadata = extensionAndObjectMetadata.getMetadata();
|
||||
const scope = { objectMetadata };
|
||||
const scope = { extension, objectMetadata };
|
||||
|
||||
let objectsExpressions = enumerateExpressionMetadataMap(
|
||||
'',
|
||||
@@ -138,7 +139,7 @@ export const enumerateBehaviorExpressions = (
|
||||
);
|
||||
const extension = extensionAndBehaviorMetadata.getExtension();
|
||||
const behaviorMetadata = extensionAndBehaviorMetadata.getMetadata();
|
||||
const scope = { behaviorMetadata };
|
||||
const scope = { extension, behaviorMetadata };
|
||||
|
||||
return enumerateExpressionMetadataMap(
|
||||
'',
|
||||
@@ -173,7 +174,7 @@ export const enumerateAllExpressions = (
|
||||
type === 'string'
|
||||
? extension.getAllStrExpressionsForObject(objectType)
|
||||
: extension.getAllExpressionsForObject(objectType),
|
||||
{ objectMetadata }
|
||||
{ extension, objectMetadata }
|
||||
)
|
||||
);
|
||||
});
|
||||
@@ -188,7 +189,7 @@ export const enumerateAllExpressions = (
|
||||
type === 'string'
|
||||
? extension.getAllStrExpressionsForBehavior(behaviorType)
|
||||
: extension.getAllExpressionsForBehavior(behaviorType),
|
||||
{ behaviorMetadata }
|
||||
{ extension, behaviorMetadata }
|
||||
)
|
||||
);
|
||||
});
|
||||
|
@@ -231,6 +231,7 @@ export const enumerateAllInstructions = (
|
||||
prefix,
|
||||
isCondition ? extension.getAllConditions() : extension.getAllActions(),
|
||||
{
|
||||
extension,
|
||||
objectMetadata: undefined,
|
||||
behaviorMetadata: undefined,
|
||||
}
|
||||
@@ -247,7 +248,7 @@ export const enumerateAllInstructions = (
|
||||
for (let j = 0; j < allObjectsTypes.size(); ++j) {
|
||||
const objectType = allObjectsTypes.at(j);
|
||||
const objectMetadata = extension.getObjectMetadata(objectType);
|
||||
const scope = { objectMetadata };
|
||||
const scope = { extension, objectMetadata };
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
...enumerateExtensionInstructions(
|
||||
@@ -271,7 +272,7 @@ export const enumerateAllInstructions = (
|
||||
for (let j = 0; j < allBehaviorsTypes.size(); ++j) {
|
||||
const behaviorType = allBehaviorsTypes.at(j);
|
||||
const behaviorMetadata = extension.getBehaviorMetadata(behaviorType);
|
||||
const scope = { behaviorMetadata };
|
||||
const scope = { extension, behaviorMetadata };
|
||||
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
@@ -377,7 +378,7 @@ export const enumerateObjectAndBehaviorsInstructions = (
|
||||
//Objects instructions:
|
||||
if (objectType !== baseObjectType && hasObjectType) {
|
||||
const objectMetadata = extension.getObjectMetadata(objectType);
|
||||
const scope = { objectMetadata };
|
||||
const scope = { extension, objectMetadata };
|
||||
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
@@ -400,7 +401,7 @@ export const enumerateObjectAndBehaviorsInstructions = (
|
||||
|
||||
if (hasBaseObjectType) {
|
||||
const objectMetadata = extension.getObjectMetadata(baseObjectType);
|
||||
const scope = { objectMetadata };
|
||||
const scope = { extension, objectMetadata };
|
||||
|
||||
allInstructions = [
|
||||
...allInstructions,
|
||||
@@ -425,7 +426,7 @@ export const enumerateObjectAndBehaviorsInstructions = (
|
||||
// eslint-disable-next-line
|
||||
behaviorTypes.forEach(behaviorType => {
|
||||
const behaviorMetadata = extension.getBehaviorMetadata(behaviorType);
|
||||
const scope = { behaviorMetadata };
|
||||
const scope = { extension, behaviorMetadata };
|
||||
|
||||
allInstructions = [
|
||||
...enumerateExtensionInstructions(
|
||||
@@ -479,6 +480,7 @@ export const enumerateFreeInstructions = (
|
||||
prefix,
|
||||
isCondition ? extension.getAllConditions() : extension.getAllActions(),
|
||||
{
|
||||
extension,
|
||||
objectMetadata: undefined,
|
||||
behaviorMetadata: undefined,
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ import { type EventsScope } from './EventsScope.flow';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
export type InstructionOrExpressionScope = {|
|
||||
extension: gdPlatformExtension,
|
||||
objectMetadata?: ?gdObjectMetadata,
|
||||
behaviorMetadata?: ?gdBehaviorMetadata,
|
||||
|};
|
||||
@@ -29,6 +30,13 @@ export type EnumeratedExpressionMetadata = {|
|
||||
parameters: Array<gdParameterMetadata>,
|
||||
|};
|
||||
|
||||
// This is copied from gd::WholeProjectRefactorer (see GetBehaviorFullType)
|
||||
// Could be factored into a single C++ function in gd::PlatformExtension?
|
||||
const getBehaviorFullType = (extensionName: string, behaviorName: string) => {
|
||||
const separator = gd.PlatformExtension.getNamespaceSeparator();
|
||||
return extensionName + separator + behaviorName;
|
||||
};
|
||||
|
||||
// An object representing InstructionMetadata or ExpressionMetadata.
|
||||
// Allow to use most information without paying the cost to call the
|
||||
// InstructionMetadata/ExpressionMetadata methods. In theory,
|
||||
@@ -37,27 +45,31 @@ export type EnumeratedInstructionOrExpressionMetadata =
|
||||
| EnumeratedInstructionMetadata
|
||||
| EnumeratedExpressionMetadata;
|
||||
|
||||
/**
|
||||
* Given a list of expression or instructions that were previously enumerated,
|
||||
* filter the ones that are not usable from the current "scope".
|
||||
*/
|
||||
export const filterEnumeratedInstructionOrExpressionMetadataByScope = <
|
||||
+T: EnumeratedInstructionOrExpressionMetadata
|
||||
>(
|
||||
list: Array<T>,
|
||||
scope: EventsScope
|
||||
): Array<T> => {
|
||||
// This is copied from gd::WholeProjectRefactorer (see GetBehaviorFullType)
|
||||
// Could be factored into a single C++ function in gd::PlatformExtension?
|
||||
const separator = gd.PlatformExtension.getNamespaceSeparator();
|
||||
const getBehaviorFullType = (extensionName: string, behaviorName: string) => {
|
||||
return extensionName + separator + behaviorName;
|
||||
};
|
||||
|
||||
return list.filter(enumeratedInstructionOrExpressionMetadata => {
|
||||
if (!enumeratedInstructionOrExpressionMetadata.isPrivate) return true;
|
||||
|
||||
// The instruction or expression is marked as "private":
|
||||
// we now compare its scope (where it was declared) and the current scope
|
||||
// (where we are) to see if we should filter it or not.
|
||||
|
||||
const {
|
||||
behaviorMetadata,
|
||||
} = enumeratedInstructionOrExpressionMetadata.scope;
|
||||
type,
|
||||
scope: { behaviorMetadata, extension },
|
||||
} = enumeratedInstructionOrExpressionMetadata;
|
||||
const { eventsBasedBehavior, eventsFunctionsExtension } = scope;
|
||||
|
||||
// Show private behavior functions when editing the behavior
|
||||
if (
|
||||
behaviorMetadata &&
|
||||
eventsBasedBehavior &&
|
||||
@@ -66,9 +78,16 @@ export const filterEnumeratedInstructionOrExpressionMetadataByScope = <
|
||||
eventsFunctionsExtension.getName(),
|
||||
eventsBasedBehavior.getName()
|
||||
) === behaviorMetadata.getName()
|
||||
) {
|
||||
)
|
||||
return true;
|
||||
|
||||
// Show private non-behavior functions when editing the extension
|
||||
if (
|
||||
!behaviorMetadata &&
|
||||
eventsFunctionsExtension &&
|
||||
eventsFunctionsExtension.getName() === extension.getName()
|
||||
)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
@@ -3,8 +3,21 @@ import { type ExpressionAutocompletion } from '../ExpressionAutocompletion';
|
||||
import { type EnumeratedExpressionMetadata } from '../InstructionOrExpression/EnumeratedInstructionOrExpressionMetadata.js';
|
||||
import { mapVector } from '../Utils/MapFor';
|
||||
|
||||
const makeNewFakeExtension = (gd: libGDevelop) => {
|
||||
const extension = new gd.PlatformExtension();
|
||||
extension.setExtensionInformation(
|
||||
'FakeExtensionForAutocompletionTests',
|
||||
'FakeExtensionForAutocompletionTests',
|
||||
'FakeExtensionForAutocompletionTests',
|
||||
'The extension author',
|
||||
'MIT'
|
||||
);
|
||||
return extension;
|
||||
};
|
||||
|
||||
const makeFakeEnumeratedExpressionMetadata = (
|
||||
name: string,
|
||||
extension: gdPlatformExtension,
|
||||
expressionMetadata: gdExpressionMetadata
|
||||
): EnumeratedExpressionMetadata => ({
|
||||
type: name,
|
||||
@@ -17,7 +30,7 @@ const makeFakeEnumeratedExpressionMetadata = (
|
||||
expressionMetadata.getParameters(),
|
||||
parameterMetadata => parameterMetadata
|
||||
),
|
||||
scope: {},
|
||||
scope: { extension },
|
||||
isPrivate: false,
|
||||
});
|
||||
|
||||
@@ -36,6 +49,8 @@ export const makeFakeExactExpressionAutocompletion = () => {
|
||||
expressionMetadata.addParameter('expression', 'Some number', '', false);
|
||||
expressionMetadata.addParameter('string', 'Some string', '', false);
|
||||
|
||||
const extension = makeNewFakeExtension(gd);
|
||||
|
||||
return [
|
||||
{
|
||||
kind: 'Expression',
|
||||
@@ -44,6 +59,7 @@ export const makeFakeExactExpressionAutocompletion = () => {
|
||||
isExact: true,
|
||||
enumeratedExpressionMetadata: makeFakeEnumeratedExpressionMetadata(
|
||||
'MyFunction',
|
||||
extension,
|
||||
expressionMetadata
|
||||
),
|
||||
},
|
||||
@@ -75,6 +91,8 @@ export const makeFakeExpressionAutocompletions = (): Array<ExpressionAutocomplet
|
||||
'res/actions/replaceScene.png'
|
||||
);
|
||||
|
||||
const extension = makeNewFakeExtension(gd);
|
||||
|
||||
return [
|
||||
{
|
||||
kind: 'Object',
|
||||
@@ -113,6 +131,7 @@ export const makeFakeExpressionAutocompletions = (): Array<ExpressionAutocomplet
|
||||
isExact: false,
|
||||
enumeratedExpressionMetadata: makeFakeEnumeratedExpressionMetadata(
|
||||
'MyFunctionWithoutParams',
|
||||
extension,
|
||||
expressionWithoutParamsMetadata
|
||||
),
|
||||
},
|
||||
@@ -123,6 +142,7 @@ export const makeFakeExpressionAutocompletions = (): Array<ExpressionAutocomplet
|
||||
isExact: false,
|
||||
enumeratedExpressionMetadata: makeFakeEnumeratedExpressionMetadata(
|
||||
'MyFunction',
|
||||
extension,
|
||||
expressionMetadata
|
||||
),
|
||||
},
|
||||
|
@@ -3036,7 +3036,7 @@ storiesOf('InstructionSelector', module)
|
||||
storiesOf('InstructionOrObjectSelector', module)
|
||||
.addDecorator(paperDecorator)
|
||||
.addDecorator(muiDecorator)
|
||||
.add('"KeyPressed" condition chosen, ', () => (
|
||||
.add('"KeyPressed" condition chosen, scope: layout', () => (
|
||||
<ValueStateHolder
|
||||
initialValue={'free-instructions'}
|
||||
render={(value, onChange) => (
|
||||
@@ -3044,6 +3044,7 @@ storiesOf('InstructionOrObjectSelector', module)
|
||||
<InstructionOrObjectSelector
|
||||
style={{ flex: 1, display: 'flex', flexDirection: 'column' }} // TODO
|
||||
project={testProject.project}
|
||||
scope={{ layout: testProject.testLayout }}
|
||||
currentTab={value}
|
||||
onChangeTab={onChange}
|
||||
globalObjectsContainer={testProject.project}
|
||||
@@ -3059,7 +3060,7 @@ storiesOf('InstructionOrObjectSelector', module)
|
||||
)}
|
||||
/>
|
||||
))
|
||||
.add('"MySpriteObject" object chosen, ', () => (
|
||||
.add('"MySpriteObject" object chosen, scope: layout', () => (
|
||||
<ValueStateHolder
|
||||
initialValue={'objects'}
|
||||
render={(value, onChange) => (
|
||||
@@ -3067,6 +3068,7 @@ storiesOf('InstructionOrObjectSelector', module)
|
||||
<InstructionOrObjectSelector
|
||||
style={{ flex: 1, display: 'flex', flexDirection: 'column' }} // TODO
|
||||
project={testProject.project}
|
||||
scope={{ layout: testProject.testLayout }}
|
||||
currentTab={value}
|
||||
onChangeTab={onChange}
|
||||
globalObjectsContainer={testProject.project}
|
||||
|
Reference in New Issue
Block a user