Browse Source

Moved the stub implementation of clipboard access from TextBox to BackendWindow.

David Piuva 2 years ago
parent
commit
5d6772b271

+ 9 - 0
Source/DFPSR/api/guiAPI.cpp

@@ -508,3 +508,12 @@ String dsr::window_getTitle(Window& window) {
 	MUST_EXIST(window, window_getTitle);
 	MUST_EXIST(window, window_getTitle);
 	return window->backend->getTitle();
 	return window->backend->getTitle();
 }
 }
+String window_loadFromClipboard(Window& window, int64_t timeoutInMilliseconds) {
+	MUST_EXIST(window, window_loadFromClipboard);
+	return window->backend->loadFromClipboard(timeoutInMilliseconds);
+}
+
+void window_saveToClipboard(Window& window, const ReadableString &text) {
+	MUST_EXIST(window, window_saveToClipboard);
+	window->backend->saveToClipboard(text);
+}

+ 11 - 0
Source/DFPSR/api/guiAPI.h

@@ -55,6 +55,17 @@ namespace dsr {
 	// Returns the window's title.
 	// Returns the window's title.
 	String window_getTitle(Window& window);
 	String window_getTitle(Window& window);
 
 
+// Clipboard
+	// Most operating systems require that a window is associated with each request to access the clipboard.
+	//   This allow anti-virus software to see the user consent for accessing the clipboard,
+	//   because a key combination commonly associated with pasting text was pressed before the request was made.
+	// Returns content from an internal or extenal clipboard.
+	//   The internal clipboard within the application is used when the system specific backend has not implemented clipboard access.
+	String window_loadFromClipboard(Window& window, int64_t timeoutInMilliseconds);
+	// Stores the text argument to an internal or extenal clipboard.
+	//   The internal clipboard within the application is used when the system specific backend has not implemented clipboard access.
+	void window_saveToClipboard(Window& window, const ReadableString &text);
+
 // Layout files
 // Layout files
 	// Loading an interface by parsing a layout file's content, with any external resources loaded relative to fromPath.
 	// Loading an interface by parsing a layout file's content, with any external resources loaded relative to fromPath.
 	//   Embedded images do not count as external resources, but file paths need fromPath in order to know from where they will be loaded.
 	//   Embedded images do not count as external resources, but file paths need fromPath in order to know from where they will be loaded.

+ 13 - 0
Source/DFPSR/gui/BackendWindow.cpp

@@ -25,6 +25,19 @@
 
 
 using namespace dsr;
 using namespace dsr;
 
 
+// Used when access to the external clipboard is not implemented.
+static String backupClipboard;
+
+ReadableString BackendWindow::loadFromClipboard(int64_t timeoutInMilliseconds) {
+	sendWarning(U"loadFromClipboard is not implemented! Simulating clipboard using a variable within the same application.");
+	return backupClipboard;
+}
+
+void BackendWindow::saveToClipboard(const ReadableString &text) {
+	sendWarning(U"saveToClipboard is not implemented! Simulating clipboard using a variable within the same application.");
+	backupClipboard = text;
+}
+
 bool BackendWindow::executeEvents() {
 bool BackendWindow::executeEvents() {
 	bool executedEvent = false;
 	bool executedEvent = false;
 	this->prefetchEvents();
 	this->prefetchEvents();

+ 9 - 0
Source/DFPSR/gui/BackendWindow.h

@@ -85,6 +85,15 @@ public:
 	bool visibleCursor = true; // Written to by setCursorVisibility on success.
 	bool visibleCursor = true; // Written to by setCursorVisibility on success.
 	virtual bool setCursorVisibility(bool visible) { return false; } // Returns true on success.
 	virtual bool setCursorVisibility(bool visible) { return false; } // Returns true on success.
 	virtual void setCursorPosition(int x, int y) {} // Does nothing unless implemented.
 	virtual void setCursorPosition(int x, int y) {} // Does nothing unless implemented.
+public:
+	// Clipboard interface
+	//   If none is replaced, both default implementations will use an internal variable.
+	//   If both are implemented, the system's clipboard should be accessed.
+	//   Partial implementations with only loadFromClipboard or saveToClipboard are not allowed.
+	// Load from the clipboard, waiting at most timeoutInMilliseconds milliseconds.
+	virtual ReadableString loadFromClipboard(int64_t timeoutInMilliseconds = 500);
+	// Save text to the clipboard.
+	virtual void saveToClipboard(const ReadableString &text);
 public:
 public:
 	// Each callback declaration has a public variable and a public getter and setter
 	// Each callback declaration has a public variable and a public getter and setter
 	DECLARE_CALLBACK(closeEvent, emptyCallback);
 	DECLARE_CALLBACK(closeEvent, emptyCallback);

+ 15 - 3
Source/DFPSR/gui/DsrWindow.cpp

@@ -90,7 +90,17 @@ DsrWindow::DsrWindow(std::shared_ptr<BackendWindow> backend)
 	this->resetInterface();
 	this->resetInterface();
 }
 }
 
 
-DsrWindow::~DsrWindow() {}
+static void setBackendWindowHandle(std::shared_ptr<VisualComponent> component, std::shared_ptr<BackendWindow> windowHandle) {
+	component->window = windowHandle;
+	for (int c = 0; c < component->children.length(); c++) {
+		setBackendWindowHandle(component->children[c], windowHandle);
+	}
+}
+
+DsrWindow::~DsrWindow() {
+	// Disconnect the backend window from all components, so that handles to components without a DsrWindow will not prevent the BackendWindow from being freed.
+	setBackendWindowHandle(this->mainPanel, std::shared_ptr<BackendWindow>());
+}
 
 
 void DsrWindow::applyLayout() {
 void DsrWindow::applyLayout() {
 	this->mainPanel->applyLayout(IRect(0, 0, this->getCanvasWidth(), this->getCanvasHeight()));
 	this->mainPanel->applyLayout(IRect(0, 0, this->getCanvasWidth(), this->getCanvasHeight()));
@@ -123,12 +133,16 @@ void DsrWindow::resetInterface() {
 		throwError(U"DsrWindow::resetInterface: The window's Panel could not be created!");
 		throwError(U"DsrWindow::resetInterface: The window's Panel could not be created!");
 	}
 	}
 	this->mainPanel->setName("mainPanel");
 	this->mainPanel->setName("mainPanel");
+	// Inherit handle to backend window to access the clipboard.
+	this->mainPanel->window = this->backend;
 	this->applyLayout();
 	this->applyLayout();
 }
 }
 
 
 void DsrWindow::loadInterfaceFromString(String layout, const ReadableString &fromPath) {
 void DsrWindow::loadInterfaceFromString(String layout, const ReadableString &fromPath) {
 	// Load a tree structure of visual components from text
 	// Load a tree structure of visual components from text
 	this->mainPanel = std::dynamic_pointer_cast<VisualComponent>(createPersistentClassFromText(layout, fromPath));
 	this->mainPanel = std::dynamic_pointer_cast<VisualComponent>(createPersistentClassFromText(layout, fromPath));
+	// Re-assign the backend window handle
+	setBackendWindowHandle(this->mainPanel, this->backend);
 	if (this->mainPanel.get() == nullptr) {
 	if (this->mainPanel.get() == nullptr) {
 		throwError(U"DsrWindow::loadInterfaceFromString: The window's root component could not be created!\n\nLayout:\n", layout, "\n");
 		throwError(U"DsrWindow::loadInterfaceFromString: The window's root component could not be created!\n\nLayout:\n", layout, "\n");
 	}
 	}
@@ -233,8 +247,6 @@ void DsrWindow::drawComponents() {
 }
 }
 
 
 AlignedImageRgbaU8 DsrWindow::getCanvas() {
 AlignedImageRgbaU8 DsrWindow::getCanvas() {
-	// TODO: Query if the backend has an optimized upload for a smaller canvas
-	//       Useful if a window backend has GPU accelerated or native upscaling
 	auto fullResolutionCanvas = this->backend->getCanvas();
 	auto fullResolutionCanvas = this->backend->getCanvas();
 	if (this->pixelScale > 1) {
 	if (this->pixelScale > 1) {
 		// Get low resolution canvas in deterministic RGBA pack order
 		// Get low resolution canvas in deterministic RGBA pack order

+ 2 - 0
Source/DFPSR/gui/DsrWindow.h

@@ -41,6 +41,8 @@ void gui_initialize();
 
 
 class DsrWindow {
 class DsrWindow {
 public:
 public:
+	// TODO: Should there be a separate interface context object to reduce the number of variables placed in each component?
+	//       If they all store a handle to the backend window, they could instead have a generic interface object storing pointers to the window, root, active components, et cetera...
 	// Window backend, which the API is allowed to call directly to bypass DsrWindow for trivial operations.
 	// Window backend, which the API is allowed to call directly to bypass DsrWindow for trivial operations.
 	std::shared_ptr<BackendWindow> backend;
 	std::shared_ptr<BackendWindow> backend;
 private:
 private:

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

@@ -262,6 +262,7 @@ void VisualComponent::addChildComponent(std::shared_ptr<VisualComponent> child)
 		this->children.push(child);
 		this->children.push(child);
 		this->childChanged = true;
 		this->childChanged = true;
 		child->parent = this;
 		child->parent = this;
+		child->window = this->window;
 	}
 	}
 }
 }
 
 
@@ -289,6 +290,13 @@ std::shared_ptr<Persistent> VisualComponent::getChild(int index) const {
 	}
 	}
 }
 }
 
 
+static void detachFromWindow(std::shared_ptr<VisualComponent> component) {
+	component->window = std::shared_ptr<BackendWindow>();
+	for (int c = 0; c < component->children.length(); c++) {
+		detachFromWindow(component->children[c]);
+	}
+}
+
 void VisualComponent::detachFromParent() {
 void VisualComponent::detachFromParent() {
 	// Check if there's a parent component
 	// Check if there's a parent component
 	VisualComponent *parent = this->parent;
 	VisualComponent *parent = this->parent;
@@ -298,6 +306,8 @@ void VisualComponent::detachFromParent() {
 		for (int i = 0; i < parent->getChildCount(); i++) {
 		for (int i = 0; i < parent->getChildCount(); i++) {
 			std::shared_ptr<VisualComponent> current = parent->children[i];
 			std::shared_ptr<VisualComponent> current = parent->children[i];
 			if (current.get() == this) {
 			if (current.get() == this) {
+				// Disconnect child from backend window.
+				detachFromWindow(parent->children[i]);
 				// Disconnect parent from child.
 				// Disconnect parent from child.
 				current->parent = nullptr;
 				current->parent = nullptr;
 				// Disconnect child from parent.
 				// Disconnect child from parent.
@@ -632,7 +642,7 @@ void VisualComponent::applyTheme(VisualTheme theme) {
 	this->theme = theme;
 	this->theme = theme;
 	this->changedTheme(theme);
 	this->changedTheme(theme);
 	for (int i = 0; i < this->getChildCount(); i++) {
 	for (int i = 0; i < this->getChildCount(); i++) {
-		this->children[i] -> applyTheme(theme);
+		this->children[i]->applyTheme(theme);
 	}
 	}
 }
 }
 
 

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

@@ -25,7 +25,7 @@
 #define DFPSR_GUI_VISUALCOMPONENT
 #define DFPSR_GUI_VISUALCOMPONENT
 
 
 #include "../persistent/includePersistent.h"
 #include "../persistent/includePersistent.h"
-#include "BackendWindow.h" // TODO: Separate event types from the window module
+#include "BackendWindow.h"
 #include "FlexRegion.h"
 #include "FlexRegion.h"
 #include "InputEvent.h"
 #include "InputEvent.h"
 #include "VisualTheme.h"
 #include "VisualTheme.h"
@@ -58,6 +58,8 @@ static const ComponentState componentState_indirect = 0b101010101010101010101010
 class VisualComponent : public Persistent {
 class VisualComponent : public Persistent {
 PERSISTENT_DECLARATION(VisualComponent)
 PERSISTENT_DECLARATION(VisualComponent)
 public: // Relations
 public: // Relations
+	// Handle to the backend window.
+	std::shared_ptr<BackendWindow> window;
 	// Parent component
 	// Parent component
 	VisualComponent *parent = nullptr;
 	VisualComponent *parent = nullptr;
 	IRect givenSpace; // Remembering the local region that was reserved inside of the parent component.
 	IRect givenSpace; // Remembering the local region that was reserved inside of the parent component.

+ 16 - 13
Source/DFPSR/gui/components/TextBox.cpp

@@ -293,15 +293,6 @@ void TextBox::receiveMouseEvent(const MouseEvent& event) {
 	}
 	}
 }
 }
 
 
-// TODO: Move stub implementation to an API and allow system wrappers to override it with a real implementation copying and pasting across different applications.
-String pasteBinStub;
-void saveToClipBoard(const ReadableString &text) {
-	pasteBinStub = text;
-}
-ReadableString readFromClipBoard() {
-	return pasteBinStub;
-}
-
 ReadableString TextBox::getSelectedText() {
 ReadableString TextBox::getSelectedText() {
 	int64_t selectionLeft = std::min(this->selectionStart, this->beamLocation);
 	int64_t selectionLeft = std::min(this->selectionStart, this->beamLocation);
 	int64_t selectionRight = std::max(this->selectionStart, this->beamLocation);
 	int64_t selectionRight = std::max(this->selectionStart, this->beamLocation);
@@ -428,14 +419,26 @@ void TextBox::receiveKeyboardEvent(const KeyboardEvent& event) {
 				this->placeBeamAtCharacter(getLineEnd(this->text.value, this->beamLocation), removeSelection);
 				this->placeBeamAtCharacter(getLineEnd(this->text.value, this->beamLocation), removeSelection);
 			} else if (event.dsrKey == DsrKey_X) {
 			} else if (event.dsrKey == DsrKey_X) {
 				// Cut selection using Ctrl + X
 				// Cut selection using Ctrl + X
-				saveToClipBoard(this->getSelectedText());
-				this->replaceSelection(U"");
+				if (this->window.get()) {
+					this->window->saveToClipboard(this->getSelectedText());
+					this->replaceSelection(U"");
+				} else {
+					sendWarning(U"No window handle found in TextBox when trying to cut text!");
+				}
 			} else if (event.dsrKey == DsrKey_C) {
 			} else if (event.dsrKey == DsrKey_C) {
 				// Copy selection using Ctrl + C
 				// Copy selection using Ctrl + C
-				saveToClipBoard(this->getSelectedText());
+				if (this->window.get()) {
+					this->window->saveToClipboard(this->getSelectedText());
+				} else {
+					sendWarning(U"No window handle found in TextBox when trying to copy text!");
+				}
 			} else if (event.dsrKey == DsrKey_V) {
 			} else if (event.dsrKey == DsrKey_V) {
 				// Paste selection using Ctrl + V
 				// Paste selection using Ctrl + V
-				this->replaceSelection(readFromClipBoard());
+				if (this->window.get()) {
+					this->replaceSelection(this->window->loadFromClipboard());
+				} else {
+					sendWarning(U"No window handle found in TextBox when trying to paste text!");
+				}
 			} else if (event.dsrKey == DsrKey_A) {
 			} else if (event.dsrKey == DsrKey_A) {
 				// Select all using Ctrl + A
 				// Select all using Ctrl + A
 				this->selectionStart = 0;
 				this->selectionStart = 0;