Browse Source

Make the system interface optional

Use C++ chrono facilities for default timing implementation, thus making the system interface no longer abstract. Add tests to ensure we can initialize the library without any interfaces.
Michael Ragazzon 1 year ago
parent
commit
bc5ff06f21

+ 4 - 10
Include/RmlUi/Core/SystemInterface.h

@@ -37,16 +37,10 @@
 namespace Rml {
 
 /**
-    RmlUi's System Interface.
+    RmlUi's system interface provides an interface for time, translation, logging, and other system utilities.
 
-    This class provides interfaces for Time, Translation and Logging.
-
-    Time is the only required implementation.
-
-    The default implemention of Translation doesn't translate anything
-
-    The default implementation of logging logs Windows Debug Console,
-    or Standard Error, depending on what platform you're using.
+    The default logging implementation outputs to the Windows Debug Console on Windows, and Standard Error on other
+    platforms.
 
     @author Lloyd Weehuizen
  */
@@ -58,7 +52,7 @@ public:
 
 	/// Get the number of seconds elapsed since the start of the application.
 	/// @return Elapsed time, in seconds.
-	virtual double GetElapsedTime() = 0;
+	virtual double GetElapsedTime();
 
 	/// Translate the input string into the translated string.
 	/// @param[out] translated Translated string ready for display.

+ 0 - 9
Samples/basic/customlog/src/SystemInterface.cpp

@@ -30,7 +30,6 @@
 #include <RmlUi/Core/Platform.h>
 #include <RmlUi/Core/StringUtilities.h>
 #include <Shell.h>
-#include <chrono>
 #include <stdio.h>
 #ifdef RMLUI_PLATFORM_WIN32
 	#include <RmlUi_Include_Windows.h>
@@ -47,14 +46,6 @@ SystemInterface::~SystemInterface()
 		fclose(fp);
 }
 
-double SystemInterface::GetElapsedTime()
-{
-	static const auto start = std::chrono::steady_clock::now();
-	const auto current = std::chrono::steady_clock::now();
-	std::chrono::duration<double> diff = current - start;
-	return diff.count();
-}
-
 bool SystemInterface::LogMessage(Rml::Log::Type type, const Rml::String& message)
 {
 	if (fp)

+ 0 - 4
Samples/basic/customlog/src/SystemInterface.h

@@ -41,10 +41,6 @@ public:
 	SystemInterface();
 	virtual ~SystemInterface();
 
-	/// Get the number of seconds elapsed since the start of the application.
-	/// @return Elapsed time, in seconds.
-	double GetElapsedTime() override;
-
 	/// Log the specified message.
 	/// @param[in] type Type of log message, ERROR, WARNING, etc.
 	/// @param[in] message Message to log.

+ 5 - 3
Source/Core/Core.cpp

@@ -73,6 +73,7 @@ static FileInterface* file_interface = nullptr;
 static FontEngineInterface* font_interface = nullptr;
 
 // Default interfaces should be created and destroyed on Initialise and Shutdown, respectively.
+static UniquePtr<SystemInterface> default_system_interface;
 static UniquePtr<FileInterface> default_file_interface;
 static UniquePtr<FontEngineInterface> default_font_interface;
 
@@ -94,11 +95,11 @@ bool Initialise()
 {
 	RMLUI_ASSERTMSG(!initialised, "Rml::Initialise() called, but RmlUi is already initialised!");
 
-	// Check for valid interfaces, or install default interfaces as appropriate.
+	// Install default interfaces as appropriate.
 	if (!system_interface)
 	{
-		Log::Message(Log::LT_ERROR, "No system interface set!");
-		return false;
+		default_system_interface = MakeUnique<SystemInterface>();
+		system_interface = default_system_interface.get();
 	}
 
 	if (!file_interface)
@@ -184,6 +185,7 @@ void Shutdown()
 
 	default_font_interface.reset();
 	default_file_interface.reset();
+	default_system_interface.reset();
 
 	ReleaseMemoryPools();
 }

+ 9 - 0
Source/Core/SystemInterface.cpp

@@ -31,6 +31,7 @@
 #include "../../Include/RmlUi/Core/StringUtilities.h"
 #include "../../Include/RmlUi/Core/URL.h"
 #include "LogDefault.h"
+#include <chrono>
 
 namespace Rml {
 
@@ -40,6 +41,14 @@ SystemInterface::SystemInterface() {}
 
 SystemInterface::~SystemInterface() {}
 
+double SystemInterface::GetElapsedTime()
+{
+	static const auto start = std::chrono::steady_clock::now();
+	const auto current = std::chrono::steady_clock::now();
+	std::chrono::duration<double> diff = current - start;
+	return diff.count();
+}
+
 bool SystemInterface::LogMessage(Log::Type type, const String& message)
 {
 	return LogDefault::LogMessage(type, message);

+ 31 - 0
Tests/Source/UnitTests/Core.cpp

@@ -224,3 +224,34 @@ TEST_CASE("core.release_resources")
 
 	render_interface->Reset();
 }
+
+TEST_CASE("core.initialize")
+{
+	TestsRenderInterface* render_interface = TestsShell::GetTestsRenderInterface();
+	// This test only works with the dummy renderer.
+	if (!render_interface)
+		return;
+
+	const Vector2i window_size = {1280, 720};
+
+	SUBCASE("GlobalRenderInterface")
+	{
+		Rml::SetRenderInterface(render_interface);
+		REQUIRE(Rml::CreateContext("invalid_before_initialise", window_size) == nullptr);
+		REQUIRE(Rml::Initialise());
+		REQUIRE(Rml::CreateContext("main", window_size) != nullptr);
+	}
+
+	SUBCASE("ContextRenderInterface")
+	{
+		REQUIRE(Rml::CreateContext("invalid_before_initialise", window_size) == nullptr);
+		// We should be able to initialize without setting any interfaces.
+		REQUIRE(Rml::Initialise());
+		// But then we must pass a render interface to new contexts (this will emit a warning).
+		REQUIRE(Rml::CreateContext("invalid_no_render_interface", window_size) == nullptr);
+		REQUIRE(Rml::CreateContext("main", window_size, render_interface) != nullptr);
+	}
+
+	Rml::Shutdown();
+	render_interface->Reset();
+}