Allow to choose the type for undeclared child-variables.

This commit is contained in:
Davy Hélard
2024-04-02 20:43:55 +02:00
parent eb9bef00ca
commit bc8535bbca
13 changed files with 249 additions and 98 deletions

View File

@@ -39,16 +39,6 @@ class GD_CORE_API ExpressionVariableTypeFinder
: public ExpressionParser2NodeWorker {
public:
static const gd::Variable::Type GetVariableType(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node) {
gd::ExpressionVariableTypeFinder typeFinder(platform,
projectScopedContainers);
node.Visit(typeFinder);
return typeFinder.variable ? typeFinder.variable->GetType() : gd::Variable::Unknown;
}
static const gd::Variable::Type GetObjectVariableType(
const gd::Platform& platform,
const gd::ProjectScopedContainers& projectScopedContainers,
gd::ExpressionNode& node, const gd::String& objectName) {
@@ -91,7 +81,8 @@ class GD_CORE_API ExpressionVariableTypeFinder
childVariableNames);
}
} else {
if (objectsContainersList.hasObjectOrGroupWithVariableNamed(objectName,
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(objectName,
node.name)) {
const auto &variableContainer =
projectScopedContainers.GetObjectsContainersList()
@@ -132,7 +123,8 @@ class GD_CORE_API ExpressionVariableTypeFinder
childVariableNames);
}
} else {
if (objectsContainersList.hasObjectOrGroupWithVariableNamed(
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
if (objectsContainersList.HasObjectOrGroupWithVariableNamed(
objectName, node.identifierName)) {
const auto &variableContainer =
projectScopedContainers.GetObjectsContainersList()

View File

@@ -16,7 +16,8 @@ const gd::String VariableInstructionSwitcher::objectVariableGetterIdentifier = "
const gd::String VariableInstructionSwitcher::objectVariableSetterIdentifier = "SetNumberObjectVariable";
const gd::String VariableInstructionSwitcher::unknownInstructionIdentifier = "";
bool VariableInstructionSwitcher::IsSwitchableVariableInstruction(const gd::String& instructionType) {
bool VariableInstructionSwitcher::IsSwitchableVariableInstruction(
const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "StringVariable" ||
instructionType == "BooleanVariable" ||
@@ -31,7 +32,9 @@ bool VariableInstructionSwitcher::IsSwitchableVariableInstruction(const gd::Stri
instructionType == "SetBooleanObjectVariable";
}
const gd::String& VariableInstructionSwitcher::GetSwitchableVariableInstructionIdentifier(const gd::String& instructionType) {
const gd::String &
VariableInstructionSwitcher::GetSwitchableVariableInstructionIdentifier(
const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "StringVariable" ||
instructionType == "BooleanVariable"
@@ -51,6 +54,26 @@ const gd::String& VariableInstructionSwitcher::GetSwitchableVariableInstructionI
VariableInstructionSwitcher::unknownInstructionIdentifier;
}
const gd::Variable::Type
VariableInstructionSwitcher::GetSwitchableInstructionVariableType(const gd::String &instructionType) {
return instructionType == "NumberVariable" ||
instructionType == "SetNumberVariable" ||
instructionType == "NumberObjectVariable" ||
instructionType == "SetNumberObjectVariable"
? gd::Variable::Number :
instructionType == "StringVariable" ||
instructionType == "SetStringVariable" ||
instructionType == "StringObjectVariable" ||
instructionType == "SetStringObjectVariable"
? gd::Variable::String :
instructionType == "BooleanVariable" ||
instructionType == "SetBooleanVariable" ||
instructionType == "BooleanObjectVariable" ||
instructionType == "SetBooleanObjectVariable"
? gd::Variable::Boolean :
gd::Variable::Unknown;
}
void VariableInstructionSwitcher::SwitchVariableInstructionType(
gd::Instruction& instruction, const gd::Variable::Type variableType) {
if (instruction.GetType() == "NumberVariable" ||

View File

@@ -35,6 +35,13 @@ public:
static const gd::String &
GetSwitchableVariableInstructionIdentifier(const gd::String &instructionType);
/**
* \brief Return the variable type for primitive variable getter or
* setter.
*/
static const gd::Variable::Type
GetSwitchableInstructionVariableType(const gd::String &instructionType);
/**
* \brief Modify the instruction type to match the given variable type.
*/

View File

@@ -225,6 +225,7 @@ interface PairStringVariable {
};
enum Variable_Type {
"Variable::Unknown",
"Variable::String",
"Variable::Number",
"Variable::Boolean",
@@ -235,16 +236,13 @@ enum Variable_Type {
interface VariableInstructionSwitcher {
boolean STATIC_IsSwitchableVariableInstruction([Const] DOMString instructionType);
[Const, Ref] DOMString STATIC_GetSwitchableVariableInstructionIdentifier([Const] DOMString instructionType);
[Const] Variable_Type STATIC_GetSwitchableInstructionVariableType([Const] DOMString instructionType);
void STATIC_SwitchVariableInstructionType(
[Ref] Instruction instruction, [Const] Variable_Type variableType);
};
interface ExpressionVariableTypeFinder {
[Const] Variable_Type STATIC_GetVariableType(
[Const, Ref] Platform platform,
[Const, Ref] ProjectScopedContainers projectScopedContainers,
[Ref] ExpressionNode node);
[Const] Variable_Type STATIC_GetObjectVariableType(
[Const, Ref] Platform platform,
[Const, Ref] ProjectScopedContainers projectScopedContainers,
[Ref] ExpressionNode node,

View File

@@ -672,8 +672,8 @@ typedef ExtensionAndMetadata<ExpressionMetadata> ExtensionAndExpressionMetadata;
#define STATIC_SwitchVariableInstructionType SwitchVariableInstructionType
#define STATIC_IsSwitchableVariableInstruction IsSwitchableVariableInstruction
#define STATIC_GetSwitchableVariableInstructionIdentifier GetSwitchableVariableInstructionIdentifier
#define STATIC_GetSwitchableInstructionVariableType GetSwitchableInstructionVariableType
#define STATIC_GetVariableType GetVariableType
#define STATIC_GetObjectVariableType GetObjectVariableType
#define STATIC_IsFreeFunctionOnlyCallingItself IsFreeFunctionOnlyCallingItself
#define STATIC_IsBehaviorFunctionOnlyCallingItself \

View File

@@ -105,18 +105,19 @@ type EventsFunctionsContainer_FunctionOwner = 0 | 1 | 2`
fs.writeFileSync(
'types/variable_type.js',
`// Automatically generated by GDevelop.js/scripts/generate-types.js
type Variable_Type = 0 | 1 | 2 | 3 | 4`
type Variable_Type = 0 | 1 | 2 | 3 | 4 | 5`
);
shell.sed(
'-i',
'declare class gdVariable {',
[
'declare class gdVariable {',
' static String: 0;',
' static Number: 1;',
' static Boolean: 2;',
' static Structure: 3;',
' static Array: 4;',
' static Unknown: 0;',
' static String: 1;',
' static Number: 2;',
' static Boolean: 3;',
' static Structure: 4;',
' static Array: 5;',
].join('\n'),
'types/gdvariable.js'
);

View File

@@ -20,11 +20,12 @@ declare class EmscriptenObject {
}
export enum Variable_Type {
String = 0,
Number = 1,
Boolean = 2,
Structure = 3,
Array = 4,
Unknown = 0,
String = 1,
Number = 2,
Boolean = 3,
Structure = 4,
Array = 5,
}
export enum ObjectsContainersList_VariableExistence {
@@ -243,12 +244,12 @@ export class PairStringVariable extends EmscriptenObject {
export class VariableInstructionSwitcher extends EmscriptenObject {
static isSwitchableVariableInstruction(instructionType: string): boolean;
static getSwitchableVariableInstructionIdentifier(instructionType: string): string;
static getSwitchableInstructionVariableType(instructionType: string): Variable_Type;
static switchVariableInstructionType(instruction: Instruction, variableType: Variable_Type): void;
}
export class ExpressionVariableTypeFinder extends EmscriptenObject {
static getVariableType(platform: Platform, projectScopedContainers: ProjectScopedContainers, node: ExpressionNode): Variable_Type;
static getObjectVariableType(platform: Platform, projectScopedContainers: ProjectScopedContainers, node: ExpressionNode, objectName: string): Variable_Type;
static getVariableType(platform: Platform, projectScopedContainers: ProjectScopedContainers, node: ExpressionNode, objectName: string): Variable_Type;
}
export class Variable extends EmscriptenObject {

View File

@@ -1,7 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdExpressionVariableTypeFinder {
static getVariableType(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, node: gdExpressionNode): Variable_Type;
static getObjectVariableType(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, node: gdExpressionNode, objectName: string): Variable_Type;
static getVariableType(platform: gdPlatform, projectScopedContainers: gdProjectScopedContainers, node: gdExpressionNode, objectName: string): Variable_Type;
delete(): void;
ptr: number;
};

View File

@@ -1,10 +1,11 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdVariable {
static String: 0;
static Number: 1;
static Boolean: 2;
static Structure: 3;
static Array: 4;
static Unknown: 0;
static String: 1;
static Number: 2;
static Boolean: 3;
static Structure: 4;
static Array: 5;
constructor(): void;
static isPrimitive(type: Variable_Type): boolean;
getType(): Variable_Type;

View File

@@ -2,6 +2,7 @@
declare class gdVariableInstructionSwitcher {
static isSwitchableVariableInstruction(instructionType: string): boolean;
static getSwitchableVariableInstructionIdentifier(instructionType: string): string;
static getSwitchableInstructionVariableType(instructionType: string): Variable_Type;
static switchVariableInstructionType(instruction: gdInstruction, variableType: Variable_Type): void;
delete(): void;
ptr: number;

View File

@@ -1,2 +1,2 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
type Variable_Type = 0 | 1 | 2 | 3 | 4
type Variable_Type = 0 | 1 | 2 | 3 | 4 | 5

View File

@@ -1,5 +1,6 @@
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import * as React from 'react';
import { type ParameterInlineRendererProps } from './ParameterInlineRenderer.flow';
import VariableField, {
@@ -14,26 +15,45 @@ import {
type FieldFocusFunction,
} from './ParameterFieldCommons';
import { enumerateValidVariableNames } from './EnumerateVariables';
import { getProjectScopedContainersFromScope } from '../../InstructionOrExpression/EventsScope.flow';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import { ColumnStackLayout } from '../../UI/Layout';
const gd: libGDevelop = global.gd;
export const switchBetweenUnifiedInstructionIfNeeded = (
const getVariableTypeFromParameters = (
platform: gdPlatform,
projectScopedContainers: gdProjectScopedContainers,
instruction: gdInstruction
): void => {
): Variable_Type | null => {
if (
instruction.getParametersCount() > 0 &&
gd.VariableInstructionSwitcher.isSwitchableVariableInstruction(
instruction.getType()
)
) {
const variableType = gd.ExpressionVariableTypeFinder.getVariableType(
return gd.ExpressionVariableTypeFinder.getVariableType(
platform,
projectScopedContainers,
instruction.getParameter(0).getRootNode()
instruction.getParameter(0).getRootNode(),
''
);
}
return null;
};
export const switchBetweenUnifiedInstructionIfNeeded = (
platform: gdPlatform,
projectScopedContainers: gdProjectScopedContainers,
instruction: gdInstruction
): void => {
const variableType = getVariableTypeFromParameters(
platform,
projectScopedContainers,
instruction
);
if (variableType != null) {
gd.VariableInstructionSwitcher.switchVariableInstructionType(
instruction,
variableType
@@ -52,7 +72,14 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
focus,
}));
const { project, scope, onInstructionTypeChanged } = props;
const {
project,
globalObjectsContainer,
objectsContainer,
scope,
onInstructionTypeChanged,
instruction,
} = props;
const { layout } = scope;
const onComputeAllVariableNames = React.useCallback(() => [], []);
@@ -78,29 +105,69 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
[layout, project]
);
const projectScopedContainers = getProjectScopedContainersFromScope(
scope,
globalObjectsContainer,
objectsContainer
);
const variableType =
project && instruction
? getVariableTypeFromParameters(
project.getCurrentPlatform(),
projectScopedContainers,
instruction
)
: null;
const needManualTypeSwitcher = variableType === gd.Variable.Unknown;
return (
<React.Fragment>
<VariableField
forceDeclaration
variablesContainers={variablesContainers}
enumerateVariableNames={enumerateGlobalAndSceneVariableNames}
parameterMetadata={props.parameterMetadata}
value={props.value}
onChange={props.onChange}
isInline={props.isInline}
onRequestClose={props.onRequestClose}
onApply={props.onApply}
ref={field}
onOpenDialog={() => setEditorOpen(true)}
globalObjectsContainer={props.globalObjectsContainer}
objectsContainer={props.objectsContainer}
scope={scope}
id={
props.parameterIndex !== undefined
? `parameter-${props.parameterIndex}-scene-variable-field`
: undefined
}
/>
<ColumnStackLayout noMargin>
<VariableField
forceDeclaration
variablesContainers={variablesContainers}
enumerateVariableNames={enumerateGlobalAndSceneVariableNames}
parameterMetadata={props.parameterMetadata}
value={props.value}
onChange={props.onChange}
isInline={props.isInline}
onRequestClose={props.onRequestClose}
onApply={props.onApply}
ref={field}
onOpenDialog={() => setEditorOpen(true)}
globalObjectsContainer={props.globalObjectsContainer}
objectsContainer={props.objectsContainer}
scope={scope}
id={
props.parameterIndex !== undefined
? `parameter-${props.parameterIndex}-scene-variable-field`
: undefined
}
getVariableTypeFromParameters={getVariableTypeFromParameters}
/>
{needManualTypeSwitcher && instruction && onInstructionTypeChanged && (
<SelectField
floatingLabelText={<Trans>Use as...</Trans>}
value={(() => {
const type = gd.VariableInstructionSwitcher.getSwitchableInstructionVariableType(
instruction.getType()
);
return type === gd.Variable.Unknown ? gd.Variable.Number : type;
})()}
onChange={(e, i, value: any) => {
gd.VariableInstructionSwitcher.switchVariableInstructionType(
instruction,
value
);
onInstructionTypeChanged();
}}
>
<SelectOption value={gd.Variable.Number} label={t`Number`} />
<SelectOption value={gd.Variable.String} label={t`String`} />
<SelectOption value={gd.Variable.Boolean} label={t`Boolean`} />
</SelectField>
)}
</ColumnStackLayout>
{editorOpen && layout && project && (
<VariablesEditorDialog
project={project}

View File

@@ -1,5 +1,6 @@
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import * as React from 'react';
import { type ParameterInlineRendererProps } from './ParameterInlineRenderer.flow';
import VariableField, {
@@ -19,6 +20,10 @@ import getObjectGroupByName from '../../Utils/GetObjectGroupByName';
import ObjectIcon from '../../UI/CustomSvgIcons/Object';
import { enumerateValidVariableNames } from './EnumerateVariables';
import intersection from 'lodash/intersection';
import { getProjectScopedContainersFromScope } from '../../InstructionOrExpression/EventsScope.flow';
import SelectField from '../../UI/SelectField';
import SelectOption from '../../UI/SelectOption';
import { ColumnStackLayout } from '../../UI/Layout';
const gd: libGDevelop = global.gd;
@@ -58,11 +63,11 @@ const getObjectOrGroupVariablesContainers = (
return variablesContainers;
};
export const switchBetweenUnifiedObjectInstructionIfNeeded = (
const getVariableTypeFromParameters = (
platform: gdPlatform,
projectScopedContainers: gdProjectScopedContainers,
instruction: gdInstruction
): void => {
): Variable_Type | null => {
if (
instruction.getParametersCount() > 1 &&
gd.VariableInstructionSwitcher.isSwitchableVariableInstruction(
@@ -71,13 +76,27 @@ export const switchBetweenUnifiedObjectInstructionIfNeeded = (
) {
const objectName = instruction.getParameter(0).getPlainString();
const variableType = gd.ExpressionVariableTypeFinder.getObjectVariableType(
return gd.ExpressionVariableTypeFinder.getVariableType(
platform,
projectScopedContainers,
instruction.getParameter(1).getRootNode(),
objectName
);
}
return null;
};
export const switchBetweenUnifiedObjectInstructionIfNeeded = (
platform: gdPlatform,
projectScopedContainers: gdProjectScopedContainers,
instruction: gdInstruction
): void => {
const variableType = getVariableTypeFromParameters(
platform,
projectScopedContainers,
instruction
);
if (variableType != null) {
gd.VariableInstructionSwitcher.switchVariableInstructionType(
instruction,
variableType
@@ -153,37 +172,79 @@ export default React.forwardRef<ParameterFieldProps, ParameterFieldInterface>(
)
: [];
const projectScopedContainers = getProjectScopedContainersFromScope(
scope,
globalObjectsContainer,
objectsContainer
);
const variableType =
project && instruction
? getVariableTypeFromParameters(
project.getCurrentPlatform(),
projectScopedContainers,
instruction
)
: null;
const needManualTypeSwitcher = variableType === gd.Variable.Unknown;
return (
<React.Fragment>
<VariableField
forceDeclaration={
instruction &&
gd.VariableInstructionSwitcher.isSwitchableVariableInstruction(
instruction.getType()
)
}
variablesContainers={variablesContainers}
enumerateVariableNames={enumerateVariableNames}
parameterMetadata={props.parameterMetadata}
value={props.value}
onChange={props.onChange}
isInline={props.isInline}
onRequestClose={props.onRequestClose}
onApply={props.onApply}
ref={field}
// There is no variable editor for groups.
onOpenDialog={
variablesContainers.length === 1 ? () => setEditorOpen(true) : null
}
globalObjectsContainer={props.globalObjectsContainer}
objectsContainer={props.objectsContainer}
scope={scope}
id={
props.parameterIndex !== undefined
? `parameter-${props.parameterIndex}-object-variable-field`
: undefined
}
/>
<ColumnStackLayout noMargin>
<VariableField
forceDeclaration={
instruction &&
gd.VariableInstructionSwitcher.isSwitchableVariableInstruction(
instruction.getType()
)
}
variablesContainers={variablesContainers}
enumerateVariableNames={enumerateVariableNames}
parameterMetadata={props.parameterMetadata}
value={props.value}
onChange={props.onChange}
isInline={props.isInline}
onRequestClose={props.onRequestClose}
onApply={props.onApply}
ref={field}
// There is no variable editor for groups.
onOpenDialog={
variablesContainers.length === 1
? () => setEditorOpen(true)
: null
}
globalObjectsContainer={props.globalObjectsContainer}
objectsContainer={props.objectsContainer}
scope={scope}
id={
props.parameterIndex !== undefined
? `parameter-${props.parameterIndex}-object-variable-field`
: undefined
}
getVariableTypeFromParameters={getVariableTypeFromParameters}
/>
{needManualTypeSwitcher && instruction && onInstructionTypeChanged && (
<SelectField
floatingLabelText={<Trans>Use as...</Trans>}
value={(() => {
const type = gd.VariableInstructionSwitcher.getSwitchableInstructionVariableType(
instruction.getType()
);
return type === gd.Variable.Unknown ? gd.Variable.Number : type;
})()}
onChange={(e, i, value: any) => {
gd.VariableInstructionSwitcher.switchVariableInstructionType(
instruction,
value
);
onInstructionTypeChanged();
}}
>
<SelectOption value={gd.Variable.Number} label={t`Number`} />
<SelectOption value={gd.Variable.String} label={t`String`} />
<SelectOption value={gd.Variable.Boolean} label={t`Boolean`} />
</SelectField>
)}
</ColumnStackLayout>
{editorOpen && project && (
<VariablesEditorDialog
project={project}