Save the scroll position of JS code events when closing and opening tabs (#8417)

This commit is contained in:
D8H
2026-03-23 17:29:51 +01:00
committed by GitHub
parent af21c2f1ca
commit 23908aad8e
10 changed files with 189 additions and 12 deletions
+8 -3
View File
@@ -73,8 +73,13 @@ void JsCodeEvent::UnserializeFrom(gd::Project& project,
JsCodeEvent::JsCodeEvent()
: BaseEvent(),
inlineCode("runtimeScene.setBackgroundColor(100,100,240);\n"),
useStrict(true),
eventsSheetExpanded(false) {}
inlineCode("runtimeScene.setBackgroundColor(100,100,240);\n") {}
JsCodeEvent::JsCodeEvent(const JsCodeEvent &event): BaseEvent(event) {
inlineCode = event.inlineCode;
parameterObjects = event.parameterObjects;
useStrict = event.useStrict;
eventsSheetExpanded = event.eventsSheetExpanded;
}
} // namespace gdjs
+26 -9
View File
@@ -3,10 +3,12 @@
* 2008-2016 Florian Rival ([email protected])
*/
#ifndef JSCODEEVENT_H
#define JSCODEEVENT_H
#pragma once
#include "GDCore/Events/Event.h"
#include "GDCore/Events/Expression.h"
#include "GDCore/Project/MemoryTrackedRegistry.h"
namespace gd {
class Instruction;
class Project;
@@ -50,18 +52,33 @@ class JsCodeEvent : public gd::BaseEvent {
bool IsEventsSheetExpanded() const { return eventsSheetExpanded; }
void SetEventsSheetExpanded(bool enable) { eventsSheetExpanded = enable; };
private:
void Init(const JsCodeEvent& event);
long GetScrollTop() const { return scrollTop; }
void SetScrollTop(long value) { scrollTop = value; };
long GetCursorColumn() const { return cursorColumn; }
void SetCursorColumn(long value) { cursorColumn = value; };
long GetCursorLine() const { return cursorLine; }
void SetCursorLine(long value) { cursorLine = value; };
private:
JsCodeEvent(const JsCodeEvent& event);
gd::String inlineCode; ///< Contains the Javascript code of the event.
gd::Expression parameterObjects; ///< Name of the (group of) objects to pass as
///< parameter.
bool useStrict; ///< Should the generated JS function have "use strict". true
///< by default. Should be removed once all the game engine
///< is using "use strict".
bool eventsSheetExpanded; ///< Is the code block expanded in the events sheet?
bool useStrict = true; ///< Should the generated JS function have "use strict". true
///< by default. Should be removed once all the game engine
///< is using "use strict".
bool eventsSheetExpanded = false; ///< Is the code block expanded in the events sheet?
// These members are not serialized.
long scrollTop = 0;
long cursorColumn = 0;
long cursorLine = 0;
gd::MemoryTracked _memoryTracked{this, "JsCodeEvent"};
};
} // namespace gdjs
#endif // JSCODEEVENT_H
+7
View File
@@ -4366,6 +4366,13 @@ interface JsCodeEvent {
boolean IsFolded();
void SetFolded(boolean folded);
long GetScrollTop();
void SetScrollTop(long value);
long GetCursorColumn();
void SetCursorColumn(long value);
long GetCursorLine();
void SetCursorLine(long value);
void SerializeTo([Ref] SerializerElement element);
void UnserializeFrom([Ref] Project project, [Const, Ref] SerializerElement element);
};
+1
View File
@@ -563,6 +563,7 @@ patchClassesForUseAfterFreeDetection(Module, {
'ObjectGroupsContainer',
'ObjectsContainer',
'VariablesContainer',
'JsCodeEvent',
]),
verbose: false,
});
+6
View File
@@ -3242,6 +3242,12 @@ export class JsCodeEvent extends EmscriptenObject {
setDisabled(disable: boolean): void;
isFolded(): boolean;
setFolded(folded: boolean): void;
getScrollTop(): number;
setScrollTop(value: number): void;
getCursorColumn(): number;
setCursorColumn(value: number): void;
getCursorLine(): number;
setCursorLine(value: number): void;
serializeTo(element: SerializerElement): void;
unserializeFrom(project: Project, element: SerializerElement): void;
}
+6
View File
@@ -18,6 +18,12 @@ declare class gdJsCodeEvent extends gdBaseEvent {
setDisabled(disable: boolean): void;
isFolded(): boolean;
setFolded(folded: boolean): void;
getScrollTop(): number;
setScrollTop(value: number): void;
getCursorColumn(): number;
setCursorColumn(value: number): void;
getCursorLine(): number;
setCursorLine(value: number): void;
serializeTo(element: gdSerializerElement): void;
unserializeFrom(project: gdProject, element: gdSerializerElement): void;
delete(): void;
@@ -0,0 +1,84 @@
diff --git a/node_modules/react-monaco-editor/lib/editor.js b/node_modules/react-monaco-editor/lib/editor.js
index 8e6ae66..87205ba 100644
--- a/node_modules/react-monaco-editor/lib/editor.js
+++ b/node_modules/react-monaco-editor/lib/editor.js
@@ -107,6 +107,13 @@ var MonacoEditor = function (_React$Component) {
}
});
}
+ }, {
+ key: 'editorWillUnmount',
+ value: function editorWillUnmount(editor) {
+ var editorWillUnmount = this.props.editorWillUnmount;
+
+ editorWillUnmount(editor, monaco);
+ }
}, {
key: 'initMonaco',
value: function initMonaco() {
@@ -134,6 +141,7 @@ var MonacoEditor = function (_React$Component) {
key: 'destroyMonaco',
value: function destroyMonaco() {
if (typeof this.editor !== 'undefined') {
+ this.editorWillUnmount(this.editor);
this.editor.dispose();
}
}
@@ -168,6 +176,7 @@ MonacoEditor.propTypes = {
options: _propTypes2.default.object,
editorDidMount: _propTypes2.default.func,
editorWillMount: _propTypes2.default.func,
+ editorWillUnmount: _propTypes2.default.func,
onChange: _propTypes2.default.func
};
@@ -181,6 +190,7 @@ MonacoEditor.defaultProps = {
options: {},
editorDidMount: noop,
editorWillMount: noop,
+ editorWillUnmount: noop,
onChange: noop
};
diff --git a/node_modules/react-monaco-editor/src/editor.js b/node_modules/react-monaco-editor/src/editor.js
index 90ab4de..589dc40 100644
--- a/node_modules/react-monaco-editor/src/editor.js
+++ b/node_modules/react-monaco-editor/src/editor.js
@@ -64,6 +64,13 @@ class MonacoEditor extends React.Component {
});
}
+ // This is a backport from react-monaco-editor0.47.0 of https://github.com/react-monaco-editor/react-monaco-editor/pull/482
+ // Starting from react-monaco-editor0.21.0 no language is registered for some reason.
+ editorWillUnmount(editor) {
+ const { editorWillUnmount } = this.props;
+ editorWillUnmount(editor, monaco);
+ }
+
initMonaco() {
const value = this.props.value !== null ? this.props.value : this.props.defaultValue;
const { language, theme, options } = this.props;
@@ -85,6 +92,7 @@ class MonacoEditor extends React.Component {
destroyMonaco() {
if (typeof this.editor !== 'undefined') {
+ this.editorWillUnmount(this.editor);
this.editor.dispose();
}
}
@@ -116,6 +124,7 @@ MonacoEditor.propTypes = {
options: PropTypes.object,
editorDidMount: PropTypes.func,
editorWillMount: PropTypes.func,
+ editorWillUnmount: PropTypes.func,
onChange: PropTypes.func
};
@@ -129,6 +138,7 @@ MonacoEditor.defaultProps = {
options: {},
editorDidMount: noop,
editorWillMount: noop,
+ editorWillUnmount: noop,
onChange: noop
};
+24
View File
@@ -16,6 +16,14 @@ export type State = {|
export type Props = {|
value: string,
onChange: string => void,
initialScrollTop: number,
initialCursorColumn: number,
initialCursorLine: number,
saveEditorState: ({
scrollTop: number,
cursorColumn: number,
cursorLine: number,
}) => void,
width?: number,
height?: number,
onEditorMounted?: () => void,
@@ -97,6 +105,12 @@ export class CodeEditor extends React.Component<Props, State> {
setupAutocompletions(monaco);
}
editor.setScrollTop(this.props.initialScrollTop);
editor.setPosition({
column: this.props.initialCursorColumn,
lineNumber: this.props.initialCursorLine,
});
if (this.props.onEditorMounted) this.props.onEditorMounted();
};
@@ -140,6 +154,15 @@ export class CodeEditor extends React.Component<Props, State> {
event.stopPropagation();
};
_saveEditorState = (editor: any, monaco: any) => {
const cursorPosition = editor.getPosition();
this.props.saveEditorState({
scrollTop: editor.getScrollTop(),
cursorColumn: cursorPosition.column,
cursorLine: cursorPosition.lineNumber,
});
};
render(): any {
const { MonacoEditor, error } = this.state;
if (error) {
@@ -174,6 +197,7 @@ export class CodeEditor extends React.Component<Props, State> {
onChange={this.props.onChange}
editorWillMount={this.setupEditorThemes}
editorDidMount={this.setupEditorCompletions}
editorWillUnmount={this._saveEditorState}
options={{
...monacoEditorOptions,
fontSize: preferences.eventsSheetZoomLevel,
@@ -18,6 +18,8 @@ import { type ParameterFieldInterface } from '../../ParameterFields/ParameterFie
import { Trans } from '@lingui/macro';
import ChevronArrowTop from '../../../UI/CustomSvgIcons/ChevronArrowTop';
import ChevronArrowBottom from '../../../UI/CustomSvgIcons/ChevronArrowBottom';
import { exceptionallyGuardAgainstDeadObject } from '../../../Utils/IsNullPtr';
const gd: libGDevelop = global.gd;
const fontFamily = '"Lucida Console", Monaco, monospace';
@@ -98,6 +100,26 @@ export default class JsCodeEvent extends React.Component<
jsCodeEvent.setInlineCode(newValue);
};
saveEditorState = ({
scrollTop,
cursorColumn,
cursorLine,
}: {
scrollTop: number,
cursorColumn: number,
cursorLine: number,
}): void => {
const jsCodeEvent = exceptionallyGuardAgainstDeadObject(
gd.asJsCodeEvent(this.props.event)
);
if (!jsCodeEvent) {
return;
}
jsCodeEvent.setScrollTop(scrollTop);
jsCodeEvent.setCursorColumn(cursorColumn);
jsCodeEvent.setCursorLine(cursorLine);
};
editObject = (domEvent: any) => {
const jsCodeEvent = gd.asJsCodeEvent(this.props.event);
const parameterObjects = jsCodeEvent.getParameterObjects();
@@ -270,6 +292,10 @@ export default class JsCodeEvent extends React.Component<
<CodeEditor
value={jsCodeEvent.getInlineCode()}
onChange={this.onChange}
initialScrollTop={jsCodeEvent.getScrollTop()}
initialCursorColumn={jsCodeEvent.getCursorColumn()}
initialCursorLine={jsCodeEvent.getCursorLine()}
saveEditorState={this.saveEditorState}
width={contentRect.bounds.width - 5}
height={this._getCodeEditorHeight()}
onEditorMounted={() => {
@@ -30,6 +30,7 @@ const trackedClasses = [
'ObjectGroupsContainer',
'ObjectsContainer',
'VariablesContainer',
'JsCodeEvent',
];
type Stats = {|