Compare commits

...

3 Commits

Author SHA1 Message Date
Cursor Agent
bb95175f24 Remove package-lock.json file
Co-authored-by: florian <florian@gdevelop.io>
2025-07-13 20:00:40 +00:00
Cursor Agent
ef9e98e3a8 Refactor editor tabs management in EditorsPane and MainFrame
Co-authored-by: florian <florian@gdevelop.io>
2025-07-13 19:22:40 +00:00
Cursor Agent
88247e02ac Refactor MainFrame to use new EditorsPane component
Co-authored-by: florian <florian@gdevelop.io>
2025-07-13 19:00:10 +00:00
2 changed files with 561 additions and 302 deletions

View File

@@ -0,0 +1,478 @@
// @flow
import * as React from 'react';
import TabsTitlebar from './TabsTitlebar';
import Toolbar, { type ToolbarInterface } from './Toolbar';
import { TabContentContainer } from '../UI/ClosableTabs';
import { DraggableEditorTabs } from './EditorTabs/DraggableEditorTabs';
import CommandsContextScopedProvider from '../CommandPalette/CommandsScopedContext';
import ErrorBoundary, {
getEditorErrorBoundaryProps,
} from '../UI/ErrorBoundary';
import {
getEditors,
getCurrentTabIndex,
getCurrentTab,
type EditorTabsState,
type EditorTab,
hasEditorTabOpenedWithKey,
changeCurrentTab,
closeEditorTab,
closeOtherEditorTabs,
closeAllEditorTabs,
moveTabToTheRightOfHoveredTab,
saveUiSettings,
} from './EditorTabs/EditorTabsHandler';
import type { PreviewState } from './PreviewState';
import type {
RenderEditorContainerPropsWithRef,
SceneEventsOutsideEditorChanges,
} from './EditorContainers/BaseEditor';
import type { ResourceManagementProps } from '../ResourcesList/ResourceSource';
import type { HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewButton';
import type { GamesList } from '../GameDashboard/UseGamesList';
import type { GamesPlatformFrameTools } from './EditorContainers/HomePage/PlaySection/UseGamesPlatformFrame';
import type { FileMetadata } from '../ProjectsStorage';
import UnsavedChangesContext from './UnsavedChangesContext';
import type { InAppTutorialOrchestrator } from '../InAppTutorial/InAppTutorialOrchestrator';
import type { VersionHistoryPanelStatus } from '../VersionHistory/UseVersionHistory';
const gd: libGDevelop = global.gd;
type Props = {|
editorTabs: EditorTabsState,
currentProject: ?gdProject,
currentFileMetadata: ?FileMetadata,
tabsTitleBarAndEditorToolbarHidden: boolean,
setTabsTitleBarAndEditorToolbarHidden: (hidden: boolean) => void,
canSave: boolean,
isSavingProject: boolean,
isSharingEnabled: boolean,
hasPreviewsRunning: boolean,
previewState: PreviewState,
checkedOutVersionStatus: VersionHistoryPanelStatus,
canDoNetworkPreview: boolean,
gamesPlatformFrameTools: GamesPlatformFrameTools,
// Callbacks from MainFrame
toggleProjectManager: () => void,
saveProject: () => Promise<void>,
openShareDialog: (tab?: string) => void,
launchDebuggerAndPreview: () => void,
launchNewPreview: (options?: {| networkPreview?: boolean |}) => void,
launchNetworkPreview: () => void,
launchHotReloadPreview: () => void,
launchPreviewWithDiagnosticReport: () => void,
setPreviewOverride: (override: {|
isPreviewOverriden: boolean,
overridenPreviewLayoutName: ?string,
overridenPreviewExternalLayoutName: ?string,
|}) => void,
openVersionHistoryPanel: () => void,
onQuitVersionHistory: () => void,
openAskAi: () => void,
getStorageProvider: () => any,
setPreviewedLayout: (layoutName: ?string) => void,
openExternalEvents: (name: string) => void,
openLayout: (name: string, options?: any) => void,
openTemplateFromTutorial: any => void,
openTemplateFromCourseChapter: any => void,
previewDebuggerServer: ?any,
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
resourceManagementProps: ResourceManagementProps,
onCreateEventsFunction: any,
openInstructionOrExpression: any,
onOpenCustomObjectEditor: any => void,
onRenamedEventsBasedObject: any => void,
onDeletedEventsBasedObject: any => void,
openObjectEvents: any,
canOpen: boolean,
openOpenFromStorageProviderDialog: () => void,
openFromFileMetadataWithStorageProvider: any => void,
openNewProjectDialog: () => void,
openProjectManager: (open: boolean) => void,
askToCloseProject: () => Promise<boolean>,
closeProject: () => Promise<void>,
onSelectExampleShortHeader: any => void,
onSelectPrivateGameTemplateListingData: any => void,
createEmptyProject: any,
createProjectFromExample: any,
onOpenProfileDialog: () => void,
openLanguageDialog: (open: boolean) => void,
openPreferencesDialog: (open: boolean) => void,
openAboutDialog: (open: boolean) => void,
selectInAppTutorial: any => void,
eventsFunctionsExtensionsState: any,
isProjectClosedSoAvoidReloadingExtensions: boolean,
renameResourcesInProject: (project: gdProject, renames: {[string]: string}) => void,
openBehaviorEvents: any,
onExtractAsExternalLayout: any,
onOpenEventBasedObjectEditor: any,
onOpenEventBasedObjectVariantEditor: any,
deleteEventsBasedObjectVariant: any,
onEventsBasedObjectChildrenEdited: any,
onSceneObjectEdited: any,
onSceneObjectsDeleted: any,
onSceneEventsModifiedOutsideEditor: any,
onExtensionInstalled: any,
gamesList: GamesList,
inAppTutorialOrchestratorRef: {| current: ?InAppTutorialOrchestrator |},
setEditorTabs: (editorTabs: EditorTabsState) => void,
|};
const EditorsPane = React.forwardRef<Props, ToolbarInterface>((props, ref) => {
const {
editorTabs,
currentProject,
currentFileMetadata,
tabsTitleBarAndEditorToolbarHidden,
setTabsTitleBarAndEditorToolbarHidden,
canSave,
isSavingProject,
isSharingEnabled,
hasPreviewsRunning,
previewState,
checkedOutVersionStatus,
canDoNetworkPreview,
gamesPlatformFrameTools,
toggleProjectManager,
saveProject,
openShareDialog,
launchDebuggerAndPreview,
launchNewPreview,
launchNetworkPreview,
launchHotReloadPreview,
launchPreviewWithDiagnosticReport,
setPreviewOverride,
openVersionHistoryPanel,
onQuitVersionHistory,
openAskAi,
getStorageProvider,
setPreviewedLayout,
openExternalEvents,
openLayout,
openTemplateFromTutorial,
openTemplateFromCourseChapter,
previewDebuggerServer,
hotReloadPreviewButtonProps,
resourceManagementProps,
onCreateEventsFunction,
openInstructionOrExpression,
onOpenCustomObjectEditor,
onRenamedEventsBasedObject,
onDeletedEventsBasedObject,
openObjectEvents,
canOpen,
openOpenFromStorageProviderDialog,
openFromFileMetadataWithStorageProvider,
openNewProjectDialog,
openProjectManager,
askToCloseProject,
closeProject,
onSelectExampleShortHeader,
onSelectPrivateGameTemplateListingData,
createEmptyProject,
createProjectFromExample,
onOpenProfileDialog,
openLanguageDialog,
openPreferencesDialog,
openAboutDialog,
selectInAppTutorial,
eventsFunctionsExtensionsState,
isProjectClosedSoAvoidReloadingExtensions,
renameResourcesInProject,
openBehaviorEvents,
onExtractAsExternalLayout,
onOpenEventBasedObjectEditor,
onOpenEventBasedObjectVariantEditor,
deleteEventsBasedObjectVariant,
onEventsBasedObjectChildrenEdited,
onSceneObjectEdited,
onSceneObjectsDeleted,
onSceneEventsModifiedOutsideEditor,
onExtensionInstalled,
gamesList,
inAppTutorialOrchestratorRef,
setEditorTabs,
} = props;
const toolbarRef = React.useRef<?ToolbarInterface>(null);
const unsavedChanges = React.useContext(UnsavedChangesContext);
const hasAskAiOpened = hasEditorTabOpenedWithKey(editorTabs, 'ask-ai');
// Internal editor toolbar management
const setEditorToolbar = React.useCallback((editorToolbar: any, isCurrentTab: boolean = true) => {
if (!toolbarRef.current || !isCurrentTab) return;
toolbarRef.current.setEditorToolbar(editorToolbar);
}, []);
const updateToolbar = React.useCallback(() => {
const editorTab = getCurrentTab(editorTabs);
if (!editorTab || !editorTab.editorRef) {
setEditorToolbar(null);
return;
}
editorTab.editorRef.updateToolbar();
}, [editorTabs, setEditorToolbar]);
React.useEffect(() => {
updateToolbar();
}, [updateToolbar]);
// Tab management functions
const _onEditorTabActivated = React.useCallback((editorTab: EditorTab) => {
updateToolbar();
// Ensure the editors shown on the screen are updated. This is for
// example useful if global objects have been updated in another editor.
if (editorTab.editorRef) {
editorTab.editorRef.forceUpdateEditor();
}
}, [updateToolbar]);
const _onChangeEditorTab = React.useCallback((value: number) => {
const newEditorTabs = changeCurrentTab(editorTabs, value);
setEditorTabs(newEditorTabs);
_onEditorTabActivated(getCurrentTab(newEditorTabs));
}, [editorTabs, setEditorTabs, _onEditorTabActivated]);
const _onCloseEditorTab = React.useCallback((editorTab: EditorTab) => {
saveUiSettings(editorTabs);
setEditorTabs(closeEditorTab(editorTabs, editorTab));
}, [editorTabs, setEditorTabs]);
const _onCloseOtherEditorTabs = React.useCallback((editorTab: EditorTab) => {
saveUiSettings(editorTabs);
setEditorTabs(closeOtherEditorTabs(editorTabs, editorTab));
}, [editorTabs, setEditorTabs]);
const _onCloseAllEditorTabs = React.useCallback(() => {
saveUiSettings(editorTabs);
setEditorTabs(closeAllEditorTabs(editorTabs));
}, [editorTabs, setEditorTabs]);
const onDropEditorTab = React.useCallback((fromIndex: number, toHoveredIndex: number) => {
setEditorTabs(moveTabToTheRightOfHoveredTab(editorTabs, fromIndex, toHoveredIndex));
}, [editorTabs, setEditorTabs]);
// Expose toolbar interface methods
React.useImperativeHandle(ref, () => ({
setEditorToolbar,
}), [setEditorToolbar]);
return (
<>
<TabsTitlebar
hidden={tabsTitleBarAndEditorToolbarHidden}
toggleProjectManager={toggleProjectManager}
renderTabs={(onEditorTabHovered, onEditorTabClosing) => (
<DraggableEditorTabs
hideLabels={false}
editorTabs={editorTabs}
onClickTab={(id: number) => _onChangeEditorTab(id)}
onCloseTab={(editorTab: EditorTab) => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseEditorTab(editorTab);
}}
onCloseOtherTabs={(editorTab: EditorTab) => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseOtherEditorTabs(editorTab);
}}
onCloseAll={() => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseAllEditorTabs();
}}
onTabActivated={(editorTab: EditorTab) =>
_onEditorTabActivated(editorTab)
}
onDropTab={onDropEditorTab}
onHoverTab={(
editorTab: ?EditorTab,
options: {| isLabelTruncated: boolean |}
) => onEditorTabHovered(editorTab, options)}
/>
)}
hasAskAiOpened={hasAskAiOpened}
onOpenAskAi={openAskAi}
/>
<Toolbar
ref={toolbarRef}
hidden={tabsTitleBarAndEditorToolbarHidden}
showProjectButtons={
!['start page', 'debugger', 'ask-ai', null].includes(
getCurrentTab(editorTabs)
? getCurrentTab(editorTabs).key
: null
)
}
canSave={canSave}
onSave={saveProject}
openShareDialog={() =>
openShareDialog(/* leave the dialog decide which tab to open */)
}
isSharingEnabled={isSharingEnabled}
onOpenDebugger={launchDebuggerAndPreview}
hasPreviewsRunning={hasPreviewsRunning}
onPreviewWithoutHotReload={launchNewPreview}
onNetworkPreview={launchNetworkPreview}
onHotReloadPreview={launchHotReloadPreview}
onLaunchPreviewWithDiagnosticReport={launchPreviewWithDiagnosticReport}
canDoNetworkPreview={canDoNetworkPreview}
setPreviewOverride={setPreviewOverride}
isPreviewEnabled={
!!currentProject && currentProject.getLayoutsCount() > 0
}
previewState={previewState}
onOpenVersionHistory={openVersionHistoryPanel}
checkedOutVersionStatus={checkedOutVersionStatus}
onQuitVersionHistory={onQuitVersionHistory}
canQuitVersionHistory={!isSavingProject}
/>
{getEditors(editorTabs).map((editorTab, id) => {
const isCurrentTab = getCurrentTabIndex(editorTabs) === id;
const errorBoundaryProps = getEditorErrorBoundaryProps(editorTab.key);
return (
<TabContentContainer
key={editorTab.key}
active={isCurrentTab}
// Deactivate pointer events when the play tab is active, so the iframe
// can be interacted with.
removePointerEvents={gamesPlatformFrameTools.iframeVisible}
>
<CommandsContextScopedProvider active={isCurrentTab}>
<ErrorBoundary
componentTitle={errorBoundaryProps.componentTitle}
scope={errorBoundaryProps.scope}
>
{editorTab.renderEditorContainer({
isActive: isCurrentTab,
extraEditorProps: editorTab.extraEditorProps,
project: currentProject,
fileMetadata: currentFileMetadata,
storageProvider: getStorageProvider(),
ref: editorRef => (editorTab.editorRef = editorRef),
setToolbar: editorToolbar =>
setEditorToolbar(editorToolbar, isCurrentTab),
hideTabsTitleBarAndEditorToolbar: setTabsTitleBarAndEditorToolbarHidden,
projectItemName: editorTab.projectItemName,
setPreviewedLayout,
onOpenExternalEvents: openExternalEvents,
onOpenEvents: (sceneName: string) => {
openLayout(sceneName, {
openEventsEditor: true,
openSceneEditor: false,
focusWhenOpened: 'events',
});
},
onOpenLayout: openLayout,
onOpenTemplateFromTutorial: openTemplateFromTutorial,
onOpenTemplateFromCourseChapter: openTemplateFromCourseChapter,
previewDebuggerServer,
hotReloadPreviewButtonProps,
resourceManagementProps,
onSave: saveProject,
canSave,
onCreateEventsFunction,
openInstructionOrExpression,
onOpenCustomObjectEditor: onOpenCustomObjectEditor,
onRenamedEventsBasedObject: onRenamedEventsBasedObject,
onDeletedEventsBasedObject: onDeletedEventsBasedObject,
openObjectEvents,
unsavedChanges: unsavedChanges,
canOpen,
onChooseProject: () => openOpenFromStorageProviderDialog(),
onOpenRecentFile: openFromFileMetadataWithStorageProvider,
onOpenNewProjectSetupDialog: openNewProjectDialog,
onOpenProjectManager: () => openProjectManager(true),
onOpenVersionHistory: openVersionHistoryPanel,
askToCloseProject,
closeProject,
onSelectExampleShortHeader: exampleShortHeader => {
onSelectExampleShortHeader({
exampleShortHeader,
preventBackHome: true,
});
},
onSelectPrivateGameTemplateListingData: privateGameTemplateListingData => {
onSelectPrivateGameTemplateListingData({
privateGameTemplateListingData,
preventBackHome: true,
});
},
onOpenPrivateGameTemplateListingData: privateGameTemplateListingData => {
onSelectPrivateGameTemplateListingData({
privateGameTemplateListingData,
preventBackHome: true,
});
},
onCreateEmptyProject: createEmptyProject,
onCreateProjectFromExample: createProjectFromExample,
onOpenProfile: onOpenProfileDialog,
onOpenLanguageDialog: () => openLanguageDialog(true),
onOpenPreferences: () => openPreferencesDialog(true),
onOpenAbout: () => openAboutDialog(true),
selectInAppTutorial: selectInAppTutorial,
onLoadEventsFunctionsExtensions: async () => {
if (isProjectClosedSoAvoidReloadingExtensions) {
return;
}
return eventsFunctionsExtensionsState.loadProjectEventsFunctionsExtensions(
currentProject
);
},
onReloadEventsFunctionsExtensionMetadata: extension => {
if (isProjectClosedSoAvoidReloadingExtensions) {
return;
}
eventsFunctionsExtensionsState.reloadProjectEventsFunctionsExtensionMetadata(
currentProject,
extension
);
},
onDeleteResource: (
resource: gdResource,
cb: boolean => void
) => {
// TODO: Project wide refactoring of objects/events using the resource
cb(true);
},
onRenameResource: (
resource: gdResource,
newName: string,
cb: boolean => void
) => {
if (currentProject)
renameResourcesInProject(currentProject, {
[resource.getName()]: newName,
});
cb(true);
},
openBehaviorEvents: openBehaviorEvents,
onExtractAsExternalLayout: onExtractAsExternalLayout,
onExtractAsEventBasedObject: onOpenEventBasedObjectEditor,
onOpenEventBasedObjectEditor: onOpenEventBasedObjectEditor,
onOpenEventBasedObjectVariantEditor: onOpenEventBasedObjectVariantEditor,
onDeleteEventsBasedObjectVariant: deleteEventsBasedObjectVariant,
onEventsBasedObjectChildrenEdited: onEventsBasedObjectChildrenEdited,
onSceneObjectEdited: onSceneObjectEdited,
onSceneObjectsDeleted: onSceneObjectsDeleted,
onSceneEventsModifiedOutsideEditor: onSceneEventsModifiedOutsideEditor,
onExtensionInstalled: onExtensionInstalled,
gamesList,
gamesPlatformFrameTools,
})}
</ErrorBoundary>
</CommandsContextScopedProvider>
</TabContentContainer>
);
})}
</>
);
});
export default EditorsPane;

View File

@@ -23,13 +23,10 @@ import Window from '../Utils/Window';
import { showErrorBox } from '../UI/Messages/MessageBox';
import { TabContentContainer } from '../UI/ClosableTabs';
import { DraggableEditorTabs } from './EditorTabs/DraggableEditorTabs';
import EditorsPane from './EditorsPane';
import {
getEditorTabsInitialState,
openEditorTab,
closeEditorTab,
closeOtherEditorTabs,
closeAllEditorTabs,
changeCurrentTab,
getEditors,
getCurrentTabIndex,
getCurrentTab,
@@ -46,7 +43,6 @@ import {
type EditorKind,
getEventsFunctionsExtensionEditor,
notifyPreviewOrExportWillStart,
moveTabToTheRightOfHoveredTab,
getCustomObjectEditor,
hasEditorTabOpenedWithKey,
getOpenedAskAiEditor,
@@ -174,7 +170,6 @@ import InAppTutorialContext from '../InAppTutorial/InAppTutorialContext';
import useOpenInitialDialog from '../Utils/UseOpenInitialDialog';
import { type InAppTutorialOrchestratorInterface } from '../InAppTutorial/InAppTutorialOrchestrator';
import useInAppTutorialOrchestrator from '../InAppTutorial/useInAppTutorialOrchestrator';
import TabsTitlebar from './TabsTitlebar';
import {
useStableUpToDateCallback,
useStableUpToDateRef,
@@ -354,7 +349,7 @@ const MainFrame = (props: Props) => {
gdjsDevelopmentWatcherEnabled: false,
}: State)
);
const toolbar = React.useRef<?ToolbarInterface>(null);
const editorsPaneRef = React.useRef<?ToolbarInterface>(null);
const [
tabsTitleBarAndEditorToolbarHidden,
setTabsTitleBarAndEditorToolbarHidden,
@@ -773,25 +768,7 @@ const MainFrame = (props: Props) => {
});
};
const updateToolbar = React.useCallback(
(newEditorTabs = state.editorTabs) => {
const editorTab = getCurrentTab(newEditorTabs);
if (!editorTab || !editorTab.editorRef) {
setEditorToolbar(null);
return;
}
editorTab.editorRef.updateToolbar();
},
[state.editorTabs]
);
React.useEffect(
() => {
updateToolbar();
},
[updateToolbar]
);
const _languageDidChange = () => {
// A change in the language will automatically be applied
@@ -1284,18 +1261,12 @@ const MainFrame = (props: Props) => {
const toggleProjectManager = React.useCallback(
() => {
if (toolbar.current)
if (editorsPaneRef.current)
openProjectManager(projectManagerOpen => !projectManagerOpen);
},
[openProjectManager]
);
const setEditorToolbar = (editorToolbar: any, isCurrentTab = true) => {
if (!toolbar.current || !isCurrentTab) return;
toolbar.current.setEditorToolbar(editorToolbar);
};
const deleteLayout = (layout: gdLayout) => {
const { currentProject } = state;
const { i18n } = props;
@@ -3249,61 +3220,7 @@ const MainFrame = (props: Props) => {
[currentProject, hasUnsavedChanges, i18n, closeProject]
);
const _onChangeEditorTab = (value: number) => {
setState(state => ({
...state,
editorTabs: changeCurrentTab(state.editorTabs, value),
})).then(state =>
_onEditorTabActivated(getCurrentTab(state.editorTabs), state)
);
};
const _onEditorTabActivated = (
editorTab: EditorTab,
newState: State = state
) => {
updateToolbar(newState.editorTabs);
// Ensure the editors shown on the screen are updated. This is for
// example useful if global objects have been updated in another editor.
if (editorTab.editorRef) {
editorTab.editorRef.forceUpdateEditor();
}
};
const _onCloseEditorTab = (editorTab: EditorTab) => {
saveUiSettings(state.editorTabs);
setState(state => ({
...state,
editorTabs: closeEditorTab(state.editorTabs, editorTab),
}));
};
const _onCloseOtherEditorTabs = (editorTab: EditorTab) => {
saveUiSettings(state.editorTabs);
setState(state => ({
...state,
editorTabs: closeOtherEditorTabs(state.editorTabs, editorTab),
}));
};
const _onCloseAllEditorTabs = () => {
saveUiSettings(state.editorTabs);
setState(state => ({
...state,
editorTabs: closeAllEditorTabs(state.editorTabs),
}));
};
const onDropEditorTab = (fromIndex: number, toHoveredIndex: number) => {
setState(state => ({
...state,
editorTabs: moveTabToTheRightOfHoveredTab(
state.editorTabs,
fromIndex,
toHoveredIndex
),
}));
};
const endTutorial = React.useCallback(
async (shouldCloseProject?: boolean) => {
@@ -3905,80 +3822,6 @@ const MainFrame = (props: Props) => {
buildMainMenuProps={buildMainMenuProps}
/>
</ProjectManagerDrawer>
<TabsTitlebar
hidden={tabsTitleBarAndEditorToolbarHidden}
toggleProjectManager={toggleProjectManager}
renderTabs={(onEditorTabHovered, onEditorTabClosing) => (
<DraggableEditorTabs
hideLabels={false}
editorTabs={state.editorTabs}
onClickTab={(id: number) => _onChangeEditorTab(id)}
onCloseTab={(editorTab: EditorTab) => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseEditorTab(editorTab);
}}
onCloseOtherTabs={(editorTab: EditorTab) => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseOtherEditorTabs(editorTab);
}}
onCloseAll={() => {
// Call onEditorTabClosing before to ensure any tooltip is removed before the tab is closed.
onEditorTabClosing();
_onCloseAllEditorTabs();
}}
onTabActivated={(editorTab: EditorTab) =>
_onEditorTabActivated(editorTab)
}
onDropTab={onDropEditorTab}
onHoverTab={(
editorTab: ?EditorTab,
options: {| isLabelTruncated: boolean |}
) => onEditorTabHovered(editorTab, options)}
/>
)}
hasAskAiOpened={hasAskAiOpened}
onOpenAskAi={openAskAi}
/>
<Toolbar
ref={toolbar}
hidden={tabsTitleBarAndEditorToolbarHidden}
showProjectButtons={
!['start page', 'debugger', 'ask-ai', null].includes(
getCurrentTab(state.editorTabs)
? getCurrentTab(state.editorTabs).key
: null
)
}
canSave={canSave}
onSave={saveProject}
openShareDialog={() =>
openShareDialog(/* leave the dialog decide which tab to open */)
}
isSharingEnabled={
!checkedOutVersionStatus && !cloudProjectRecoveryOpenedVersionId
}
onOpenDebugger={launchDebuggerAndPreview}
hasPreviewsRunning={hasPreviewsRunning}
onPreviewWithoutHotReload={launchNewPreview}
onNetworkPreview={launchNetworkPreview}
onHotReloadPreview={launchHotReloadPreview}
onLaunchPreviewWithDiagnosticReport={launchPreviewWithDiagnosticReport}
canDoNetworkPreview={
!!_previewLauncher.current &&
_previewLauncher.current.canDoNetworkPreview()
}
setPreviewOverride={setPreviewOverride}
isPreviewEnabled={
!!currentProject && currentProject.getLayoutsCount() > 0
}
previewState={previewState}
onOpenVersionHistory={openVersionHistoryPanel}
checkedOutVersionStatus={checkedOutVersionStatus}
onQuitVersionHistory={onQuitVersionHistory}
canQuitVersionHistory={!isSavingProject}
/>
{// Render games platform frame before the editors, so the editor have priority
// in what to display (ex: Loader of play section)
gamesPlatformFrameTools.renderGamesPlatformFrame()}
@@ -3987,148 +3830,86 @@ const MainFrame = (props: Props) => {
state.currentProject ? state.currentProject.getProjectUuid() : ''
}
>
{getEditors(state.editorTabs).map((editorTab, id) => {
const isCurrentTab = getCurrentTabIndex(state.editorTabs) === id;
const errorBoundaryProps = getEditorErrorBoundaryProps(editorTab.key);
return (
<TabContentContainer
key={editorTab.key}
active={isCurrentTab}
// Deactivate pointer events when the play tab is active, so the iframe
// can be interacted with.
removePointerEvents={gamesPlatformFrameTools.iframeVisible}
>
<CommandsContextScopedProvider active={isCurrentTab}>
<ErrorBoundary
componentTitle={errorBoundaryProps.componentTitle}
scope={errorBoundaryProps.scope}
>
{editorTab.renderEditorContainer({
isActive: isCurrentTab,
extraEditorProps: editorTab.extraEditorProps,
project: currentProject,
fileMetadata: currentFileMetadata,
storageProvider: getStorageProvider(),
ref: editorRef => (editorTab.editorRef = editorRef),
setToolbar: editorToolbar =>
setEditorToolbar(editorToolbar, isCurrentTab),
hideTabsTitleBarAndEditorToolbar: setTabsTitleBarAndEditorToolbarHidden,
projectItemName: editorTab.projectItemName,
setPreviewedLayout,
onOpenExternalEvents: openExternalEvents,
onOpenEvents: (sceneName: string) => {
openLayout(sceneName, {
openEventsEditor: true,
openSceneEditor: false,
focusWhenOpened: 'events',
});
},
onOpenLayout: openLayout,
onOpenTemplateFromTutorial: openTemplateFromTutorial,
onOpenTemplateFromCourseChapter: openTemplateFromCourseChapter,
previewDebuggerServer,
hotReloadPreviewButtonProps,
resourceManagementProps,
onSave: saveProject,
canSave,
onCreateEventsFunction,
openInstructionOrExpression,
onOpenCustomObjectEditor: openCustomObjectEditor,
onRenamedEventsBasedObject: onRenamedEventsBasedObject,
onDeletedEventsBasedObject: onDeletedEventsBasedObject,
openObjectEvents,
unsavedChanges: unsavedChanges,
canOpen: !!props.storageProviders.filter(
({ hiddenInOpenDialog }) => !hiddenInOpenDialog
).length,
onChooseProject: () => openOpenFromStorageProviderDialog(),
onOpenRecentFile: openFromFileMetadataWithStorageProvider,
onOpenNewProjectSetupDialog: openNewProjectDialog,
onOpenProjectManager: () => openProjectManager(true),
onOpenVersionHistory: openVersionHistoryPanel,
askToCloseProject,
closeProject,
onSelectExampleShortHeader: exampleShortHeader => {
onSelectExampleShortHeader({
exampleShortHeader,
preventBackHome: true,
});
},
onSelectPrivateGameTemplateListingData: privateGameTemplateListingData => {
onSelectPrivateGameTemplateListingData({
privateGameTemplateListingData,
preventBackHome: true,
});
},
onOpenPrivateGameTemplateListingData: privateGameTemplateListingData => {
onSelectPrivateGameTemplateListingData({
privateGameTemplateListingData,
preventBackHome: true,
});
},
onCreateEmptyProject: createEmptyProject,
onCreateProjectFromExample: createProjectFromExample,
onOpenProfile: onOpenProfileDialog,
onOpenLanguageDialog: () => openLanguageDialog(true),
onOpenPreferences: () => openPreferencesDialog(true),
onOpenAbout: () => openAboutDialog(true),
selectInAppTutorial: selectInAppTutorial,
onLoadEventsFunctionsExtensions: async () => {
if (isProjectClosedSoAvoidReloadingExtensions) {
return;
}
return eventsFunctionsExtensionsState.loadProjectEventsFunctionsExtensions(
currentProject
);
},
onReloadEventsFunctionsExtensionMetadata: extension => {
if (isProjectClosedSoAvoidReloadingExtensions) {
return;
}
eventsFunctionsExtensionsState.reloadProjectEventsFunctionsExtensionMetadata(
currentProject,
extension
);
},
onDeleteResource: (
resource: gdResource,
cb: boolean => void
) => {
// TODO: Project wide refactoring of objects/events using the resource
cb(true);
},
onRenameResource: (
resource: gdResource,
newName: string,
cb: boolean => void
) => {
if (currentProject)
renameResourcesInProject(currentProject, {
[resource.getName()]: newName,
});
cb(true);
},
openBehaviorEvents: openBehaviorEvents,
onExtractAsExternalLayout: onExtractAsExternalLayout,
onExtractAsEventBasedObject: onOpenEventBasedObjectEditor,
onOpenEventBasedObjectEditor: onOpenEventBasedObjectEditor,
onOpenEventBasedObjectVariantEditor: onOpenEventBasedObjectVariantEditor,
onDeleteEventsBasedObjectVariant: deleteEventsBasedObjectVariant,
onEventsBasedObjectChildrenEdited: onEventsBasedObjectChildrenEdited,
onSceneObjectEdited: onSceneObjectEdited,
onSceneObjectsDeleted: onSceneObjectsDeleted,
onSceneEventsModifiedOutsideEditor: onSceneEventsModifiedOutsideEditor,
onExtensionInstalled: onExtensionInstalled,
gamesList,
gamesPlatformFrameTools,
})}
</ErrorBoundary>
</CommandsContextScopedProvider>
</TabContentContainer>
);
})}
<EditorsPane
ref={editorsPaneRef}
editorTabs={state.editorTabs}
currentProject={currentProject}
currentFileMetadata={currentFileMetadata}
tabsTitleBarAndEditorToolbarHidden={tabsTitleBarAndEditorToolbarHidden}
setTabsTitleBarAndEditorToolbarHidden={setTabsTitleBarAndEditorToolbarHidden}
canSave={canSave}
isSavingProject={isSavingProject}
isSharingEnabled={!checkedOutVersionStatus && !cloudProjectRecoveryOpenedVersionId}
hasPreviewsRunning={hasPreviewsRunning}
previewState={previewState}
checkedOutVersionStatus={checkedOutVersionStatus}
canDoNetworkPreview={
!!_previewLauncher.current &&
_previewLauncher.current.canDoNetworkPreview()
}
gamesPlatformFrameTools={gamesPlatformFrameTools}
toggleProjectManager={toggleProjectManager}
setEditorTabs={setEditorTabs}
saveProject={saveProject}
openShareDialog={openShareDialog}
launchDebuggerAndPreview={launchDebuggerAndPreview}
launchNewPreview={launchNewPreview}
launchNetworkPreview={launchNetworkPreview}
launchHotReloadPreview={launchHotReloadPreview}
launchPreviewWithDiagnosticReport={launchPreviewWithDiagnosticReport}
setPreviewOverride={setPreviewOverride}
openVersionHistoryPanel={openVersionHistoryPanel}
onQuitVersionHistory={onQuitVersionHistory}
openAskAi={openAskAi}
getStorageProvider={getStorageProvider}
setPreviewedLayout={setPreviewedLayout}
openExternalEvents={openExternalEvents}
openLayout={openLayout}
openTemplateFromTutorial={openTemplateFromTutorial}
openTemplateFromCourseChapter={openTemplateFromCourseChapter}
previewDebuggerServer={previewDebuggerServer}
hotReloadPreviewButtonProps={hotReloadPreviewButtonProps}
resourceManagementProps={resourceManagementProps}
onCreateEventsFunction={onCreateEventsFunction}
openInstructionOrExpression={openInstructionOrExpression}
onOpenCustomObjectEditor={openCustomObjectEditor}
onRenamedEventsBasedObject={onRenamedEventsBasedObject}
onDeletedEventsBasedObject={onDeletedEventsBasedObject}
openObjectEvents={openObjectEvents}
canOpen={!!props.storageProviders.filter(
({ hiddenInOpenDialog }) => !hiddenInOpenDialog
).length}
openOpenFromStorageProviderDialog={openOpenFromStorageProviderDialog}
openFromFileMetadataWithStorageProvider={openFromFileMetadataWithStorageProvider}
openNewProjectDialog={openNewProjectDialog}
openProjectManager={openProjectManager}
askToCloseProject={askToCloseProject}
closeProject={closeProject}
onSelectExampleShortHeader={onSelectExampleShortHeader}
onSelectPrivateGameTemplateListingData={onSelectPrivateGameTemplateListingData}
createEmptyProject={createEmptyProject}
createProjectFromExample={createProjectFromExample}
onOpenProfileDialog={onOpenProfileDialog}
openLanguageDialog={openLanguageDialog}
openPreferencesDialog={openPreferencesDialog}
openAboutDialog={openAboutDialog}
selectInAppTutorial={selectInAppTutorial}
eventsFunctionsExtensionsState={eventsFunctionsExtensionsState}
isProjectClosedSoAvoidReloadingExtensions={isProjectClosedSoAvoidReloadingExtensions}
renameResourcesInProject={renameResourcesInProject}
openBehaviorEvents={openBehaviorEvents}
onExtractAsExternalLayout={onExtractAsExternalLayout}
onOpenEventBasedObjectEditor={onOpenEventBasedObjectEditor}
onOpenEventBasedObjectVariantEditor={onOpenEventBasedObjectVariantEditor}
deleteEventsBasedObjectVariant={deleteEventsBasedObjectVariant}
onEventsBasedObjectChildrenEdited={onEventsBasedObjectChildrenEdited}
onSceneObjectEdited={onSceneObjectEdited}
onSceneObjectsDeleted={onSceneObjectsDeleted}
onSceneEventsModifiedOutsideEditor={onSceneEventsModifiedOutsideEditor}
onExtensionInstalled={onExtensionInstalled}
gamesList={gamesList}
inAppTutorialOrchestratorRef={inAppTutorialOrchestratorRef}
/>
</LeaderboardProvider>
<CommandPaletteWithAlgoliaSearch ref={commandPaletteRef} />
<LoaderModal