Skip to content

Commit f91073b

Browse files
committed
Save and restore properties panel scroll position per instance/object
- Add GetPersistentUuid to gd::Object (C++ header, IDL bindings, JS types) so objects can be identified by UUID like instances already can. - Call resetPersistentUuid() on objects in all copy/paste/duplicate/add paths (ObjectTreeViewItemContent, CustomObjectExtractor, EditorFunctions, InstallAsset) to ensure copied objects get unique UUIDs. - Rework setEditorStateForProject to accept partial updates (merges into existing state) instead of replacing the entire editor state. This allows propertiesPanelScroll to coexist with editorTabs without overwriting. - Add propertiesPanelScroll to PersistedEditorState, stored as an object keyed by instance/object persistent UUID mapping to scroll position. - In InstanceOrObjectPropertiesEditorContainer, save scroll position (debounced 500ms) and restore it when an instance or object is selected. https://claude.ai/code/session_01UiSsvtAFrmLrqVVPWSWmnu
1 parent 7371537 commit f91073b

File tree

11 files changed

+140
-27
lines changed

11 files changed

+140
-27
lines changed

Core/GDCore/Project/Object.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,12 @@ class GD_CORE_API Object {
242242
*/
243243
Object& ResetPersistentUuid();
244244

245+
/**
246+
* \brief Get the persistent UUID used to recognize
247+
* the same object between serialization.
248+
*/
249+
const gd::String& GetPersistentUuid() const { return persistentUuid; }
250+
245251
/**
246252
* \brief Remove the persistent UUID - when the object no
247253
* longer need to be recognized between serializations.

GDevelop.js/Bindings/Bindings.idl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,7 @@ interface gdObject {
912912
void SerializeTo([Ref] SerializerElement element);
913913
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
914914
[Ref] gdObject ResetPersistentUuid();
915+
[Const, Ref] DOMString GetPersistentUuid();
915916
[Ref] gdObject ClearPersistentUuid();
916917
};
917918

GDevelop.js/types/gdobject.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ declare class gdObject {
2020
serializeTo(element: gdSerializerElement): void;
2121
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
2222
resetPersistentUuid(): gdObject;
23+
getPersistentUuid(): string;
2324
clearPersistentUuid(): gdObject;
2425
delete(): void;
2526
ptr: number;

newIDE/app/src/AssetStore/InstallAsset.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ export const addAssetToProject = async ({
285285
);
286286
// The name was overwritten after unserialization.
287287
object.setName(newName);
288+
object.resetPersistentUuid();
288289
object.setAssetStoreId(asset.id);
289290
if (project.hasEventsBasedObject(object.getType())) {
290291
const customObjectConfiguration = gd.asCustomObjectConfiguration(

newIDE/app/src/EditorFunctions/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,7 @@ const createOrReplaceObject: EditorFunction = {
10841084
project
10851085
);
10861086
newObject.setName(targetObjectName); // Unserialization has overwritten the name.
1087+
newObject.resetPersistentUuid();
10871088

10881089
// Update behaviors shared data for the scene where the object was duplicated.
10891090
if (target_object_scope === 'global') {

newIDE/app/src/MainFrame/EditorTabs/UseEditorTabsStateSaving.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,10 @@ const useEditorTabsStateSaving = ({
9393
})),
9494
};
9595

96-
setEditorStateForProject(
97-
currentProjectId,
98-
editorState.editors.length === 0
99-
? undefined
100-
: { editorTabs: editorState }
101-
);
96+
setEditorStateForProject(currentProjectId, {
97+
editorTabs:
98+
editorState.editors.length === 0 ? undefined : editorState,
99+
});
102100
},
103101
[currentProjectId, editorTabs, setEditorStateForProject]
104102
);
@@ -127,7 +125,8 @@ const useEditorTabsStateSaving = ({
127125
const hasAPreviousSaveForEditorTabsState = React.useCallback(
128126
(project: gdProject) => {
129127
const projectId = project.getProjectUuid();
130-
return !!getEditorStateForProject(projectId);
128+
const editorState = getEditorStateForProject(projectId);
129+
return !!editorState && !!editorState.editorTabs;
131130
},
132131
[getEditorStateForProject]
133132
);
@@ -136,7 +135,7 @@ const useEditorTabsStateSaving = ({
136135
(project: gdProject): number => {
137136
const projectId = project.getProjectUuid();
138137
const editorState = getEditorStateForProject(projectId);
139-
if (!editorState) return 0;
138+
if (!editorState || !editorState.editorTabs) return 0;
140139
let shouldOpenSavedCurrentTab = true;
141140

142141
const editorsOpeningOptions = editorState.editorTabs.editors

newIDE/app/src/MainFrame/Preferences/PreferencesContext.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ export const allAlertMessages: Array<{
170170
},
171171
];
172172

173+
export type PersistedEditorState = {
174+
editorTabs?: EditorTabsPersistedState,
175+
propertiesPanelScroll?: { [string]: number },
176+
};
177+
173178
/**
174179
* All the preferences of GDevelop. To add a new preference, add it into this
175180
* type and add a setter into `Preferences` type. Then, update the
@@ -226,7 +231,7 @@ export type PreferencesValues = {|
226231
[featureId: string]: {| dates: [number] |},
227232
},
228233
displaySaveReminder: {| activated: boolean |}, // Store as object in case we need to add options.
229-
editorStateByProject: { [string]: { editorTabs: EditorTabsPersistedState } },
234+
editorStateByProject: { [string]: PersistedEditorState },
230235
fetchPlayerTokenForPreviewAutomatically: boolean,
231236
previewCrashReportUploadLevel: string,
232237
gamesDashboardOrderBy: GamesDashboardOrderBy,
@@ -344,12 +349,11 @@ export type Preferences = {|
344349
[featureId: string]: {| dates: [number] |},
345350
}) => void,
346351
setDisplaySaveReminder: ({| activated: boolean |}) => void,
347-
getEditorStateForProject: (
348-
projectId: string
349-
) => ?{| editorTabs: EditorTabsPersistedState |},
352+
getEditorStateForProject: (projectId: string) => ?PersistedEditorState,
350353
setEditorStateForProject: (
351354
projectId: string,
352-
editorState?: {| editorTabs: EditorTabsPersistedState |}
355+
// $FlowFixMe[deprecated-utility]
356+
editorStateUpdate: $Shape<PersistedEditorState>
353357
) => void,
354358
setFetchPlayerTokenForPreviewAutomatically: (enabled: boolean) => void,
355359
setPreviewCrashReportUploadLevel: (level: string) => void,

newIDE/app/src/MainFrame/Preferences/PreferencesProvider.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
type PreferencesValues,
1313
type EditorMosaicName,
1414
type ProjectSpecificPreferencesValues,
15+
type PersistedEditorState,
1516
} from './PreferencesContext';
1617
import type {
1718
ResourceKind,
@@ -21,7 +22,6 @@ import { type EditorMosaicNode } from '../../UI/EditorMosaic';
2122
import { type FileMetadataAndStorageProviderName } from '../../ProjectsStorage';
2223
import defaultShortcuts from '../../KeyboardShortcuts/DefaultShortcuts';
2324
import { type CommandName } from '../../CommandPalette/CommandsList';
24-
import { type EditorTabsPersistedState } from '../EditorTabs/EditorTabsHandler';
2525
import {
2626
getBrowserLanguageOrLocale,
2727
setLanguageInDOM,
@@ -1254,16 +1254,19 @@ export default class PreferencesProvider extends React.Component<Props, State> {
12541254

12551255
_setEditorStateForProject(
12561256
projectId: string,
1257-
editorState?: {| editorTabs: EditorTabsPersistedState |}
1257+
// $FlowFixMe[deprecated-utility]
1258+
editorStateUpdate: $Shape<PersistedEditorState>
12581259
) {
12591260
this.setState(
12601261
state => ({
12611262
values: {
12621263
...state.values,
12631264
editorStateByProject: {
12641265
...state.values.editorStateByProject,
1265-
// $FlowFixMe[incompatible-type]
1266-
[projectId]: editorState,
1266+
[projectId]: {
1267+
...state.values.editorStateByProject[projectId],
1268+
...editorStateUpdate,
1269+
},
12671270
},
12681271
},
12691272
}),

newIDE/app/src/ObjectsList/ObjectTreeViewItemContent.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export const addSerializedObjectToObjectsContainer = ({
161161
project
162162
);
163163
newObject.setName(newName); // Unserialization has overwritten the name.
164+
newObject.resetPersistentUuid();
164165

165166
return { object: newObject, global };
166167
};

newIDE/app/src/SceneEditor/CustomObjectExtractor/CustomObjectExtractor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export const extractAsCustomObject = ({
115115
'unserializeFrom',
116116
project
117117
);
118+
childObject.resetPersistentUuid();
118119
}
119120
}
120121
newEventsBasedObject.markAsRenderedIn3D(!!isRenderedIn3D);

0 commit comments

Comments
 (0)