Trigger an hot-reload when an effect is added.

This commit is contained in:
Davy Hélard
2025-07-08 13:15:23 +02:00
parent ac4a500fff
commit 794b74a90c
13 changed files with 110 additions and 44 deletions

View File

@@ -388,16 +388,6 @@ const Effect = React.forwardRef(
}
);
type Props = {|
project: gdProject,
resourceManagementProps: ResourceManagementProps,
effectsContainer: gdEffectsContainer,
onEffectsUpdated: () => void,
onEffectsRenamed: (oldName: string, newName: string) => void,
target: 'object' | 'layer',
layerRenderingType: string,
|};
export const getEnumeratedEffectMetadata = (
allEffectDescriptions: Array<EnumeratedEffectMetadata>,
effectType: string
@@ -468,12 +458,14 @@ export const useManageEffects = ({
effectsContainer,
project,
onEffectsUpdated,
onEffectAdded,
onUpdate,
target,
}: {|
effectsContainer: gdEffectsContainer,
project: gdProject,
onEffectsUpdated: () => void,
onEffectAdded: () => void,
onUpdate: () => void,
target: 'object' | 'layer',
|}): UseManageEffectsState => {
@@ -558,8 +550,15 @@ export const useManageEffects = ({
onUpdate();
onEffectsUpdated();
setJustAddedEffectName(newName);
onEffectAdded();
},
[chooseEffectType, effectsContainer, onUpdate, onEffectsUpdated]
[
chooseEffectType,
effectsContainer,
onUpdate,
onEffectsUpdated,
onEffectAdded,
]
);
const addEffect = addCreateBadgePreHookIfNotClaimed(
@@ -800,6 +799,17 @@ export const useManageEffects = ({
};
};
type Props = {|
project: gdProject,
resourceManagementProps: ResourceManagementProps,
effectsContainer: gdEffectsContainer,
onEffectsUpdated: () => void,
onEffectsRenamed: (oldName: string, newName: string) => void,
onEffectAdded: () => void,
target: 'object' | 'layer',
layerRenderingType: string,
|};
/**
* Display a list of effects and allow to add/remove/edit them.
*
@@ -810,6 +820,7 @@ export default function EffectsList(props: Props) {
effectsContainer,
onEffectsUpdated,
onEffectsRenamed,
onEffectAdded,
project,
target,
} = props;
@@ -838,6 +849,7 @@ export default function EffectsList(props: Props) {
effectsContainer,
project,
onEffectsUpdated,
onEffectAdded,
onUpdate: forceUpdate,
target,
});

View File

@@ -41,7 +41,7 @@ type Props = {|
initialTab: 'properties' | 'effects',
onCancel: () => void,
onApply: () => void,
onApply: (hasAnyEffectBeenAdded: boolean) => void,
// Preview:
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
@@ -94,6 +94,7 @@ const LayerEditorDialog = ({
},
[layer, initialInstances]
);
const [hasAnyEffectBeenAdded, setAnyEffectBeenAdded] = React.useState(false);
const onChangeCamera3DFieldOfView = React.useCallback(
value => {
@@ -199,12 +200,12 @@ const LayerEditorDialog = ({
<DialogPrimaryButton
label={<Trans>Apply</Trans>}
primary
onClick={onApply}
onClick={() => onApply(hasAnyEffectBeenAdded)}
key={'Apply'}
/>,
]}
onRequestClose={onCancelChanges}
onApply={onApply}
onApply={() => onApply(hasAnyEffectBeenAdded)}
fullHeight
flexColumnBody
fixedContent={
@@ -483,6 +484,9 @@ const LayerEditorDialog = ({
forceUpdate(); /*Force update to ensure dialog is properly positioned*/
notifyOfChange();
}}
onEffectAdded={() => {
setAnyEffectBeenAdded(true);
}}
/>
)}
</Dialog>

View File

@@ -51,7 +51,7 @@ type Props = {|
isLocked: boolean,
onChangeLockState: boolean => void,
effectsCount: number,
onEditEffects: () => void,
onEditLayerEffects: () => void,
onEdit: () => void,
width: number,
|};
@@ -68,7 +68,7 @@ const LayerRow = ({
isLocked,
onChangeLockState,
effectsCount,
onEditEffects,
onEditLayerEffects,
onChangeVisibility,
onBeginDrag,
onDrop,
@@ -174,7 +174,7 @@ const LayerRow = ({
},
{
label: i18n._(t`Edit effects (${effectsCount})`),
click: onEditEffects,
click: onEditLayerEffects,
},
{
type: 'checkbox',
@@ -232,7 +232,7 @@ const LayerRow = ({
/>
<IconButton
size="small"
onClick={onEditEffects}
onClick={onEditLayerEffects}
tooltip={t`Edit effects (${effectsCount})`}
>
<Badge badgeContent={effectsCount} color="primary">

View File

@@ -36,7 +36,7 @@ type LayersListBodyProps = {|
unsavedChanges?: ?UnsavedChanges,
onRemoveLayer: (layerName: string, cb: (done: boolean) => void) => void,
onLayerRenamed: () => void,
onEditEffects: (layer: ?gdLayer) => void,
onEditLayerEffects: (layer: ?gdLayer) => void,
onEdit: (layer: ?gdLayer) => void,
onLayersModified: () => void,
width: number,
@@ -57,7 +57,7 @@ const LayersListBody = ({
eventsFunctionsExtension,
eventsBasedObject,
layersContainer,
onEditEffects,
onEditLayerEffects,
onEdit,
width,
onLayerRenamed,
@@ -114,7 +114,7 @@ const LayersListBody = ({
onSelect={() => onSelectLayer(layerName)}
nameError={nameErrors[layerName]}
effectsCount={getEffectsCount(project.getCurrentPlatform(), layer)}
onEditEffects={() => onEditEffects(layer)}
onEditLayerEffects={() => onEditLayerEffects(layer)}
onEdit={() => onEdit(layer)}
onBeginDrag={() => {
draggedLayerIndexRef.current = i;
@@ -311,7 +311,7 @@ const LayersList = React.forwardRef<Props, LayersListInterface>(
eventsFunctionsExtension={eventsFunctionsExtension}
eventsBasedObject={eventsBasedObject}
layersContainer={props.layersContainer}
onEditEffects={props.onEditLayerEffects}
onEditLayerEffects={props.onEditLayerEffects}
onEdit={props.onEditLayer}
onLayersModified={props.onLayersModified}
onRemoveLayer={props.onRemoveLayer}

View File

@@ -201,6 +201,7 @@ export type RenderEditorContainerProps = {|
eventBasedObject: gdEventsBasedObject,
variant: gdEventsBasedObjectVariant
) => void,
onEffectAdded: () => void,
|};
export type RenderEditorContainerPropsWithRef = {|

View File

@@ -328,6 +328,7 @@ export class CustomObjectEditorContainer extends React.Component<RenderEditorCon
onDeleteEventsBasedObjectVariant={
this.props.onDeleteEventsBasedObjectVariant
}
onEffectAdded={this.props.onEffectAdded}
/>
</div>
);

View File

@@ -356,6 +356,7 @@ export class ExternalLayoutEditorContainer extends React.Component<
onDeleteEventsBasedObjectVariant={
this.props.onDeleteEventsBasedObjectVariant
}
onEffectAdded={this.props.onEffectAdded}
/>
)}
{!layout && (

View File

@@ -234,6 +234,7 @@ export class SceneEditorContainer extends React.Component<RenderEditorContainerP
onDeleteEventsBasedObjectVariant={
this.props.onDeleteEventsBasedObjectVariant
}
onEffectAdded={this.props.onEffectAdded}
onObjectEdited={objectWithContext =>
this.props.onSceneObjectEdited(layout, objectWithContext)
}

View File

@@ -1524,6 +1524,16 @@ const MainFrame = (props: Props) => {
[hotReloadInGameEditorIfNeeded]
);
const onEffectAdded = React.useCallback(
() => {
// Ensure the effect implementation is exported.
hotReloadInGameEditorIfNeeded({
projectDataOnlyExport: false,
});
},
[hotReloadInGameEditorIfNeeded]
);
const renameLayout = (oldName: string, newName: string) => {
const { currentProject } = state;
const { i18n } = props;
@@ -4339,6 +4349,7 @@ const MainFrame = (props: Props) => {
onSceneEventsModifiedOutsideEditor: onSceneEventsModifiedOutsideEditor,
onExtensionInstalled: onExtensionInstalled,
onEventBasedObjectTypeChanged: onEventBasedObjectTypeChanged,
onEffectAdded: onEffectAdded,
gamesList,
gamesPlatformFrameTools,
})}

View File

@@ -78,6 +78,8 @@ export const styles = {
},
};
const noop = () => {};
const behaviorsHelpLink = getHelpLink('/behaviors');
const effectsHelpLink = getHelpLink('/objects/effects');
const objectVariablesHelpLink = getHelpLink(
@@ -367,6 +369,7 @@ export const CompactObjectPropertiesEditor = ({
effectsContainer,
project,
onEffectsUpdated: forceUpdate,
onEffectAdded: noop,
onUpdate: forceUpdate,
target: 'object',
});

View File

@@ -38,7 +38,10 @@ type Props = {|
open: boolean,
object: ?gdObject,
onApply: (hasResourceChanged: boolean) => void,
onApply: (
hasResourceChanged: boolean,
hasAnyEffectBeenAdded: boolean
) => void,
onCancel: () => void,
// Object renaming:
@@ -141,6 +144,7 @@ const InnerDialog = (props: InnerDialogProps) => {
}),
[resourceManagementProps]
);
const [hasAnyEffectBeenAdded, setAnyEffectBeenAdded] = React.useState(false);
// Don't use a memo for this because metadata from custom objects are built
// from event-based object when extensions are refreshed after an extension
@@ -154,7 +158,7 @@ const InnerDialog = (props: InnerDialogProps) => {
props.editorComponent;
const onApply = async () => {
props.onApply(hasResourceChanged);
props.onApply(hasResourceChanged, hasAnyEffectBeenAdded);
const initialInstances =
(layout && layout.getInitialInstances()) ||
@@ -412,6 +416,9 @@ const InnerDialog = (props: InnerDialogProps) => {
forceUpdate(); /*Force update to ensure dialog is properly positioned*/
notifyOfChange();
}}
onEffectAdded={() => {
setAnyEffectBeenAdded(true);
}}
/>
)}
</Dialog>

View File

@@ -174,6 +174,7 @@ type Props = {|
eventBasedObject: gdEventsBasedObject,
variant: gdEventsBasedObjectVariant
) => void,
onEffectAdded: () => void,
// Preview:
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
@@ -1263,23 +1264,32 @@ export default class SceneEditor extends React.Component<Props, State> {
this.forceUpdatePropertiesEditor();
};
_onLayersModified = () => {
if (this.props.project.areEffectsHiddenInEditor()) {
return;
}
const { previewDebuggerServer, layersContainer } = this.props;
if (previewDebuggerServer) {
previewDebuggerServer.getExistingDebuggerIds().forEach(debuggerId => {
previewDebuggerServer.sendMessage(debuggerId, {
command: 'hotReloadLayers',
payload: {
layers: mapFor(0, layersContainer.getLayersCount(), i => {
const layer = layersContainer.getLayerAt(i);
return serializeToJSObject(layer);
}),
},
_onLayersModified = (hasAnyEffectBeenAdded: boolean) => {
const {
previewDebuggerServer,
layersContainer,
onEffectAdded,
} = this.props;
if (hasAnyEffectBeenAdded) {
// This triggers a full hot-reload. We don't need to reload layers specifically.
onEffectAdded();
} else {
if (this.props.project.areEffectsHiddenInEditor()) {
return;
}
if (previewDebuggerServer) {
previewDebuggerServer.getExistingDebuggerIds().forEach(debuggerId => {
previewDebuggerServer.sendMessage(debuggerId, {
command: 'hotReloadLayers',
payload: {
layers: mapFor(0, layersContainer.getLayersCount(), i => {
const layer = layersContainer.getLayerAt(i);
return serializeToJSObject(layer);
}),
},
});
});
});
}
}
};
@@ -2463,7 +2473,7 @@ export default class SceneEditor extends React.Component<Props, State> {
selectedObjectFolderOrObjectsWithContext
}
onLayerRenamed={this._onLayerRenamed}
onLayersModified={this._onLayersModified}
onLayersModified={() => this._onLayersModified(false)}
onLayersVisibilityInEditorChanged={
this._onLayersVisibilityInEditorChanged
}
@@ -2598,13 +2608,19 @@ export default class SceneEditor extends React.Component<Props, State> {
onRename={newName => {
this._onRenameEditedObject(newName);
}}
onApply={(hasResourceChanged: boolean) => {
onApply={(
hasResourceChanged: boolean,
hasAnyEffectBeenAdded: boolean
) => {
if (editedObjectWithContext) {
this._onObjectEdited(
editedObjectWithContext,
hasResourceChanged
);
}
if (hasAnyEffectBeenAdded) {
this.props.onEffectAdded();
}
this.editObject(null);
}}
hotReloadPreviewButtonProps={
@@ -2738,8 +2754,8 @@ export default class SceneEditor extends React.Component<Props, State> {
layer={this.state.editedLayer}
initialInstances={initialInstances}
initialTab={this.state.editedLayerInitialTab}
onApply={() => {
this._onLayersModified();
onApply={(hasAnyEffectBeenAdded: boolean) => {
this._onLayersModified(hasAnyEffectBeenAdded);
this.setState({
editedLayer: null,
});

View File

@@ -21,6 +21,7 @@ export const withSomeEffectsForAMixedLayer = () => (
effectsContainer={testProject.layerWithEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -37,6 +38,7 @@ export const withSomeEffectsForA2DLayer = () => (
effectsContainer={testProject.layerWith2DEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -56,6 +58,7 @@ export const withSomeEffectsForA3DLayer = () => (
effectsContainer={testProject.layerWith3DEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -72,6 +75,7 @@ export const withSomeEffectsForAnObject = () => (
effectsContainer={testProject.spriteObjectWithEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -88,6 +92,7 @@ export const withAnEffectWithoutEffectTypeForALayer = () => (
effectsContainer={testProject.layerWithEffectWithoutEffectType.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -104,6 +109,7 @@ export const withoutEffectsForAMixedLayer = () => (
effectsContainer={testProject.layerWithoutEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -129,6 +135,7 @@ export const withoutEffectsForA2DLayer = () => (
effectsContainer={testProject.layerWithoutEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -154,6 +161,7 @@ export const withoutEffectsForA3DLayer = () => (
effectsContainer={testProject.layerWithoutEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>
@@ -170,6 +178,7 @@ export const withoutEffectsForAnObject = () => (
effectsContainer={testProject.spriteObjectWithoutEffects.getEffects()}
onEffectsRenamed={action('effects renamed')}
onEffectsUpdated={action('effects updated')}
onEffectAdded={action('effect added')}
/>
</FixedHeightFlexContainer>
</DragAndDropContextProvider>