Browse Source

Made it safe to erase a component from within its own event, by deferring it for later.

David Piuva 2 years ago
parent
commit
886e481a7f

+ 1 - 1
Source/DFPSR/api/guiAPI.cpp

@@ -474,7 +474,7 @@ String dsr::component_call(const Component& component, const ReadableString& met
 
 
 void dsr::component_detachFromParent(const Component& component) {
 void dsr::component_detachFromParent(const Component& component) {
 	MUST_EXIST(component, component_detachFromParent);
 	MUST_EXIST(component, component_detachFromParent);
-	component->detachFromParent();
+	component->detach = true;
 }
 }
 
 
 Component dsr::component_create(const Component& parent, const ReadableString& className, const ReadableString& identifierName, int index) {
 Component dsr::component_create(const Component& parent, const ReadableString& className, const ReadableString& identifierName, int index) {

+ 15 - 1
Source/DFPSR/gui/VisualComponent.cpp

@@ -187,6 +187,12 @@ static void drawOverlays(ImageRgbaU8& targetImage, VisualComponent &component, c
 
 
 // Offset may become non-zero when the origin is outside of targetImage from being clipped outside of the parent region
 // Offset may become non-zero when the origin is outside of targetImage from being clipped outside of the parent region
 void VisualComponent::draw(ImageRgbaU8& targetImage, const IVector2D& offset) {
 void VisualComponent::draw(ImageRgbaU8& targetImage, const IVector2D& offset) {
+	// TODO: Any more good places to send notifications to make the GUI respond faster?
+	// When about to start drawing from the root, check for state changes and handle events before drawing,
+	//   so that anything needed for visuals is handled without further delay.
+	if (this->parent == nullptr) {
+		this->sendNotifications();
+	}
 	if (this->getVisible()) {
 	if (this->getVisible()) {
 		this->updateChildLocations();
 		this->updateChildLocations();
 		IRect containerBound = this->getLocation() + offset;
 		IRect containerBound = this->getLocation() + offset;
@@ -392,8 +398,16 @@ void VisualComponent::updateIndirectStates() {
 
 
 void VisualComponent::sendNotifications() {
 void VisualComponent::sendNotifications() {
 	// Call recursively for child components while checking what they contain.
 	// Call recursively for child components while checking what they contain.
+	//   Run the loop backwards, so that no components are missed when once is detached.
 	for (int i = this->getChildCount() - 1; i >= 0; i--) {
 	for (int i = this->getChildCount() - 1; i >= 0; i--) {
-		this->children[i]->sendNotifications();
+		// Use a reference counted pointer to the child, so that it can be removed safely outside of custom events.
+		std::shared_ptr<VisualComponent> child = this->children[i];
+		if (child->detach) {
+			child->detach = false;
+			child->detachFromParent();
+		} else {
+			child->sendNotifications();
+		}
 	}
 	}
 	// Detect differences for all flags at once using bits in the integers.
 	// Detect differences for all flags at once using bits in the integers.
 	if (this->currentState != this->previousState) {
 	if (this->currentState != this->previousState) {

+ 3 - 0
Source/DFPSR/gui/VisualComponent.h

@@ -67,6 +67,8 @@ public: // Relations
 	// Remember the component used for a drag event.
 	// Remember the component used for a drag event.
 	//   Ensures that mouse down events are followed by mouse up events on the same component.
 	//   Ensures that mouse down events are followed by mouse up events on the same component.
 	int holdCount = 0;
 	int holdCount = 0;
+	// Marked for removal from the parent when set to true.
+	bool detach = false;
 	// Remember the pressed component for sending mouse move events outside of its region.
 	// Remember the pressed component for sending mouse move events outside of its region.
 	std::shared_ptr<VisualComponent> dragComponent;
 	std::shared_ptr<VisualComponent> dragComponent;
 private: // States
 private: // States
@@ -79,6 +81,7 @@ private: // State updates
 	// Looking for recent state changes and sending notifications through updateStateEvent for each components that had a state change.
 	// Looking for recent state changes and sending notifications through updateStateEvent for each components that had a state change.
 	//   Deferring update notifications using this makes sure that events that trigger updates get to finish before the next one starts.
 	//   Deferring update notifications using this makes sure that events that trigger updates get to finish before the next one starts.
 	//   This reduces the risk of dead-locks, race-conditions, pointer errors...
 	//   This reduces the risk of dead-locks, race-conditions, pointer errors...
+	// Also checking which components are marked for removal and detaching them, so that the object is not deleted while a memeber method is being called.
 	void sendNotifications();
 	void sendNotifications();
 	// Remove the zeroes in addMask from ones in the component and all child components.
 	// Remove the zeroes in addMask from ones in the component and all child components.
 	void applyStateAndMask(ComponentState keepMask);
 	void applyStateAndMask(ComponentState keepMask);