Browse Source

Add SFML 3 support to the SFML backend

By default, CMake will now first look for SFML 3, and only if that is not found, it will look for SFML 2. To override the automatic selection, set `RMLUI_SFML_VERSION_MAJOR` to the desired version (2 or 3). Here, an empty value means default (automatic) behavior.
Michael Ragazzon 11 months ago
parent
commit
b261d161a5

+ 73 - 34
Backends/RmlUi_Backend_SFML_GL2.cpp

@@ -36,6 +36,13 @@
 #include <RmlUi/Debugger/Debugger.h>
 #include <SFML/Graphics.hpp>
 #include <SFML/Window.hpp>
+#include <cstdint>
+
+#if SFML_VERSION_MAJOR >= 3
+	#define SFML_COORDINATE(x, y) {x, y}
+#else
+	#define SFML_COORDINATE(x, y) x, y
+#endif
 
 /**
     Custom render interface example for the SFML/GL2 backend.
@@ -82,11 +89,11 @@ public:
 		{
 			for (unsigned int y = 0; y < image.getSize().y; y++)
 			{
-				sf::Color color = image.getPixel(x, y);
-				color.r = (sf::Uint8)((color.r * color.a) / 255);
-				color.g = (sf::Uint8)((color.g * color.a) / 255);
-				color.b = (sf::Uint8)((color.b * color.a) / 255);
-				image.setPixel(x, y, color);
+				sf::Color color = image.getPixel(SFML_COORDINATE(x, y));
+				color.r = static_cast<std::uint8_t>((color.r * color.a) / 255);
+				color.g = static_cast<std::uint8_t>((color.g * color.a) / 255);
+				color.b = static_cast<std::uint8_t>((color.b * color.a) / 255);
+				image.setPixel(SFML_COORDINATE(x, y), color);
 			}
 		}
 
@@ -103,19 +110,22 @@ public:
 		return (Rml::TextureHandle)texture;
 	}
 
-	Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions) override
+	Rml::TextureHandle GenerateTexture(Rml::Span<const Rml::byte> source, Rml::Vector2i source_dimensions_i) override
 	{
-		sf::Texture* texture = new sf::Texture();
-		texture->setSmooth(true);
+		const auto source_dimensions = Rml::Vector2<unsigned int>(source_dimensions_i);
 
+#if SFML_VERSION_MAJOR >= 3
+		sf::Texture* texture = new sf::Texture(sf::Vector2u{source_dimensions.x, source_dimensions.y});
+#else
+		sf::Texture* texture = new sf::Texture();
 		if (!texture->create(source_dimensions.x, source_dimensions.y))
 		{
 			delete texture;
 			return false;
 		}
-
-		texture->update(source.data(), source_dimensions.x, source_dimensions.y, 0, 0);
-
+#endif
+		texture->setSmooth(true);
+		texture->update(source.data(), SFML_COORDINATE(source_dimensions.x, source_dimensions.y), SFML_COORDINATE(0, 0));
 		return (Rml::TextureHandle)texture;
 	}
 
@@ -131,7 +141,7 @@ static void UpdateWindowDimensions(sf::RenderWindow& window, RenderInterface_GL2
 	if (context)
 		context->SetDimensions(Rml::Vector2i(width, height));
 
-	sf::View view(sf::FloatRect(0.f, 0.f, (float)width, (float)height));
+	sf::View view(sf::FloatRect(SFML_COORDINATE(0.f, 0.f), SFML_COORDINATE((float)width, (float)height)));
 	window.setView(view);
 
 	render_interface.SetViewport(width, height);
@@ -156,17 +166,23 @@ bool Backend::Initialize(const char* window_name, int width, int height, bool al
 
 	data = Rml::MakeUnique<BackendData>();
 
+	const std::uint32_t style = (allow_resize ? sf::Style::Default : (sf::Style::Titlebar | sf::Style::Close));
+	constexpr unsigned int anti_aliasing_level = 2;
+
 	// Create the window.
 	sf::RenderWindow out_window;
 	sf::ContextSettings context_settings;
 	context_settings.stencilBits = 8;
-	context_settings.antialiasingLevel = 2;
-
-	const sf::Uint32 style = (allow_resize ? sf::Style::Default : (sf::Style::Titlebar | sf::Style::Close));
 
+#if SFML_VERSION_MAJOR >= 3
+	context_settings.antiAliasingLevel = anti_aliasing_level;
+	data->window.create(sf::VideoMode({(unsigned int)width, (unsigned int)height}), window_name, style, sf::State::Windowed, context_settings);
+#else
+	context_settings.antialiasingLevel = anti_aliasing_level;
 	data->window.create(sf::VideoMode(width, height), window_name, style, context_settings);
-	data->window.setVerticalSyncEnabled(true);
+#endif
 
+	data->window.setVerticalSyncEnabled(true);
 	if (!data->window.isOpen())
 	{
 		data.reset();
@@ -209,34 +225,57 @@ bool Backend::ProcessEvents(Rml::Context* context, KeyDownCallback key_down_call
 	bool result = data->running;
 	data->running = true;
 
+	auto handle_key_pressed = [&](const sf::Event& ev, sf::Keyboard::Key key_pressed_code) {
+		const Rml::Input::KeyIdentifier key = RmlSFML::ConvertKey(key_pressed_code);
+		const int key_modifier = RmlSFML::GetKeyModifierState();
+		const float native_dp_ratio = 1.f;
+
+		// See if we have any global shortcuts that take priority over the context.
+		if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true))
+			return;
+		// Otherwise, hand the event over to the context by calling the input handler as normal.
+		if (!RmlSFML::InputHandler(context, ev))
+			return;
+		// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
+		if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false))
+			return;
+	};
+
+#if SFML_VERSION_MAJOR >= 3
+	while (const std::optional ev = data->window.pollEvent())
+	{
+		if (ev->is<sf::Event::Resized>())
+		{
+			UpdateWindowDimensions(data->window, data->render_interface, context);
+		}
+		else if (auto key_pressed = ev->getIf<sf::Event::KeyPressed>())
+		{
+			handle_key_pressed(*ev, key_pressed->code);
+		}
+		else if (ev->is<sf::Event::Closed>())
+		{
+			result = false;
+		}
+		else
+		{
+			RmlSFML::InputHandler(context, *ev);
+		}
+	}
+
+#else
+
 	sf::Event ev;
 	while (data->window.pollEvent(ev))
 	{
 		switch (ev.type)
 		{
 		case sf::Event::Resized: UpdateWindowDimensions(data->window, data->render_interface, context); break;
-		case sf::Event::KeyPressed:
-		{
-			const Rml::Input::KeyIdentifier key = RmlSFML::ConvertKey(ev.key.code);
-			const int key_modifier = RmlSFML::GetKeyModifierState();
-			const float native_dp_ratio = 1.f;
-
-			// See if we have any global shortcuts that take priority over the context.
-			if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, true))
-				break;
-			// Otherwise, hand the event over to the context by calling the input handler as normal.
-			if (!RmlSFML::InputHandler(context, ev))
-				break;
-			// The key was not consumed by the context either, try keyboard shortcuts of lower priority.
-			if (key_down_callback && !key_down_callback(context, key, key_modifier, native_dp_ratio, false))
-				break;
-		}
-		break;
+		case sf::Event::KeyPressed: handle_key_pressed(ev, ev.key.code); break;
 		case sf::Event::Closed: result = false; break;
 		default: RmlSFML::InputHandler(context, ev); break;
 		}
 	}
-
+#endif
 	return result;
 }
 

+ 184 - 104
Backends/RmlUi_Platform_SFML.cpp

@@ -35,17 +35,42 @@
 #include <SFML/System.hpp>
 #include <SFML/Window.hpp>
 
+#if SFML_VERSION_MAJOR >= 3
+SystemInterface_SFML::Cursors::Cursors() :
+	cursor_default(sf::Cursor::Type::Arrow), cursor_move(sf::Cursor::Type::SizeAll), cursor_pointer(sf::Cursor::Type::Hand),
+	cursor_resize(sf::Cursor::createFromSystem(sf::Cursor::Type::SizeTopLeftBottomRight).value_or(sf::Cursor(sf::Cursor::Type::SizeAll))),
+	cursor_cross(sf::Cursor::Type::Cross), cursor_text(sf::Cursor::Type::Text), cursor_unavailable(sf::Cursor::Type::NotAllowed)
+{}
 SystemInterface_SFML::SystemInterface_SFML()
 {
-	cursors_valid = true;
-	cursors_valid &= cursor_default.loadFromSystem(sf::Cursor::Arrow);
-	cursors_valid &= cursor_move.loadFromSystem(sf::Cursor::SizeAll);
-	cursors_valid &= cursor_pointer.loadFromSystem(sf::Cursor::Hand);
-	cursors_valid &= cursor_resize.loadFromSystem(sf::Cursor::SizeTopLeftBottomRight) || cursor_resize.loadFromSystem(sf::Cursor::SizeAll);
-	cursors_valid &= cursor_cross.loadFromSystem(sf::Cursor::Cross);
-	cursors_valid &= cursor_text.loadFromSystem(sf::Cursor::Text);
-	cursors_valid &= cursor_unavailable.loadFromSystem(sf::Cursor::NotAllowed);
+	try
+	{
+		cursors = std::make_unique<Cursors>();
+	} catch (const sf::Exception& /*exception*/)
+	{
+		cursors.reset();
+	}
+}
+
+#else
+
+SystemInterface_SFML::Cursors::Cursors() {}
+SystemInterface_SFML::SystemInterface_SFML()
+{
+	cursors = std::make_unique<Cursors>();
+	bool cursors_valid = true;
+	cursors_valid &= cursors->cursor_default.loadFromSystem(sf::Cursor::Arrow);
+	cursors_valid &= cursors->cursor_move.loadFromSystem(sf::Cursor::SizeAll);
+	cursors_valid &= cursors->cursor_pointer.loadFromSystem(sf::Cursor::Hand);
+	cursors_valid &=
+		cursors->cursor_resize.loadFromSystem(sf::Cursor::SizeTopLeftBottomRight) || cursors->cursor_resize.loadFromSystem(sf::Cursor::SizeAll);
+	cursors_valid &= cursors->cursor_cross.loadFromSystem(sf::Cursor::Cross);
+	cursors_valid &= cursors->cursor_text.loadFromSystem(sf::Cursor::Text);
+	cursors_valid &= cursors->cursor_unavailable.loadFromSystem(sf::Cursor::NotAllowed);
+	if (!cursors_valid)
+		cursors.reset();
 }
+#endif
 
 void SystemInterface_SFML::SetWindow(sf::RenderWindow* in_window)
 {
@@ -59,25 +84,25 @@ double SystemInterface_SFML::GetElapsedTime()
 
 void SystemInterface_SFML::SetMouseCursor(const Rml::String& cursor_name)
 {
-	if (cursors_valid && window)
+	if (cursors && window)
 	{
 		sf::Cursor* cursor = nullptr;
 		if (cursor_name.empty() || cursor_name == "arrow")
-			cursor = &cursor_default;
+			cursor = &cursors->cursor_default;
 		else if (cursor_name == "move")
-			cursor = &cursor_move;
+			cursor = &cursors->cursor_move;
 		else if (cursor_name == "pointer")
-			cursor = &cursor_pointer;
+			cursor = &cursors->cursor_pointer;
 		else if (cursor_name == "resize")
-			cursor = &cursor_resize;
+			cursor = &cursors->cursor_resize;
 		else if (cursor_name == "cross")
-			cursor = &cursor_cross;
+			cursor = &cursors->cursor_cross;
 		else if (cursor_name == "text")
-			cursor = &cursor_text;
+			cursor = &cursors->cursor_text;
 		else if (cursor_name == "unavailable")
-			cursor = &cursor_unavailable;
+			cursor = &cursors->cursor_unavailable;
 		else if (Rml::StringUtilities::StartsWith(cursor_name, "rmlui-scroll"))
-			cursor = &cursor_move;
+			cursor = &cursors->cursor_move;
 
 		if (cursor)
 			window->setMouseCursor(*cursor);
@@ -94,10 +119,56 @@ void SystemInterface_SFML::GetClipboardText(Rml::String& text)
 	text = sf::Clipboard::getString();
 }
 
-bool RmlSFML::InputHandler(Rml::Context* context, sf::Event& ev)
+bool RmlSFML::InputHandler(Rml::Context* context, const sf::Event& ev)
 {
 	bool result = true;
 
+#if SFML_VERSION_MAJOR >= 3
+
+	if (auto mouse_moved = ev.getIf<sf::Event::MouseMoved>())
+	{
+		result = context->ProcessMouseMove(mouse_moved->position.x, mouse_moved->position.y, RmlSFML::GetKeyModifierState());
+	}
+	else if (auto mouse_pressed = ev.getIf<sf::Event::MouseButtonPressed>())
+	{
+		result = context->ProcessMouseButtonDown(int(mouse_pressed->button), RmlSFML::GetKeyModifierState());
+	}
+	else if (auto mouse_released = ev.getIf<sf::Event::MouseButtonReleased>())
+	{
+		result = context->ProcessMouseButtonUp(int(mouse_released->button), RmlSFML::GetKeyModifierState());
+	}
+	else if (auto wheel_scrolled = ev.getIf<sf::Event::MouseWheelScrolled>())
+	{
+		const Rml::Vector2f delta = {
+			wheel_scrolled->wheel == sf::Mouse::Wheel::Horizontal ? -wheel_scrolled->delta : 0.f,
+			wheel_scrolled->wheel == sf::Mouse::Wheel::Vertical ? -wheel_scrolled->delta : 0.f,
+		};
+		result = context->ProcessMouseWheel(delta, RmlSFML::GetKeyModifierState());
+	}
+	else if (ev.is<sf::Event::MouseLeft>())
+	{
+		result = context->ProcessMouseLeave();
+	}
+	else if (auto text_entered = ev.getIf<sf::Event::TextEntered>())
+	{
+		Rml::Character character = Rml::Character(text_entered->unicode);
+		if (character == Rml::Character('\r'))
+			character = Rml::Character('\n');
+
+		if (text_entered->unicode >= 32 || character == Rml::Character('\n'))
+			result = context->ProcessTextInput(character);
+	}
+	else if (auto key_pressed = ev.getIf<sf::Event::KeyPressed>())
+	{
+		result = context->ProcessKeyDown(RmlSFML::ConvertKey(key_pressed->code), RmlSFML::GetKeyModifierState());
+	}
+	else if (auto key_released = ev.getIf<sf::Event::KeyReleased>())
+	{
+		result = context->ProcessKeyUp(RmlSFML::ConvertKey(key_released->code), RmlSFML::GetKeyModifierState());
+	}
+
+#else
+
 	switch (ev.type)
 	{
 	case sf::Event::MouseMoved: result = context->ProcessMouseMove(ev.mouseMove.x, ev.mouseMove.y, RmlSFML::GetKeyModifierState()); break;
@@ -119,98 +190,107 @@ bool RmlSFML::InputHandler(Rml::Context* context, sf::Event& ev)
 	case sf::Event::KeyReleased: result = context->ProcessKeyUp(RmlSFML::ConvertKey(ev.key.code), RmlSFML::GetKeyModifierState()); break;
 	default: break;
 	}
+#endif
 
 	return result;
 }
 
 Rml::Input::KeyIdentifier RmlSFML::ConvertKey(sf::Keyboard::Key sfml_key)
 {
+#if SFML_VERSION_MAJOR >= 3
+	constexpr auto sfBackspace = sf::Keyboard::Key::Backspace;
+	constexpr auto sfEnter = sf::Keyboard::Key::Enter;
+#else
+	constexpr auto sfBackspace = sf::Keyboard::Key::BackSpace;
+	constexpr auto sfEnter = sf::Keyboard::Key::Return;
+#endif
+
 	// clang-format off
 	switch (sfml_key)
 	{
-	case sf::Keyboard::A:         return Rml::Input::KI_A;
-	case sf::Keyboard::B:         return Rml::Input::KI_B;
-	case sf::Keyboard::C:         return Rml::Input::KI_C;
-	case sf::Keyboard::D:         return Rml::Input::KI_D;
-	case sf::Keyboard::E:         return Rml::Input::KI_E;
-	case sf::Keyboard::F:         return Rml::Input::KI_F;
-	case sf::Keyboard::G:         return Rml::Input::KI_G;
-	case sf::Keyboard::H:         return Rml::Input::KI_H;
-	case sf::Keyboard::I:         return Rml::Input::KI_I;
-	case sf::Keyboard::J:         return Rml::Input::KI_J;
-	case sf::Keyboard::K:         return Rml::Input::KI_K;
-	case sf::Keyboard::L:         return Rml::Input::KI_L;
-	case sf::Keyboard::M:         return Rml::Input::KI_M;
-	case sf::Keyboard::N:         return Rml::Input::KI_N;
-	case sf::Keyboard::O:         return Rml::Input::KI_O;
-	case sf::Keyboard::P:         return Rml::Input::KI_P;
-	case sf::Keyboard::Q:         return Rml::Input::KI_Q;
-	case sf::Keyboard::R:         return Rml::Input::KI_R;
-	case sf::Keyboard::S:         return Rml::Input::KI_S;
-	case sf::Keyboard::T:         return Rml::Input::KI_T;
-	case sf::Keyboard::U:         return Rml::Input::KI_U;
-	case sf::Keyboard::V:         return Rml::Input::KI_V;
-	case sf::Keyboard::W:         return Rml::Input::KI_W;
-	case sf::Keyboard::X:         return Rml::Input::KI_X;
-	case sf::Keyboard::Y:         return Rml::Input::KI_Y;
-	case sf::Keyboard::Z:         return Rml::Input::KI_Z;
-	case sf::Keyboard::Num0:      return Rml::Input::KI_0;
-	case sf::Keyboard::Num1:      return Rml::Input::KI_1;
-	case sf::Keyboard::Num2:      return Rml::Input::KI_2;
-	case sf::Keyboard::Num3:      return Rml::Input::KI_3;
-	case sf::Keyboard::Num4:      return Rml::Input::KI_4;
-	case sf::Keyboard::Num5:      return Rml::Input::KI_5;
-	case sf::Keyboard::Num6:      return Rml::Input::KI_6;
-	case sf::Keyboard::Num7:      return Rml::Input::KI_7;
-	case sf::Keyboard::Num8:      return Rml::Input::KI_8;
-	case sf::Keyboard::Num9:      return Rml::Input::KI_9;
-	case sf::Keyboard::Numpad0:   return Rml::Input::KI_NUMPAD0;
-	case sf::Keyboard::Numpad1:   return Rml::Input::KI_NUMPAD1;
-	case sf::Keyboard::Numpad2:   return Rml::Input::KI_NUMPAD2;
-	case sf::Keyboard::Numpad3:   return Rml::Input::KI_NUMPAD3;
-	case sf::Keyboard::Numpad4:   return Rml::Input::KI_NUMPAD4;
-	case sf::Keyboard::Numpad5:   return Rml::Input::KI_NUMPAD5;
-	case sf::Keyboard::Numpad6:   return Rml::Input::KI_NUMPAD6;
-	case sf::Keyboard::Numpad7:   return Rml::Input::KI_NUMPAD7;
-	case sf::Keyboard::Numpad8:   return Rml::Input::KI_NUMPAD8;
-	case sf::Keyboard::Numpad9:   return Rml::Input::KI_NUMPAD9;
-	case sf::Keyboard::Left:      return Rml::Input::KI_LEFT;
-	case sf::Keyboard::Right:     return Rml::Input::KI_RIGHT;
-	case sf::Keyboard::Up:        return Rml::Input::KI_UP;
-	case sf::Keyboard::Down:      return Rml::Input::KI_DOWN;
-	case sf::Keyboard::Add:       return Rml::Input::KI_ADD;
-	case sf::Keyboard::BackSpace: return Rml::Input::KI_BACK;
-	case sf::Keyboard::Delete:    return Rml::Input::KI_DELETE;
-	case sf::Keyboard::Divide:    return Rml::Input::KI_DIVIDE;
-	case sf::Keyboard::End:       return Rml::Input::KI_END;
-	case sf::Keyboard::Escape:    return Rml::Input::KI_ESCAPE;
-	case sf::Keyboard::F1:        return Rml::Input::KI_F1;
-	case sf::Keyboard::F2:        return Rml::Input::KI_F2;
-	case sf::Keyboard::F3:        return Rml::Input::KI_F3;
-	case sf::Keyboard::F4:        return Rml::Input::KI_F4;
-	case sf::Keyboard::F5:        return Rml::Input::KI_F5;
-	case sf::Keyboard::F6:        return Rml::Input::KI_F6;
-	case sf::Keyboard::F7:        return Rml::Input::KI_F7;
-	case sf::Keyboard::F8:        return Rml::Input::KI_F8;
-	case sf::Keyboard::F9:        return Rml::Input::KI_F9;
-	case sf::Keyboard::F10:       return Rml::Input::KI_F10;
-	case sf::Keyboard::F11:       return Rml::Input::KI_F11;
-	case sf::Keyboard::F12:       return Rml::Input::KI_F12;
-	case sf::Keyboard::F13:       return Rml::Input::KI_F13;
-	case sf::Keyboard::F14:       return Rml::Input::KI_F14;
-	case sf::Keyboard::F15:       return Rml::Input::KI_F15;
-	case sf::Keyboard::Home:      return Rml::Input::KI_HOME;
-	case sf::Keyboard::Insert:    return Rml::Input::KI_INSERT;
-	case sf::Keyboard::LControl:  return Rml::Input::KI_LCONTROL;
-	case sf::Keyboard::LShift:    return Rml::Input::KI_LSHIFT;
-	case sf::Keyboard::Multiply:  return Rml::Input::KI_MULTIPLY;
-	case sf::Keyboard::Pause:     return Rml::Input::KI_PAUSE;
-	case sf::Keyboard::RControl:  return Rml::Input::KI_RCONTROL;
-	case sf::Keyboard::Return:    return Rml::Input::KI_RETURN;
-	case sf::Keyboard::RShift:    return Rml::Input::KI_RSHIFT;
-	case sf::Keyboard::Space:     return Rml::Input::KI_SPACE;
-	case sf::Keyboard::Subtract:  return Rml::Input::KI_SUBTRACT;
-	case sf::Keyboard::Tab:       return Rml::Input::KI_TAB;
+	case sf::Keyboard::Key::A:         return Rml::Input::KI_A;
+	case sf::Keyboard::Key::B:         return Rml::Input::KI_B;
+	case sf::Keyboard::Key::C:         return Rml::Input::KI_C;
+	case sf::Keyboard::Key::D:         return Rml::Input::KI_D;
+	case sf::Keyboard::Key::E:         return Rml::Input::KI_E;
+	case sf::Keyboard::Key::F:         return Rml::Input::KI_F;
+	case sf::Keyboard::Key::G:         return Rml::Input::KI_G;
+	case sf::Keyboard::Key::H:         return Rml::Input::KI_H;
+	case sf::Keyboard::Key::I:         return Rml::Input::KI_I;
+	case sf::Keyboard::Key::J:         return Rml::Input::KI_J;
+	case sf::Keyboard::Key::K:         return Rml::Input::KI_K;
+	case sf::Keyboard::Key::L:         return Rml::Input::KI_L;
+	case sf::Keyboard::Key::M:         return Rml::Input::KI_M;
+	case sf::Keyboard::Key::N:         return Rml::Input::KI_N;
+	case sf::Keyboard::Key::O:         return Rml::Input::KI_O;
+	case sf::Keyboard::Key::P:         return Rml::Input::KI_P;
+	case sf::Keyboard::Key::Q:         return Rml::Input::KI_Q;
+	case sf::Keyboard::Key::R:         return Rml::Input::KI_R;
+	case sf::Keyboard::Key::S:         return Rml::Input::KI_S;
+	case sf::Keyboard::Key::T:         return Rml::Input::KI_T;
+	case sf::Keyboard::Key::U:         return Rml::Input::KI_U;
+	case sf::Keyboard::Key::V:         return Rml::Input::KI_V;
+	case sf::Keyboard::Key::W:         return Rml::Input::KI_W;
+	case sf::Keyboard::Key::X:         return Rml::Input::KI_X;
+	case sf::Keyboard::Key::Y:         return Rml::Input::KI_Y;
+	case sf::Keyboard::Key::Z:         return Rml::Input::KI_Z;
+	case sf::Keyboard::Key::Num0:      return Rml::Input::KI_0;
+	case sf::Keyboard::Key::Num1:      return Rml::Input::KI_1;
+	case sf::Keyboard::Key::Num2:      return Rml::Input::KI_2;
+	case sf::Keyboard::Key::Num3:      return Rml::Input::KI_3;
+	case sf::Keyboard::Key::Num4:      return Rml::Input::KI_4;
+	case sf::Keyboard::Key::Num5:      return Rml::Input::KI_5;
+	case sf::Keyboard::Key::Num6:      return Rml::Input::KI_6;
+	case sf::Keyboard::Key::Num7:      return Rml::Input::KI_7;
+	case sf::Keyboard::Key::Num8:      return Rml::Input::KI_8;
+	case sf::Keyboard::Key::Num9:      return Rml::Input::KI_9;
+	case sf::Keyboard::Key::Numpad0:   return Rml::Input::KI_NUMPAD0;
+	case sf::Keyboard::Key::Numpad1:   return Rml::Input::KI_NUMPAD1;
+	case sf::Keyboard::Key::Numpad2:   return Rml::Input::KI_NUMPAD2;
+	case sf::Keyboard::Key::Numpad3:   return Rml::Input::KI_NUMPAD3;
+	case sf::Keyboard::Key::Numpad4:   return Rml::Input::KI_NUMPAD4;
+	case sf::Keyboard::Key::Numpad5:   return Rml::Input::KI_NUMPAD5;
+	case sf::Keyboard::Key::Numpad6:   return Rml::Input::KI_NUMPAD6;
+	case sf::Keyboard::Key::Numpad7:   return Rml::Input::KI_NUMPAD7;
+	case sf::Keyboard::Key::Numpad8:   return Rml::Input::KI_NUMPAD8;
+	case sf::Keyboard::Key::Numpad9:   return Rml::Input::KI_NUMPAD9;
+	case sf::Keyboard::Key::Left:      return Rml::Input::KI_LEFT;
+	case sf::Keyboard::Key::Right:     return Rml::Input::KI_RIGHT;
+	case sf::Keyboard::Key::Up:        return Rml::Input::KI_UP;
+	case sf::Keyboard::Key::Down:      return Rml::Input::KI_DOWN;
+	case sf::Keyboard::Key::Add:       return Rml::Input::KI_ADD;
+	case sfBackspace:                  return Rml::Input::KI_BACK;
+	case sf::Keyboard::Key::Delete:    return Rml::Input::KI_DELETE;
+	case sf::Keyboard::Key::Divide:    return Rml::Input::KI_DIVIDE;
+	case sf::Keyboard::Key::End:       return Rml::Input::KI_END;
+	case sf::Keyboard::Key::Escape:    return Rml::Input::KI_ESCAPE;
+	case sf::Keyboard::Key::F1:        return Rml::Input::KI_F1;
+	case sf::Keyboard::Key::F2:        return Rml::Input::KI_F2;
+	case sf::Keyboard::Key::F3:        return Rml::Input::KI_F3;
+	case sf::Keyboard::Key::F4:        return Rml::Input::KI_F4;
+	case sf::Keyboard::Key::F5:        return Rml::Input::KI_F5;
+	case sf::Keyboard::Key::F6:        return Rml::Input::KI_F6;
+	case sf::Keyboard::Key::F7:        return Rml::Input::KI_F7;
+	case sf::Keyboard::Key::F8:        return Rml::Input::KI_F8;
+	case sf::Keyboard::Key::F9:        return Rml::Input::KI_F9;
+	case sf::Keyboard::Key::F10:       return Rml::Input::KI_F10;
+	case sf::Keyboard::Key::F11:       return Rml::Input::KI_F11;
+	case sf::Keyboard::Key::F12:       return Rml::Input::KI_F12;
+	case sf::Keyboard::Key::F13:       return Rml::Input::KI_F13;
+	case sf::Keyboard::Key::F14:       return Rml::Input::KI_F14;
+	case sf::Keyboard::Key::F15:       return Rml::Input::KI_F15;
+	case sf::Keyboard::Key::Home:      return Rml::Input::KI_HOME;
+	case sf::Keyboard::Key::Insert:    return Rml::Input::KI_INSERT;
+	case sf::Keyboard::Key::LControl:  return Rml::Input::KI_LCONTROL;
+	case sf::Keyboard::Key::LShift:    return Rml::Input::KI_LSHIFT;
+	case sf::Keyboard::Key::Multiply:  return Rml::Input::KI_MULTIPLY;
+	case sf::Keyboard::Key::Pause:     return Rml::Input::KI_PAUSE;
+	case sf::Keyboard::Key::RControl:  return Rml::Input::KI_RCONTROL;
+	case sfEnter:                      return Rml::Input::KI_RETURN;
+	case sf::Keyboard::Key::RShift:    return Rml::Input::KI_RSHIFT;
+	case sf::Keyboard::Key::Space:     return Rml::Input::KI_SPACE;
+	case sf::Keyboard::Key::Subtract:  return Rml::Input::KI_SUBTRACT;
+	case sf::Keyboard::Key::Tab:       return Rml::Input::KI_TAB;
 	default: break;
 	}
 	// clang-format on
@@ -222,13 +302,13 @@ int RmlSFML::GetKeyModifierState()
 {
 	int modifiers = 0;
 
-	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::RShift))
+	if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RShift))
 		modifiers |= Rml::Input::KM_SHIFT;
 
-	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::RControl))
+	if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LControl) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RControl))
 		modifiers |= Rml::Input::KM_CTRL;
 
-	if (sf::Keyboard::isKeyPressed(sf::Keyboard::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::RAlt))
+	if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LAlt) || sf::Keyboard::isKeyPressed(sf::Keyboard::Key::RAlt))
 		modifiers |= Rml::Input::KM_ALT;
 
 	return modifiers;

+ 13 - 10
Backends/RmlUi_Platform_SFML.h

@@ -58,24 +58,27 @@ private:
 	sf::Clock timer;
 	sf::RenderWindow* window = nullptr;
 
-	bool cursors_valid = false;
-	sf::Cursor cursor_default;
-	sf::Cursor cursor_move;
-	sf::Cursor cursor_pointer;
-	sf::Cursor cursor_resize;
-	sf::Cursor cursor_cross;
-	sf::Cursor cursor_text;
-	sf::Cursor cursor_unavailable;
+	struct Cursors {
+		Cursors();
+		sf::Cursor cursor_default;
+		sf::Cursor cursor_move;
+		sf::Cursor cursor_pointer;
+		sf::Cursor cursor_resize;
+		sf::Cursor cursor_cross;
+		sf::Cursor cursor_text;
+		sf::Cursor cursor_unavailable;
+	};
+	Rml::UniquePtr<Cursors> cursors;
 };
 
 /**
-    Optional helper functions for the SFML plaform.
+    Optional helper functions for the SFML platform.
  */
 namespace RmlSFML {
 
 // Applies input on the context based on the given SFML event.
 // @return True if the event is still propagating, false if it was handled by the context.
-bool InputHandler(Rml::Context* context, sf::Event& ev);
+bool InputHandler(Rml::Context* context, const sf::Event& ev);
 
 // Converts the SFML key to RmlUi key.
 Rml::Input::KeyIdentifier ConvertKey(sf::Keyboard::Key sfml_key);

+ 25 - 22
CMake/DependenciesForBackends.cmake

@@ -74,48 +74,44 @@ endif()
 
 # SFML
 if(RMLUI_BACKEND MATCHES "^SFML")
+	set(RMLUI_SFML_VERSION_MAJOR "" CACHE STRING "Major version of SFML to search for, or empty for automatic search.")
+	mark_as_advanced(RMLUI_SFML_VERSION_MAJOR)
+
 	#[[
-		Starting with SFML 2.7, the recommended method to find the library is using
+		Starting with SFML 3.0, the recommended method to find the library is using
 		the official config file which sets up targets for each module of the library.
 		The names of the targets follow the namespaced target names convention.
 
 		When one of the required modules isn't present as a SFML::<module> target,
-		that means SFML < 2.7 is being used and we need to set up the target(s) by ourselves.
+		that means SFML < 3.0 is being used and we need to set up the target(s) by ourselves.
 
 		In SFML 2.5 the first iteration of the SFMLConfig.cmake file was introduced, which
 		uses a target-oriented approach to exposing the different modules of SFML but it doesn't
-		use the same names as the config file from SFML 2.7.
+		use the same names as the config file from SFML 3.0.
 	]]
 
 	# List of required components in capital case
 	set(RMLUI_SFML_REQUIRED_COMPONENTS "Graphics" "Window" "System")
 
-	# Run find package with component names both capitalized and lower-cased
-	find_package("SFML" "2" COMPONENTS ${RMLUI_SFML_REQUIRED_COMPONENTS} QUIET)
-	if(NOT SFML_FOUND)
-		list(TRANSFORM RMLUI_SFML_REQUIRED_COMPONENTS TOLOWER OUTPUT_VARIABLE RMLUI_SFML_REQUIRED_COMPONENTS_LOWER_CASE)
-		find_package("SFML" "2" COMPONENTS ${RMLUI_SFML_REQUIRED_COMPONENTS_LOWER_CASE} QUIET)
+	# Look for SFML 3 first. We always require the window module, so use that to test if the dependency has been found.
+	if(NOT TARGET SFML::Window AND (RMLUI_SFML_VERSION_MAJOR EQUAL "3" OR RMLUI_SFML_VERSION_MAJOR STREQUAL ""))
+		find_package("SFML" "3" COMPONENTS ${RMLUI_SFML_REQUIRED_COMPONENTS} QUIET)
 	endif()
 
-	# Since we are using find_package() in basic mode, we can check the _FOUND variable
-	if(NOT SFML_FOUND)
-		report_dependency_not_found("SFML" "SFML" SFML::SFML)
+	# Look for SFML 2 next unless another version is found.
+	if(NOT TARGET SFML::Window AND (RMLUI_SFML_VERSION_MAJOR EQUAL "2" OR RMLUI_SFML_VERSION_MAJOR STREQUAL ""))
+		list(TRANSFORM RMLUI_SFML_REQUIRED_COMPONENTS TOLOWER OUTPUT_VARIABLE RMLUI_SFML_REQUIRED_COMPONENTS_LOWER_CASE)
+		find_package("SFML" "2" COMPONENTS ${RMLUI_SFML_REQUIRED_COMPONENTS_LOWER_CASE} QUIET)
 	endif()
 
-	#[[
-		Since the RmlUi CMake project uses the SFML 2.7 namespaced target names, if the version is lower wrappers
-		need to be set up.
-
-		Because we always require the window module, we can use it to determine which iteration of the config.
-	]]
-
-	# If any of the mandatory SFML 2.7 targets isn't present, assume SFML < 2.7 has been found and set up wrappers
 	if(NOT TARGET SFML::Window)
 		#[[
-			If sfml-window exists, then that means the version is either SFML 2.5 or 2.6 which set up
-			module-specific CMake targets but with different names using a config file.
+			Since the RmlUi CMake project uses the SFML 3.0 namespaced target names, if the version is lower then wrappers
+			need to be set up.
 
-			Therefore, we need to alias the target names to match those declared by SFML 2.7 and used by RmlUi.
+			If e.g. sfml-window exists, then that means the version is either SFML 2.5 or 2.6 which set up
+			module-specific CMake targets but with different names using a config file. Therefore, we need to alias the
+			target names to match those declared by SFML 3.0 and used by RmlUi.
 		]]
 
 		# For each SFML component the project requires
@@ -140,6 +136,13 @@ if(RMLUI_BACKEND MATCHES "^SFML")
 			endif()
 		endforeach()
 	endif()
+
+	if(NOT TARGET SFML::Window)
+		list(TRANSFORM RMLUI_SFML_REQUIRED_COMPONENTS PREPEND "SFML::" OUTPUT_VARIABLE RMLUI_SFML_REQUIRED_TARGETS)
+		report_dependency_not_found("SFML" "SFML" "${RMLUI_SFML_REQUIRED_TARGETS}")
+	endif()
+
+	report_dependency_found("SFML" SFML)
 endif()
 
 # X11