Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions newIDE/app/src/AiGeneration/AskAiEditorContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { type I18n as I18nType } from '@lingui/core';
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
import { exceptionallyGuardAgainstDeadObject } from '../Utils/IsNullPtr';
import { I18n } from '@lingui/react';
import { type RenderEditorContainerPropsWithRef } from '../MainFrame/EditorContainers/BaseEditor';
import {
type RenderEditorContainerPropsWithRef,
type SceneEventsOutsideEditorChanges,
type InstancesOutsideEditorChanges,
type ObjectsOutsideEditorChanges,
type ObjectGroupsOutsideEditorChanges,
} from '../MainFrame/EditorContainers/BaseEditor';
type ProjectItemRenamedOutsideEditorChanges,
} from '../EditorFunctions/OutsideEditorChanges';
import { type ObjectWithContext } from '../ObjectsList/EnumerateObjects';
import Paper from '../UI/Paper';
import { AiRequestChat, type AiRequestChatInterface } from './AiRequestChat';
Expand Down Expand Up @@ -152,6 +153,9 @@ type Props = {|
onObjectGroupsModifiedOutsideEditor: (
changes: ObjectGroupsOutsideEditorChanges
) => void,
onProjectItemRenamedOutsideEditor: (
changes: ProjectItemRenamedOutsideEditorChanges
) => void,
onWillInstallExtension: (extensionNames: Array<string>) => void,
onExtensionInstalled: (extensionNames: Array<string>) => void,
onOpenAskAi: ({|
Expand Down Expand Up @@ -250,6 +254,7 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
onWillInstallExtension,
onExtensionInstalled,
onOpenAskAi,
Expand Down Expand Up @@ -916,6 +921,7 @@ export const AskAiEditor: React.ComponentType<Props> = React.memo<Props>(
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
i18n,
onWillInstallExtension,
onExtensionInstalled,
Expand Down Expand Up @@ -1627,6 +1633,9 @@ export const renderAskAiEditorContainer = (
onObjectGroupsModifiedOutsideEditor={
props.onObjectGroupsModifiedOutsideEditor
}
onProjectItemRenamedOutsideEditor={
props.onProjectItemRenamedOutsideEditor
}
onWillInstallExtension={props.onWillInstallExtension}
onExtensionInstalled={props.onExtensionInstalled}
onOpenAskAi={props.onOpenAskAi}
Expand Down
1 change: 1 addition & 0 deletions newIDE/app/src/AiGeneration/AskAiStandAloneForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,7 @@ export const AskAiStandAloneForm = ({
onInstancesModifiedOutsideEditor: () => {},
onObjectsModifiedOutsideEditor: () => {},
onObjectGroupsModifiedOutsideEditor: () => {},
onProjectItemRenamedOutsideEditor: () => {},
onWillInstallExtension,
onExtensionInstalled,
isReadyToProcessFunctionCalls: true,
Expand Down
11 changes: 10 additions & 1 deletion newIDE/app/src/AiGeneration/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
type InstancesOutsideEditorChanges,
type ObjectsOutsideEditorChanges,
type ObjectGroupsOutsideEditorChanges,
} from '../MainFrame/EditorContainers/BaseEditor';
type ProjectItemRenamedOutsideEditorChanges,
} from '../EditorFunctions/OutsideEditorChanges';
import {
getAiRequest,
getAiRequestSuggestions,
Expand Down Expand Up @@ -222,6 +223,7 @@ export const useProcessFunctionCalls = ({
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
onWillInstallExtension,
onExtensionInstalled,
isReadyToProcessFunctionCalls,
Expand Down Expand Up @@ -259,6 +261,9 @@ export const useProcessFunctionCalls = ({
onObjectGroupsModifiedOutsideEditor: (
changes: ObjectGroupsOutsideEditorChanges
) => void,
onProjectItemRenamedOutsideEditor: (
changes: ProjectItemRenamedOutsideEditorChanges
) => void,
onWillInstallExtension: (extensionNames: Array<string>) => void,
onExtensionInstalled: (extensionNames: Array<string>) => void,
isReadyToProcessFunctionCalls: boolean,
Expand Down Expand Up @@ -550,6 +555,9 @@ export const useProcessFunctionCalls = ({
onObjectGroupsModifiedOutsideEditor: changes => {
accumulatedObjectGroupsScenes.add(changes.scene);
},
// Not coalesced: the tab rename must track the model rename, else the
// open scene editor briefly looks up a now-missing layout name.
onProjectItemRenamedOutsideEditor,
ensureExtensionInstalled,
onWillInstallExtension,
onExtensionInstalled,
Expand Down Expand Up @@ -600,6 +608,7 @@ export const useProcessFunctionCalls = ({
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
ensureExtensionInstalled,
onWillInstallExtension,
onExtensionInstalled,
Expand Down
12 changes: 10 additions & 2 deletions newIDE/app/src/EditorFunctions/EditorFunctionCallRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import {
type RelatedAiRequestLastMessages,
type ResourceSearchAndInstallOptions,
type ResourceSearchAndInstallResult,
type ToolOptions,
} from '.';
import {
type SceneEventsOutsideEditorChanges,
type InstancesOutsideEditorChanges,
type ObjectsOutsideEditorChanges,
type ObjectGroupsOutsideEditorChanges,
type ToolOptions,
} from '.';
type ProjectItemRenamedOutsideEditorChanges,
} from './OutsideEditorChanges';
import PixiResourcesLoader from '../ObjectsRendering/PixiResourcesLoader';
import { type EnsureExtensionInstalledOptions } from '../AiGeneration/UseEnsureExtensionInstalled';

Expand All @@ -48,6 +51,9 @@ type ProcessEditorFunctionCallsOptions = {|
onObjectGroupsModifiedOutsideEditor: (
changes: ObjectGroupsOutsideEditorChanges
) => void,
onProjectItemRenamedOutsideEditor: (
changes: ProjectItemRenamedOutsideEditorChanges
) => void,
ensureExtensionInstalled: (
options: EnsureExtensionInstalledOptions
) => Promise<void>,
Expand All @@ -73,6 +79,7 @@ export const processEditorFunctionCalls = async ({
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
relatedAiRequestId,
getRelatedAiRequestLastMessages,
ensureExtensionInstalled,
Expand Down Expand Up @@ -187,6 +194,7 @@ export const processEditorFunctionCalls = async ({
onInstancesModifiedOutsideEditor,
onObjectsModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
ensureExtensionInstalled,
onWillInstallExtension,
onExtensionInstalled,
Expand Down
62 changes: 62 additions & 0 deletions newIDE/app/src/EditorFunctions/EditorFunctions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('editorFunctions', () => {
onInstancesModifiedOutsideEditor: jest.fn(),
onObjectGroupsModifiedOutsideEditor: jest.fn(),
onSceneEventsModifiedOutsideEditor: jest.fn(),
onProjectItemRenamedOutsideEditor: jest.fn(),
toolOptions: {
includeEventsJson: true,
},
Expand Down Expand Up @@ -2521,6 +2522,67 @@ describe('editorFunctions', () => {
expect(project.getScaleMode()).toBe('nearest');
expect(project.getName()).toBe('My Game');
});

it('renames the scene when setting the "name" property', async () => {
const onProjectItemRenamedOutsideEditor: JestMockFn<any, any> = jest.fn();
const wasFirstScene = project.getFirstLayout() === 'TestScene';

const result = await editorFunctions.change_scene_properties_layers_effects_groups.launchFunction(
{
...makeFakeLaunchFunctionOptionsWithProject(project),
onProjectItemRenamedOutsideEditor,
args: {
scene_name: 'TestScene',
changed_properties: [
{ property_name: 'name', new_value: 'GameScene' },
],
},
}
);

expect(result.success).toBe(true);
expect(result.message).toContain(
'Renamed scene "TestScene" to "GameScene"'
);

// The scene is actually renamed in the project.
expect(project.hasLayoutNamed('TestScene')).toBe(false);
expect(project.hasLayoutNamed('GameScene')).toBe(true);
// The kept layout pointer is the same one.
expect(testScene.getName()).toBe('GameScene');
if (wasFirstScene) {
expect(project.getFirstLayout()).toBe('GameScene');
}

// The editor is notified so open tabs can be kept and updated.
expect(onProjectItemRenamedOutsideEditor).toHaveBeenCalledWith({
kind: 'scene',
oldName: 'TestScene',
newName: 'GameScene',
});
});

it('does nothing when renaming a scene to its current name', async () => {
const onProjectItemRenamedOutsideEditor: JestMockFn<any, any> = jest.fn();

const result = await editorFunctions.change_scene_properties_layers_effects_groups.launchFunction(
{
...makeFakeLaunchFunctionOptionsWithProject(project),
onProjectItemRenamedOutsideEditor,
args: {
scene_name: 'TestScene',
changed_properties: [
{ property_name: 'name', new_value: 'TestScene' },
],
},
}
);

expect(result.success).toBe(true);
expect(result.message).toContain('Scene already named "TestScene".');
expect(project.hasLayoutNamed('TestScene')).toBe(true);
expect(onProjectItemRenamedOutsideEditor).not.toHaveBeenCalled();
});
});

describe('object groups behave like objects', () => {
Expand Down
28 changes: 28 additions & 0 deletions newIDE/app/src/EditorFunctions/OutsideEditorChanges.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// @flow

export type SceneEventsOutsideEditorChanges = {|
scene: gdLayout,
newOrChangedAiGeneratedEventIds: Set<string>,
|};

export type InstancesOutsideEditorChanges = {|
scene: gdLayout,
|};

export type ObjectsOutsideEditorChanges = {|
scene: gdLayout,
isNewObjectTypeUsed: boolean,
|};

export type ObjectGroupsOutsideEditorChanges = {|
scene: gdLayout,
|};

// Only scenes are renamed outside the editor for now; extend as needed.
export type RenamableProjectItemKind = 'scene';

export type ProjectItemRenamedOutsideEditorChanges = {|
kind: RenamableProjectItemKind,
oldName: string,
newName: string,
|};
59 changes: 39 additions & 20 deletions newIDE/app/src/EditorFunctions/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// @flow
import * as React from 'react';
import { getInstancesInLayoutForLayer } from '../Utils/Layout';
import {
getInstancesInLayoutForLayer,
renameLayoutInProject,
} from '../Utils/Layout';
import { mapFor, mapVector } from '../Utils/MapFor';
import { SafeExtractor } from '../Utils/SafeExtractor';
import {
Expand Down Expand Up @@ -37,6 +40,13 @@ import { retryIfFailed } from '../Utils/RetryIfFailed';
import newNameGenerator from '../Utils/NewNameGenerator';
import getObjectByName from '../Utils/GetObjectByName';
import { getAllVisibleBehaviorNames } from '../Utils/Behavior';
import type {
SceneEventsOutsideEditorChanges,
InstancesOutsideEditorChanges,
ObjectsOutsideEditorChanges,
ObjectGroupsOutsideEditorChanges,
ProjectItemRenamedOutsideEditorChanges,
} from './OutsideEditorChanges';
Comment thread
ClementPasteau marked this conversation as resolved.
import { type AssetShortHeader } from '../Utils/GDevelopServices/Asset';
import { type ExampleShortHeader } from '../Utils/GDevelopServices/Example';
import { swapAsset } from '../AssetStore/AssetSwapper';
Expand Down Expand Up @@ -226,24 +236,6 @@ export type EditorCallbacks = {|
|}>,
|};

export type SceneEventsOutsideEditorChanges = {|
scene: gdLayout,
newOrChangedAiGeneratedEventIds: Set<string>,
|};

export type InstancesOutsideEditorChanges = {|
scene: gdLayout,
|};

export type ObjectsOutsideEditorChanges = {|
scene: gdLayout,
isNewObjectTypeUsed: boolean,
|};

export type ObjectGroupsOutsideEditorChanges = {|
scene: gdLayout,
|};

export type ToolOptions = {
includeEventsJson?: boolean,
...
Expand Down Expand Up @@ -288,6 +280,9 @@ type LaunchFunctionOptionsWithoutProject = {|
onObjectGroupsModifiedOutsideEditor: (
changes: ObjectGroupsOutsideEditorChanges
) => void,
onProjectItemRenamedOutsideEditor: (
changes: ProjectItemRenamedOutsideEditorChanges
) => void,
ensureExtensionInstalled: (
options: EnsureExtensionInstalledOptions
) => Promise<void>,
Expand Down Expand Up @@ -4879,6 +4874,7 @@ const inspectScenePropertiesLayersEffects: EditorFunction = {
success: true,
propertiesLayersEffectsForSceneNamed: scene.getName(),
properties: {
name: scene.getName(),
backgroundColor: rgbColorToHex(
scene.getBackgroundColorRed(),
scene.getBackgroundColorGreen(),
Expand Down Expand Up @@ -5034,6 +5030,7 @@ const changeScenePropertiesLayersEffectsGroups: EditorFunction = {
args,
onInstancesModifiedOutsideEditor,
onObjectGroupsModifiedOutsideEditor,
onProjectItemRenamedOutsideEditor,
}) => {
const scene_name = extractRequiredString(args, 'scene_name');

Expand Down Expand Up @@ -5081,7 +5078,29 @@ const changeScenePropertiesLayersEffectsGroups: EditorFunction = {
return;
}

if (isFuzzyMatch(propertyName, 'backgroundColor')) {
if (isFuzzyMatch(propertyName, 'name')) {
const oldName = scene.getName();
if (newValue === oldName) {
changes.push(`Scene already named "${newValue}".`);
return;
}

const newSceneName = newNameGenerator(
gd.Project.getSafeName(newValue),
tentativeNewName => project.hasLayoutNamed(tentativeNewName)
);

renameLayoutInProject(project, oldName, newSceneName);
onProjectItemRenamedOutsideEditor({
kind: 'scene',
oldName,
newName: newSceneName,
});

changes.push(
`Renamed scene "${oldName}" to "${newSceneName}" (events and references updated).`
);
} else if (isFuzzyMatch(propertyName, 'backgroundColor')) {
const colorAsRgb = hexNumberToRGBArray(rgbOrHexToHexNumber(newValue));
scene.setBackgroundColor(colorAsRgb[0], colorAsRgb[1], colorAsRgb[2]);
changes.push(
Expand Down
Loading
Loading