When browsing search results in event sheet, start from currently selected event

This commit is contained in:
AlexandreS
2022-02-11 09:22:13 +01:00
committed by GitHub
parent 41c9753268
commit 46a12e6758
14 changed files with 442 additions and 193 deletions
@@ -0,0 +1,27 @@
#include "GDCore/IDE/Events/EventsPositionFinder.h"
#include <map>
#include <memory>
#include <vector>
#include "GDCore/Events/Event.h"
#include "GDCore/Events/EventsList.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class EventsList;
} // namespace gd
namespace gd {
bool EventsPositionFinder::DoVisitEvent(gd::BaseEvent& event) {
auto it = std::find(searchedEvents.begin(), searchedEvents.end(), &event);
if (it != searchedEvents.end()) {
positions[it - searchedEvents.begin()] = index;
}
index++;
return false;
}
EventsPositionFinder::~EventsPositionFinder() {}
} // namespace gd
@@ -0,0 +1,48 @@
#ifndef EventsPositionFinder_H
#define EventsPositionFinder_H
#include "GDCore/Events/EventsList.h"
#include "GDCore/IDE/Events/ArbitraryEventsWorker.h"
#include "GDCore/String.h"
namespace gd {
class BaseEvent;
class EventsList;
} // namespace gd
namespace gd {
/**
* \brief Scans an event list to retrieve the position of a list of searched
* events when the events list is flattened.
*
* \ingroup IDE
*/
class GD_CORE_API EventsPositionFinder : public ArbitraryEventsWorker {
public:
EventsPositionFinder() : index(0){};
virtual ~EventsPositionFinder();
/**
* Return the positions of all searched events
*/
const std::vector<std::size_t>& GetPositions() const { return positions; }
/**
* Add an event for which the position must be reported in `GetPositions`.
*/
void AddEventToSearch(gd::BaseEvent* event) {
searchedEvents.push_back(event);
positions.push_back(gd::String::npos);
}
private:
bool DoVisitEvent(gd::BaseEvent& event) override;
std::vector<gd::BaseEvent*> searchedEvents;
std::vector<std::size_t> positions;
std::size_t index;
};
} // namespace gd
#endif // EventsPositionFinder_H
+14
View File
@@ -39,6 +39,11 @@ interface VectorDependencyMetadata {
[Const, Ref] DependencyMetadata at(unsigned long index);
};
interface VectorInt {
unsigned long size();
long at(unsigned long index);
};
interface VectorVariable {
unsigned long size();
[Const] Variable WRAPPED_at(unsigned long index);
@@ -2289,6 +2294,15 @@ interface EventsParametersLister {
void Launch([Ref] EventsList events);
};
interface EventsPositionFinder {
void EventsPositionFinder();
[Value] VectorInt GetPositions();
void AddEventToSearch(BaseEvent event);
//Inherited from ArbitraryEventsWorker
void Launch([Ref] EventsList events);
};
interface EventsTypesLister {
void EventsTypesLister([Const, Ref] Project project);
[Const, Ref] VectorString GetAllEventsTypes();
+2
View File
@@ -25,6 +25,7 @@
#include <GDCore/IDE/Events/EventsContextAnalyzer.h>
#include <GDCore/IDE/Events/EventsListUnfolder.h>
#include <GDCore/IDE/Events/EventsParametersLister.h>
#include <GDCore/IDE/Events/EventsPositionFinder.h>
#include <GDCore/IDE/Events/EventsRefactorer.h>
#include <GDCore/IDE/Events/EventsRemover.h>
#include <GDCore/IDE/Events/EventsTypesLister.h>
@@ -383,6 +384,7 @@ typedef std::vector<std::shared_ptr<gd::Variable>> VectorVariable;
typedef std::map<gd::String, gd::PropertyDescriptor>
MapStringPropertyDescriptor;
typedef std::set<gd::String> SetString;
typedef std::vector<std::size_t> VectorInt;
typedef std::vector<Point> VectorPoint;
typedef std::vector<Polygon2d> VectorPolygon2d;
typedef std::vector<sf::Vector2f> VectorVector2f;
+9
View File
@@ -159,6 +159,15 @@ var adaptNamingConventions = function (gd) {
return arr;
};
gd.VectorInt.prototype.toJSArray = function () {
var arr = [];
var size = this.size();
for (var i = 0; i < size; ++i) {
arr.push(this.at(i));
}
return arr;
};
// Add gd.Serializer.fromJSObject which is much faster than manually parsing
// JSON with gd.Serializer.fromJSON.
const elementFromJSObject = function (object, element) {
File diff suppressed because it is too large Load Diff
+6
View File
@@ -199,6 +199,12 @@ type ParticleEmitterObject_RendererType = 0 | 1 | 2`
'declare class gdVectorString {\n toJSArray(): Array<string>;',
'types/gdvectorstring.js'
);
shell.sed(
'-i',
'declare class gdVectorInt {',
'declare class gdVectorInt {\n toJSArray(): Array<number>;',
'types/gdvectorint.js'
);
shell.sed(
'-i',
'declare class gdSerializer {',
@@ -0,0 +1,9 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdEventsPositionFinder {
constructor(): void;
getPositions(): gdVectorInt;
addEventToSearch(event: gdBaseEvent): void;
launch(events: gdEventsList): void;
delete(): void;
ptr: number;
};
+8
View File
@@ -0,0 +1,8 @@
// Automatically generated by GDevelop.js/scripts/generate-types.js
declare class gdVectorInt {
toJSArray(): Array<number>;
size(): number;
at(index: number): number;
delete(): void;
ptr: number;
};
+2
View File
@@ -37,6 +37,7 @@ declare class libGDevelop {
VectorString: Class<gdVectorString>;
VectorPlatformExtension: Class<gdVectorPlatformExtension>;
VectorDependencyMetadata: Class<gdVectorDependencyMetadata>;
VectorInt: Class<gdVectorInt>;
VectorVariable: Class<gdVectorVariable>;
MapStringString: Class<gdMapStringString>;
MapStringBoolean: Class<gdMapStringBoolean>;
@@ -169,6 +170,7 @@ declare class libGDevelop {
ProjectResourcesAdder: Class<gdProjectResourcesAdder>;
ArbitraryEventsWorker: Class<gdArbitraryEventsWorker>;
EventsParametersLister: Class<gdEventsParametersLister>;
EventsPositionFinder: Class<gdEventsPositionFinder>;
EventsTypesLister: Class<gdEventsTypesLister>;
InstructionsTypeRenamer: Class<gdInstructionsTypeRenamer>;
EventsContext: Class<gdEventsContext>;
+1 -1
View File
@@ -24,4 +24,4 @@
[options]
module.ignore_non_literal_requires=true
sharedmemory.hash_table_pow=21
sharedmemory.hash_table_pow=22
+47 -5
View File
@@ -1,6 +1,6 @@
// @flow
import * as React from 'react';
import { type SelectionState } from './SelectionHandler';
import { type SelectionState, getSelectedEvents } from './SelectionHandler';
import { mapFor } from '../Utils/MapFor';
import uniqBy from 'lodash/uniqBy';
const gd: libGDevelop = global.gd;
@@ -47,6 +47,43 @@ type Props = {|
|}) => React.Node,
|};
/**
* Computes the positions of the first selected event and the search results
* in the flatten event tree and looks for the search result just after the
* first selected event.
*/
const getSearchInitialOffset = (
events: gdEventsList,
resultEvents: Array<gdBaseEvent>,
selection: SelectionState
): number => {
const selectedEvents = getSelectedEvents(selection);
if (!selectedEvents.length) return 0;
const eventsToSearch = [selectedEvents[0], ...resultEvents];
const positionFinder = new gd.EventsPositionFinder();
eventsToSearch.forEach(event => positionFinder.addEventToSearch(event));
positionFinder.launch(events);
const [
selectedEventPosition,
...searchResultsPositions
] = positionFinder.getPositions().toJSArray();
// Search results are considered to be sorted by position
// (top to bottom in the flatten event tree)
for (
let searchResultIndex = 0;
searchResultIndex < searchResultsPositions.length;
searchResultIndex++
) {
if (searchResultsPositions[searchResultIndex] >= selectedEventPosition) {
return searchResultIndex;
}
}
return 0;
};
/**
* Component allowing to do search in events and pass the results
* to its children components, as well as methods to browse the results.
@@ -185,11 +222,16 @@ export default class EventsSearcher extends React.Component<Props, State> {
return null;
}
const { searchFocusOffset } = this.state;
let newSearchFocusOffset =
this.state.searchFocusOffset === null
? 0
: ((this.state.searchFocusOffset || 0) + step) %
this._resultEvents.length;
searchFocusOffset === null || searchFocusOffset === undefined
? getSearchInitialOffset(
this.props.events,
this._resultEvents,
this.props.selection
)
: (searchFocusOffset + step) % this._resultEvents.length;
if (newSearchFocusOffset < 0)
newSearchFocusOffset += this._resultEvents.length;
+1 -2
View File
@@ -332,6 +332,7 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
};
_closeSearchPanel = () => {
if (this._eventSearcher) this._eventSearcher.reset();
this.setState({ showSearchPanel: false });
};
@@ -1309,7 +1310,6 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
replaceInEvents,
goToPreviousSearchResult,
goToNextSearchResult,
clearSearchResults,
}) => (
<div
className="gd-events-sheet"
@@ -1390,7 +1390,6 @@ export class EventsSheetComponentWithoutHandle extends React.Component<
this._ensureEventUnfolded(goToPreviousSearchResult)
}
onCloseSearchPanel={() => {
clearSearchResults();
this._closeSearchPanel();
}}
onGoToNextSearchResult={() =>
@@ -115,28 +115,37 @@ export default class KeyboardShortcuts {
if (onMove) {
if (evt.which === UP_KEY) {
evt.preventDefault();
this._shiftPressed ? onMove(0, -5) : onMove(0, -1);
} else if (evt.which === DOWN_KEY) {
evt.preventDefault();
this._shiftPressed ? onMove(0, 5) : onMove(0, 1);
} else if (evt.which === LEFT_KEY) {
evt.preventDefault();
this._shiftPressed ? onMove(-5, 0) : onMove(-1, 0);
} else if (evt.which === RIGHT_KEY) {
evt.preventDefault();
this._shiftPressed ? onMove(5, 0) : onMove(1, 0);
}
}
if (onDelete && (evt.which === BACKSPACE_KEY || evt.which === DELETE_KEY)) {
evt.preventDefault();
onDelete();
}
if (onCopy && this._isControlOrCmdPressed() && evt.which === C_KEY) {
evt.preventDefault();
onCopy();
}
if (onCut && this._isControlOrCmdPressed() && evt.which === X_KEY) {
evt.preventDefault();
onCut();
}
if (onPaste && this._isControlOrCmdPressed() && evt.which === V_KEY) {
evt.preventDefault();
onPaste();
}
if (onUndo && this._isControlOrCmdPressed() && evt.which === Z_KEY) {
evt.preventDefault();
onUndo();
}
if (
@@ -145,29 +154,37 @@ export default class KeyboardShortcuts {
evt.shiftKey &&
evt.which === Z_KEY
) {
evt.preventDefault();
onRedo();
}
if (onRedo && this._isControlOrCmdPressed() && evt.which === Y_KEY) {
evt.preventDefault();
onRedo();
}
if (onSearch && this._isControlOrCmdPressed() && evt.which === F_KEY) {
evt.preventDefault();
onSearch();
}
if (onEscape && evt.which === ESC_KEY) {
evt.preventDefault();
onEscape();
}
if (onZoomOut && this._isControlOrCmdPressed() && evt.which === MINUS_KEY) {
evt.preventDefault();
onZoomOut(evt);
}
if (onZoomOut && evt.which === NUMPAD_SUBTRACT) {
evt.preventDefault();
onZoomOut(evt);
}
if (onZoomIn && this._isControlOrCmdPressed() && evt.which === EQUAL_KEY) {
evt.preventDefault();
onZoomIn(evt);
}
if (onZoomIn && evt.which === NUMPAD_ADD) {
evt.preventDefault();
onZoomIn(evt);
}
};