Bladeren bron

Add `Rml::Element::Matches` function (#573)

Jonathan 1 jaar geleden
bovenliggende
commit
632a3ad12c
5 gewijzigde bestanden met toevoegingen van 67 en 0 verwijderingen
  1. 3 0
      Include/RmlUi/Core/Element.h
  2. 22 0
      Source/Core/Element.cpp
  3. 8 0
      Source/Lua/Element.cpp
  4. 1 0
      Source/Lua/Element.h
  5. 33 0
      Tests/Source/UnitTests/Selectors.cpp

+ 3 - 0
Include/RmlUi/Core/Element.h

@@ -561,6 +561,9 @@ public:
 	/// @param[in] selectors The selector or comma-separated selectors to match against.
 	/// @performance Prefer GetElementById/TagName/ClassName whenever possible.
 	void QuerySelectorAll(ElementList& elements, const String& selectors);
+	/// Check if the element matches the given RCSS selector query.
+	/// @return True if the element matches the given RCSS selector query, false otherwise.
+	bool Matches(const String& selectors);
 
 	//@}
 

+ 22 - 0
Source/Core/Element.cpp

@@ -1527,6 +1527,28 @@ void Element::QuerySelectorAll(ElementList& elements, const String& selectors)
 	QuerySelectorAllMatchRecursive(elements, leaf_nodes, this);
 }
 
+bool Element::Matches(const String& selectors)
+{
+	StyleSheetNode root_node;
+	StyleSheetNodeListRaw leaf_nodes = StyleSheetParser::ConstructNodes(root_node, selectors);
+
+	if (leaf_nodes.empty())
+	{
+		Log::Message(Log::LT_WARNING, "Query selector '%s' is empty. In element %s", selectors.c_str(), GetAddress().c_str());
+		return false;
+	}
+
+	for (const StyleSheetNode* node : leaf_nodes)
+	{
+		if (node->IsApplicable(this))
+		{
+			return true;
+		}
+	}
+
+	return false;
+}
+
 EventDispatcher* Element::GetEventDispatcher() const
 {
 	return &meta->event_dispatcher;

+ 8 - 0
Source/Lua/Element.cpp

@@ -211,6 +211,13 @@ int ElementQuerySelectorAll(lua_State* L, Element* obj)
 	return 1;
 }
 
+int ElementMatches(lua_State* L, Element* obj)
+{
+	const char* tag = luaL_checkstring(L, 1);
+	lua_pushboolean(L, obj->Matches(tag));
+	return 1;
+}
+
 int ElementHasAttribute(lua_State* L, Element* obj)
 {
 	const char* name = luaL_checkstring(L, 1);
@@ -594,6 +601,7 @@ RegType<Element> ElementMethods[] = {
 	RMLUI_LUAMETHOD(Element, GetElementsByTagName),
 	RMLUI_LUAMETHOD(Element, QuerySelector),
 	RMLUI_LUAMETHOD(Element, QuerySelectorAll),
+	RMLUI_LUAMETHOD(Element, Matches),
 	RMLUI_LUAMETHOD(Element, HasAttribute),
 	RMLUI_LUAMETHOD(Element, HasChildNodes),
 	RMLUI_LUAMETHOD(Element, InsertBefore),

+ 1 - 0
Source/Lua/Element.h

@@ -51,6 +51,7 @@ int ElementGetElementById(lua_State* L, Element* obj);
 int ElementGetElementsByTagName(lua_State* L, Element* obj);
 int ElementQuerySelector(lua_State* L, Element* obj);
 int ElementQuerySelectorAll(lua_State* L, Element* obj);
+int ElementMatches(lua_State* L, Element* obj);
 int ElementHasAttribute(lua_State* L, Element* obj);
 int ElementHasChildNodes(lua_State* L, Element* obj);
 int ElementInsertBefore(lua_State* L, Element* obj);

+ 33 - 0
Tests/Source/UnitTests/Selectors.cpp

@@ -242,6 +242,22 @@ static const Vector<ClosestSelector> closest_selectors =
 	{ "D1",  ":nth-child(4)",    "D" },
 	{ "D1",  "div:nth-child(4)", "P" },
 };
+
+struct MatchesSelector {
+	String id;
+	String selector;
+	bool expected_result;
+};
+static const Vector<MatchesSelector> matches_selectors =
+{
+	{ "X", ".world",         false },
+	{ "X", ".hello",         true },
+	{ "X", ".hello, .world", true },
+	{ "E", "h3",             true },
+	{ "G", "p#G[class]",     true },
+	{ "G", "p#G[missing]",   false },
+	{ "B", "[unit='m']",     true }
+};
 // clang-format on
 
 // Recursively iterate through 'element' and all of its descendants to find all
@@ -423,5 +439,22 @@ TEST_CASE("Selectors")
 		context->UnloadDocument(document);
 	}
 
+	SUBCASE("Matches")
+	{
+		const String document_string = doc_begin + doc_end;
+		ElementDocument* document = context->LoadDocumentFromMemory(document_string);
+		REQUIRE(document);
+
+		for (const MatchesSelector& selector : matches_selectors)
+		{
+			Element* start = document->GetElementById(selector.id);
+			REQUIRE(start);
+
+			bool matches = start->Matches(selector.selector);
+			CHECK_MESSAGE(matches == selector.expected_result, "Matches() selector '" << selector.selector << "' from " << selector.id);
+		}
+		context->UnloadDocument(document);
+	}
+
 	TestsShell::ShutdownShell();
 }