Skip to content

Commit

Permalink
Add a drop-down menu action to extract selected instances as an exter…
Browse files Browse the repository at this point in the history
…nal layout (#6561)
  • Loading branch information
D8H committed May 14, 2024
1 parent 92b9203 commit f8bd3fc
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 1 deletion.
2 changes: 2 additions & 0 deletions newIDE/app/src/MainFrame/EditorContainers/BaseEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export type RenderEditorContainerProps = {|

// Object editing
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,

onExtractAsExternalLayout: (name: string) => void,
|};

export type RenderEditorContainerPropsWithRef = {|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
isActive={isActive}
canInstallPrivateAsset={this.props.canInstallPrivateAsset}
openBehaviorEvents={this.props.openBehaviorEvents}
onExtractAsExternalLayout={this.props.onExtractAsExternalLayout}
/>
)}
{!layout && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
isActive={isActive}
hotReloadPreviewButtonProps={this.props.hotReloadPreviewButtonProps}
openBehaviorEvents={this.props.openBehaviorEvents}
onExtractAsExternalLayout={this.props.onExtractAsExternalLayout}
/>
);
}
Expand Down
5 changes: 5 additions & 0 deletions newIDE/app/src/MainFrame/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1880,6 +1880,10 @@ const MainFrame = (props: Props) => {
}
};
const onExtractAsExternalLayout = (name: string) => {
openExternalLayout(name);
};
const _onProjectItemModified = () => {
if (unsavedChanges) unsavedChanges.triggerUnsavedChanges();
forceUpdate();
Expand Down Expand Up @@ -3285,6 +3289,7 @@ const MainFrame = (props: Props) => {
cb(true);
},
openBehaviorEvents: openBehaviorEvents,
onExtractAsExternalLayout: onExtractAsExternalLayout,
})}
</ErrorBoundary>
</CommandsContextScopedProvider>
Expand Down
2 changes: 1 addition & 1 deletion newIDE/app/src/ProjectManager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ const ProjectManager = React.forwardRef<Props, ProjectManagerInterface>(
return [
new PlaceHolderTreeViewItem(
externalLayoutEmptyPlaceholderId,
i18n._(t`Start by adding new a external layout.`)
i18n._(t`Start by adding a new external layout.`)
),
];
}
Expand Down
73 changes: 73 additions & 0 deletions newIDE/app/src/SceneEditor/ExtractAsExternalLayoutDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// @flow
import { Trans } from '@lingui/macro';
import * as React from 'react';
import FlatButton from '../UI/FlatButton';
import Text from '../UI/Text';
import TextField from '../UI/TextField';
import { ColumnStackLayout } from '../UI/Layout';
import Dialog, { DialogPrimaryButton } from '../UI/Dialog';
import HelpButton from '../UI/HelpButton';

type Props = {|
suggestedName: string,
onApply: (chosenName: string) => void,
onCancel: () => void,
|};

export default function ExtractAsExternalLayoutDialog({
suggestedName,
onApply,
onCancel,
}: Props) {
const [chosenName, setChosenName] = React.useState<string>(suggestedName);

const apply = React.useCallback(() => onApply(suggestedName), [
onApply,
suggestedName,
]);

return (
<Dialog
title={<Trans>Extract as an external layout</Trans>}
actions={[
<FlatButton
key="cancel"
label={<Trans>Cancel</Trans>}
primary={false}
onClick={onCancel}
/>,
<DialogPrimaryButton
key="apply"
label={<Trans>Move instances</Trans>}
primary={true}
onClick={apply}
/>,
]}
secondaryActions={[
<HelpButton
helpPagePath="/interface/scene-editor/external-layouts/"
key="help"
/>,
]}
onRequestClose={onCancel}
onApply={apply}
open
maxWidth="sm"
exceptionallyStillAllowRenderingInstancesEditors
>
<ColumnStackLayout noMargin>
<Text>
<Trans>
Selected instances will be moved to a new external layout.
</Trans>
</Text>
<TextField
floatingLabelText={<Trans>External layout name</Trans>}
fullWidth
value={chosenName}
onChange={(e, value) => setChosenName(value)}
/>
</ColumnStackLayout>
</Dialog>
);
}
59 changes: 59 additions & 0 deletions newIDE/app/src/SceneEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ObjectGroupEditorDialog from '../ObjectGroupEditor/ObjectGroupEditorDialo
import InstancesSelection from '../InstancesEditor/InstancesSelection';
import SetupGridDialog from './SetupGridDialog';
import ScenePropertiesDialog from './ScenePropertiesDialog';
import ExtractAsExternalLayoutDialog from './ExtractAsExternalLayoutDialog';
import { type ObjectEditorTab } from '../ObjectEditor/ObjectEditorDialog';
import MosaicEditorsDisplayToolbar from './MosaicEditorsDisplay/Toolbar';
import SwipeableDrawerEditorsDisplayToolbar from './SwipeableDrawerEditorsDisplay/Toolbar';
Expand Down Expand Up @@ -70,6 +71,7 @@ import {
registerOnResourceExternallyChangedCallback,
unregisterOnResourceExternallyChangedCallback,
} from '../MainFrame/ResourcesWatcher';
import { unserializeFromJSObject } from '../Utils/Serializer';

const gd: libGDevelop = global.gd;

Expand Down Expand Up @@ -106,6 +108,7 @@ type Props = {|
unsavedChanges?: ?UnsavedChanges,
canInstallPrivateAsset: () => boolean,
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,
onExtractAsExternalLayout: (name: string) => void,

// Preview:
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
Expand All @@ -128,6 +131,7 @@ type State = {|
variablesEditedInstance: ?gdInitialInstance,
newObjectInstanceSceneCoordinates: ?[number, number],
invisibleLayerOnWhichInstancesHaveJustBeenAdded: string | null,
extractAsExternalLayoutDialogOpen: boolean,

editedGroup: ?gdObjectGroup,

Expand Down Expand Up @@ -177,6 +181,7 @@ export default class SceneEditor extends React.Component<Props, State> {
variablesEditedInstance: null,
newObjectInstanceSceneCoordinates: null,
editedGroup: null,
extractAsExternalLayoutDialogOpen: false,

instancesEditorSettings: props.getInitialInstancesEditorSettings(),
history: getHistoryInitialState(props.initialInstances, {
Expand Down Expand Up @@ -1321,6 +1326,11 @@ export default class SceneEditor extends React.Component<Props, State> {
},
},
{ type: 'separator' },
{
label: i18n._(t`Extract as an external layout`),
click: () => this.setState({ extractAsExternalLayoutDialogOpen: true }),
enabled: hasSelectedInstances,
},
{
label: i18n._(t`Show/Hide instance properties`),
click: () => this.toggleProperties(),
Expand Down Expand Up @@ -1551,6 +1561,39 @@ export default class SceneEditor extends React.Component<Props, State> {
this.forceUpdatePropertiesEditor();
};

extractAsExternalLayout = (chosenName: string) => {
const { project, layout, onExtractAsExternalLayout } = this.props;

const serializedSelection = this.instancesSelection
.getSelectedInstances()
.map(instance => serializeToJSObject(instance));

const newName = newNameGenerator(chosenName, name =>
project.hasExternalLayoutNamed(name)
);
const newExternalLayout = project.insertNewExternalLayout(
newName,
project.getExternalLayoutsCount()
);
newExternalLayout.setAssociatedLayout(layout.getName());

for (const serializedInstance of serializedSelection) {
const instance = new gd.InitialInstance();
unserializeFromJSObject(instance, serializedInstance);
newExternalLayout
.getInitialInstances()
.insertInitialInstance(instance)
.resetPersistentUuid();
instance.delete();
}

this.deleteSelection();

this.setState({ extractAsExternalLayoutDialogOpen: false });

onExtractAsExternalLayout(newName);
};

onSelectAllInstancesOfObjectInLayout = (objectName: string) => {
const { initialInstances } = this.props;
const instancesToSelect = getInstancesInLayoutForObject(
Expand Down Expand Up @@ -1970,6 +2013,22 @@ export default class SceneEditor extends React.Component<Props, State> {
<I18n>
{({ i18n }) => (
<React.Fragment>
{this.state.extractAsExternalLayoutDialogOpen && (
<ExtractAsExternalLayoutDialog
suggestedName={newNameGenerator(
i18n._(t`${layout.getName()} part`),
name => project.hasExternalLayoutNamed(name)
)}
onCancel={() =>
this.setState({
extractAsExternalLayoutDialogOpen: false,
})
}
onApply={chosenName =>
this.extractAsExternalLayout(chosenName)
}
/>
)}
<DismissableInfoBar
show={this.state.showAdditionalWorkInfoBar}
identifier={this.state.additionalWorkInfoBar.identifier}
Expand Down

0 comments on commit f8bd3fc

Please sign in to comment.