Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Core/GDCore/IDE/Events/ArbitraryEventsWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ void AbstractReadOnlyArbitraryEventsWorker::VisitEventList(const gd::EventsList&
break;
}
events[i].AcceptVisitor(*this);

if (events[i].CanHaveSubEvents()) {
VisitEventList(events[i].GetSubEvents());
}
}
}

Expand All @@ -125,6 +121,12 @@ void AbstractReadOnlyArbitraryEventsWorker::VisitEvent(const gd::BaseEvent& even
}
VisitInstructionList(*actionsVectors[j], false);
}

// Visit sub-events inside VisitEvent to ensure local variables remain in scope
// when using ReadOnlyArbitraryEventsWorkerWithContext
if (!shouldStopIteration && event.CanHaveSubEvents()) {
VisitEventList(event.GetSubEvents());
}
}

void AbstractReadOnlyArbitraryEventsWorker::VisitLinkEvent(const gd::LinkEvent& linkEvent) {
Expand Down
15 changes: 15 additions & 0 deletions GDevelop.js/Bindings/Bindings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -3513,6 +3513,21 @@ interface EventsContextAnalyzer {
void Launch([Ref] EventsList events, [Const, Ref] ProjectScopedContainers projectScopedContainers);
};

interface ReadOnlyArbitraryEventsWorkerWithContext {
void Launch([Const, Ref] EventsList events, [Const, Ref] ProjectScopedContainers projectScopedContainers);
};
[JSImplementation=ReadOnlyArbitraryEventsWorkerWithContext]
interface ReadOnlyArbitraryEventsWorkerWithContextJS {
void ReadOnlyArbitraryEventsWorkerWithContextJS();

// Called for each event visited
void DoVisitEvent([Const, Ref] BaseEvent event);

// Called for each instruction visited, with the current scoped containers (includes local variables)
void DoVisitInstruction([Const, Ref] Instruction instruction, boolean isCondition, [Const, Ref] ProjectScopedContainers projectScopedContainers);
};
ReadOnlyArbitraryEventsWorkerWithContextJS implements ReadOnlyArbitraryEventsWorkerWithContext;

interface ArbitraryResourceWorker {
};
[JSImplementation=ArbitraryResourceWorker]
Expand Down
36 changes: 36 additions & 0 deletions GDevelop.js/Bindings/Wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,42 @@ class AbstractFileSystemJS : public AbstractFileSystem {
virtual ~AbstractFileSystemJS(){};
};

/**
* \brief Manual binding of gd::ReadOnlyArbitraryEventsWorkerWithContext to allow
* overriding DoVisitEvent and DoVisitInstruction from JavaScript.
*/
class ReadOnlyArbitraryEventsWorkerWithContextJS : public ReadOnlyArbitraryEventsWorkerWithContext {
public:
ReadOnlyArbitraryEventsWorkerWithContextJS(){};
virtual ~ReadOnlyArbitraryEventsWorkerWithContextJS(){};

virtual void DoVisitEvent(const gd::BaseEvent &event) {
EM_ASM(
{
var self = Module['getCache'](Module['ReadOnlyArbitraryEventsWorkerWithContextJS'])[$0];
if (!self.hasOwnProperty('doVisitEvent'))
throw 'a JSImplementation must implement all functions, you forgot ReadOnlyArbitraryEventsWorkerWithContextJS::doVisitEvent.';
self.doVisitEvent($1);
},
(int)this,
(int)&event);
}

virtual void DoVisitInstruction(const gd::Instruction &instruction, bool isCondition) {
EM_ASM(
{
var self = Module['getCache'](Module['ReadOnlyArbitraryEventsWorkerWithContextJS'])[$0];
if (!self.hasOwnProperty('doVisitInstruction'))
throw 'a JSImplementation must implement all functions, you forgot ReadOnlyArbitraryEventsWorkerWithContextJS::doVisitInstruction.';
self.doVisitInstruction($1, $2, $3);
},
(int)this,
(int)&instruction,
isCondition,
(int)&GetProjectScopedContainers());
}
};

class InitialInstanceJSFunctorWrapper : public gd::InitialInstanceFunctor {
public:
InitialInstanceJSFunctorWrapper(){};
Expand Down
10 changes: 10 additions & 0 deletions GDevelop.js/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2573,6 +2573,16 @@ export class EventsContextAnalyzer extends EmscriptenObject {
launch(events: EventsList, projectScopedContainers: ProjectScopedContainers): void;
}

export class ReadOnlyArbitraryEventsWorkerWithContext extends EmscriptenObject {
launch(events: EventsList, projectScopedContainers: ProjectScopedContainers): void;
}

export class ReadOnlyArbitraryEventsWorkerWithContextJS extends ReadOnlyArbitraryEventsWorkerWithContext {
constructor();
doVisitEvent(event: BaseEvent): void;
doVisitInstruction(instruction: Instruction, isCondition: boolean, projectScopedContainers: ProjectScopedContainers): void;
}

export class ArbitraryResourceWorker extends EmscriptenObject {}

export class ArbitraryResourceWorkerJS extends ArbitraryResourceWorker {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdReadOnlyArbitraryEventsWorkerWithContext {
launch(events: gdEventsList, projectScopedContainers: gdProjectScopedContainers): void;
delete(): void;
ptr: number;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdReadOnlyArbitraryEventsWorkerWithContextJS extends gdReadOnlyArbitraryEventsWorkerWithContext {
constructor(): void;
doVisitEvent(event: gdBaseEvent): void;
doVisitInstruction(instruction: gdInstruction, isCondition: boolean, projectScopedContainers: gdProjectScopedContainers): void;
delete(): void;
ptr: number;
};
2 changes: 2 additions & 0 deletions GDevelop.js/types/libgdevelop.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ declare class libGDevelop {
InstructionsTypeRenamer: Class<gdInstructionsTypeRenamer>;
EventsContext: Class<gdEventsContext>;
EventsContextAnalyzer: Class<gdEventsContextAnalyzer>;
ReadOnlyArbitraryEventsWorkerWithContext: Class<gdReadOnlyArbitraryEventsWorkerWithContext>;
ReadOnlyArbitraryEventsWorkerWithContextJS: Class<gdReadOnlyArbitraryEventsWorkerWithContextJS>;
ArbitraryResourceWorker: Class<gdArbitraryResourceWorker>;
ArbitraryResourceWorkerJS: Class<gdArbitraryResourceWorkerJS>;
ResourcesMergingHelper: Class<gdResourcesMergingHelper>;
Expand Down
3 changes: 3 additions & 0 deletions GDevelop.js/update-bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,14 @@ function patchGlueCppFile(cb) {
'BehaviorJsImplementation',
'ObjectJsImplementation',
'BehaviorSharedDataJsImplementation',
'ReadOnlyArbitraryEventsWorkerWithContextJS',
];
var functionsToErase = [
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeImage_1',
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeShader_1',
'emscripten_bind_ArbitraryResourceWorkerJS_ExposeFile_1',
'emscripten_bind_ReadOnlyArbitraryEventsWorkerWithContextJS_DoVisitEvent_1',
'emscripten_bind_ReadOnlyArbitraryEventsWorkerWithContextJS_DoVisitInstruction_3',
];
fs.readFile(file, function(err, data) {
if (err) cb(err);
Expand Down
5 changes: 5 additions & 0 deletions newIDE/app/src/CommandPalette/CommandsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type CommandName =
| 'LAUNCH_NETWORK_PREVIEW'
| 'HOT_RELOAD_PREVIEW'
| 'LAUNCH_PREVIEW_WITH_DIAGNOSTIC_REPORT'
| 'OPEN_DIAGNOSTIC_REPORT'
| 'OPEN_HOME_PAGE'
| 'CREATE_NEW_PROJECT'
| 'OPEN_PROJECT'
Expand Down Expand Up @@ -118,6 +119,10 @@ const commandsList: { [CommandName]: CommandMetadata } = {
area: 'PROJECT',
displayText: t`Launch preview with diagnostic report`,
},
OPEN_DIAGNOSTIC_REPORT: {
area: 'PROJECT',
displayText: t`Show diagnostic report`,
},
OPEN_HOME_PAGE: { area: 'IDE', displayText: t`Show Home` },
CREATE_NEW_PROJECT: {
area: 'GENERAL',
Expand Down
45 changes: 43 additions & 2 deletions newIDE/app/src/EventsSheet/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import LocalVariablesDialog from '../VariablesList/LocalVariablesDialog';
import GlobalAndSceneVariablesDialog from '../VariablesList/GlobalAndSceneVariablesDialog';
import { type HotReloadPreviewButtonProps } from '../HotReload/HotReloadPreviewButton';
import { useHighlightedAiGeneratedEvent } from './UseHighlightedAiGeneratedEvent';
import { findEventByPath } from '../Utils/EventsValidationScanner';

const gd: libGDevelop = global.gd;

Expand Down Expand Up @@ -212,6 +213,7 @@ type State = {|
showSearchPanel: boolean,
searchResults: ?Array<gdBaseEvent>,
searchFocusOffset: ?number,
navigationHighlightEvent: ?gdBaseEvent,

layoutVariablesDialogOpen: boolean,

Expand Down Expand Up @@ -307,6 +309,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
showSearchPanel: false,
searchResults: null,
searchFocusOffset: null,
navigationHighlightEvent: null,

layoutVariablesDialogOpen: false,

Expand Down Expand Up @@ -381,6 +384,33 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
);
};

scrollToEventPath = (eventPath: Array<number>) => {
const eventsTree = this._eventsTree;
if (!eventsTree || eventPath.length === 0) return;

// Find the event at the path
const event = findEventByPath(this.props.events, eventPath);
if (!event) return;

// Unfold and scroll to the event
eventsTree.unfoldForEvent(event);

// Highlight the event like search results
this.setState({ navigationHighlightEvent: event });

setTimeout(() => {
const row = eventsTree.getEventRow(event);
if (row !== -1) {
eventsTree.scrollToRow(row);
}
}, 100 /* Give some time for the events sheet to render before scrolling */);

// Clear the highlight after a few seconds
setTimeout(() => {
this.setState({ navigationHighlightEvent: null });
}, 3000);
};

updateToolbar() {
if (!this.props.setToolbar) return;

Expand Down Expand Up @@ -2022,8 +2052,14 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
}}
onOpenExternalEvents={onOpenExternalEvents}
onOpenLayout={onOpenLayout}
searchResults={eventsSearchResultEvents}
searchFocusOffset={searchFocusOffset}
searchResults={
this.state.navigationHighlightEvent
? [this.state.navigationHighlightEvent]
: eventsSearchResultEvents
}
searchFocusOffset={
this.state.navigationHighlightEvent ? 0 : searchFocusOffset
}
onEventMoved={this._onEventMoved}
onEndEditingEvent={this._onEndEditingStringEvent}
showObjectThumbnails={
Expand Down Expand Up @@ -2243,6 +2279,7 @@ export type EventsSheetInterface = {|
updateToolbar: () => void,
onResourceExternallyChanged: ({| identifier: string |}) => void,
onEventsModifiedOutsideEditor: (changes: OutOfEditorChanges) => void,
scrollToEventPath: (eventPath: Array<number>) => void,
|};

// EventsSheet is a wrapper so that the component can use multiple
Expand All @@ -2252,6 +2289,7 @@ const EventsSheet = (props, ref) => {
updateToolbar,
onResourceExternallyChanged,
onEventsModifiedOutsideEditor,
scrollToEventPath,
}));

const {
Expand All @@ -2271,6 +2309,9 @@ const EventsSheet = (props, ref) => {
addNewAiGeneratedEventIds(changes.newOrChangedAiGeneratedEventIds);
if (component.current) component.current.onEventsModifiedOutsideEditor();
};
const scrollToEventPath = (eventPath: Array<number>) => {
if (component.current) component.current.scrollToEventPath(eventPath);
};

const authenticatedUser = React.useContext(AuthenticatedUserContext);
const preferences = React.useContext(PreferencesContext);
Expand Down
Loading