Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Include/RmlUi/Core/BaseXMLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Stream;
class URL;
using XMLAttributes = Dictionary;

enum class XMLDataType { Text, CData, InnerXML };
enum class XMLDataType { Text, CDATA, InnerXML };

/**
@author Peter Curry
Expand All @@ -55,6 +55,10 @@ class RMLUICORE_API BaseXMLParser {
/// @param[in] tag The tag to register as containing generic character data.
void RegisterCDATATag(const String& tag);

/// Checks if the given tag is registered as a CDATA tag.
/// @param[in] tag The tag to check if it is registered as containing generic character data
bool IsCDATATag(const String& tag);

/// When an XML attribute with the given name is encountered during parsing, then all content below the current
/// node is treated as data.
/// @note While children nodes are treated as data (text), it is assumed that the content represents valid XML.
Expand Down
9 changes: 7 additions & 2 deletions Include/RmlUi/Core/XMLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class RMLUICORE_API XMLParser : public BaseXMLParser {
XMLParser(Element* root);
~XMLParser();

/// Registers a tag were its contents should be treated as CDATA
/// @param[in] _tag The tag for contents to be treated as CDATA
static void PreRegisterCDATATag(const String& _tag);

/// Registers a custom node handler to be used to a given tag.
/// @param[in] tag The tag the custom parser will handle.
/// @param[in] handler The custom handler.
Expand Down Expand Up @@ -95,13 +99,14 @@ class RMLUICORE_API XMLParser : public BaseXMLParser {
/// Returns the source URL of this parse.
const URL& GetSourceURL() const;

/// Called when the parser encounters data.
void HandleData(const String& data, XMLDataType type) override;

protected:
/// Called when the parser finds the beginning of an element tag.
void HandleElementStart(const String& name, const XMLAttributes& attributes) override;
/// Called when the parser finds the end of an element tag.
void HandleElementEnd(const String& name) override;
/// Called when the parser encounters data.
void HandleData(const String& data, XMLDataType type) override;

private:
UniquePtr<DocumentHeader> header;
Expand Down
13 changes: 11 additions & 2 deletions Include/RmlUi/SVG/ElementSVG.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,24 @@ class RMLUICORE_API ElementSVG : public Element {
public:
RMLUI_RTTI_DefineWithParent(ElementSVG, Element)

ElementSVG(const String& tag);
virtual ~ElementSVG();
explicit ElementSVG(const String& tag);
~ElementSVG() override;

/// Returns the element's inherent size.
bool GetIntrinsicDimensions(Vector2f& dimensions, float& ratio) override;

/// Loads the current source file if needed. This normally happens automatically during layouting.
void EnsureSourceLoaded();

/// Sets the dirty flag for the element.
/// @param[in] flag_value The value to set the dirty flag to. Defaults to true.
/// @param[in] force_relayout For layout to be re-evaluated after flag is set. Defaults to true.
void SetDirtyFlag(bool flag_value = true, bool force_relayout = true);

/// Gets the SVG XML data (as text) if using inline SVG, if using a file source this will return a blank string
/// @param[out] content The SVG XML data (as text) or blank string
void GetInnerRML(String& content) const override;

protected:
/// Renders the image.
void OnRender() override;
Expand Down
1 change: 1 addition & 0 deletions Samples/basic/svg/data/svg_decorator.rml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
height: 225px;
margin: auto;
left: 400px;
top: -400px;
}

div.tiger {
Expand Down
1 change: 1 addition & 0 deletions Samples/basic/svg/data/svg_element.rml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
height: 225px;
margin: auto;
left: -400px;
top: -400px;
}

svg {
Expand Down
26 changes: 26 additions & 0 deletions Samples/basic/svg/data/svg_inline.rml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<rml>
<head>
<title>Demo</title>
<link type="text/template" href="/assets/window.rml" />
<style>
body {
width: 300px;
height: 225px;
margin: auto;
left: -400px;
top: 200px;
}

svg {
display: block;
margin: 0 auto;
}
</style>
</head>
<body template="window">
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
<circle cx="25" cy="25" r="20" stroke="black" stroke-width="3" fill="red" />
</svg>
<button id="svg_inline_button">Click Me</button>
</body>
</rml>
47 changes: 44 additions & 3 deletions Samples/basic/svg/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@
#include <RmlUi_Backend.h>
#include <Shell.h>

class ButtonClickEventListener final : public Rml::EventListener {
public:
void ProcessEvent(Rml::Event& event) override;
};

static ButtonClickEventListener click_listener;

void ButtonClickEventListener::ProcessEvent(Rml::Event& event)
{
if (event == Rml::EventId::Click)
{
Rml::Element* current_element = event.GetCurrentElement();
Rml::ElementDocument* document = current_element->GetOwnerDocument();
if (document)
{
Rml::ElementList elements;
document->GetElementsByTagName(elements, "svg");
for (size_t i = 0; i < elements.size(); i++)
{
if (elements[i]->GetAttribute<std::string>("_toggle", "").empty())
{
elements[i]->SetInnerRML("<circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"yellow\" stroke-width=\"3\" fill=\"green\" />");
elements[i]->SetAttribute("_toggle", "1");
}
else
{
elements[i]->SetInnerRML("<circle cx=\"25\" cy=\"25\" r=\"20\" stroke=\"black\" stroke-width=\"3\" fill=\"red\" />");
elements[i]->SetAttribute("_toggle", "");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor formatting thing, but it's nice to use raw string literals R"(...)" when we have a lot of double quotes.

}
}
}
}
}

#if defined RMLUI_PLATFORM_WIN32
#include <RmlUi_Include_Windows.h>
int APIENTRY WinMain(HINSTANCE /*instance_handle*/, HINSTANCE /*previous_instance_handle*/, char* /*command_line*/, int /*command_show*/)
Expand Down Expand Up @@ -73,12 +107,19 @@ int main(int /*argc*/, char** /*argv*/)
Shell::LoadFonts();

// Load and show the documents.
for (const char* filename : {"basic/svg/data/svg_element.rml", "basic/svg/data/svg_decorator.rml"})
std::vector<std::pair<std::string, std::string>> rml_docs = {{"basic/svg/data/svg_element.rml", "SVG Element"},
{"basic/svg/data/svg_decorator.rml", "SVG Decorator"}, {"basic/svg/data/svg_inline.rml", "SVG Inline"}};
for (const auto& rml_doc : rml_docs)
{
if (Rml::ElementDocument* document = context->LoadDocument(filename))
if (Rml::ElementDocument* document = context->LoadDocument(rml_doc.first))
{
document->Show();
document->GetElementById("title")->SetInnerRML("SVG");
document->GetElementById("title")->SetInnerRML(rml_doc.second);
Rml::Element* e = document->GetElementById("svg_inline_button");
if (e != nullptr)
{
e->AddEventListener("click", &click_listener, false);
}
}
}

Expand Down
10 changes: 7 additions & 3 deletions Source/Core/BaseXMLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ void BaseXMLParser::RegisterCDATATag(const String& tag)
cdata_tags.insert(StringUtilities::ToLower(tag));
}

bool BaseXMLParser::IsCDATATag(const String& tag)
{
return cdata_tags.find(StringUtilities::ToLower(tag)) != cdata_tags.end();
}

void BaseXMLParser::RegisterInnerXMLAttribute(const String& attribute_name)
{
attributes_for_inner_xml_data.insert(attribute_name);
Expand Down Expand Up @@ -268,16 +273,15 @@ bool BaseXMLParser::ReadOpenTag()
if (section_opened)
{
const String lcase_tag_name = StringUtilities::ToLower(tag_name);
bool is_cdata_tag = (cdata_tags.find(lcase_tag_name) != cdata_tags.end());

if (is_cdata_tag)
if (IsCDATATag(lcase_tag_name))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should matter too much, but we're doing ToLower an extra time here now. Note to self to do some performance measurements in the end.

{
if (ReadCDATA(lcase_tag_name.c_str()))
{
open_tag_depth--;
if (!data.empty())
{
HandleDataInternal(data, XMLDataType::CData);
HandleDataInternal(data, XMLDataType::CDATA);
data.clear();
}
HandleElementEndInternal(tag_name);
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/Factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,12 @@ ElementPtr Factory::InstanceElement(Element* parent, const String& instancer_nam
bool Factory::InstanceElementText(Element* parent, const String& in_text)
{
RMLUI_ASSERT(parent);
XMLParser parser(parent);
if (parser.IsCDATATag(parent->GetTagName()))
{
parser.HandleData(in_text, XMLDataType::CDATA);
return true;
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change worries me a bit. It doesn't seem right for this function to branch based on the parent, and even knowing about cdata doesn't seem right here.

I wonder if the more appropriate thing is to overload the SetInnerRml call? Then we should also get away with removing most modifications to the XML parser (except the CData setter). We should measure that making that function virtual is not causing any significant performance degradation, but I don't suspect as much.

I'm still open about this one, but at least I think we can make some improvements to better isolate responsibilities. An alternative is perhaps to set the parse_as_rml to true, and then I think it should essentially do the same thing.

Any thoughts?


String text;
if (SystemInterface* system_interface = GetSystemInterface())
Expand Down
32 changes: 32 additions & 0 deletions Source/Core/XMLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
*/

#include "../../Include/RmlUi/Core/XMLParser.h"
#include "../../Include/RmlUi/Core/Element.h"
#include "../../Include/RmlUi/Core/Factory.h"
#include "../../Include/RmlUi/Core/Log.h"
#include "../../Include/RmlUi/Core/Profiling.h"
Expand All @@ -41,6 +42,7 @@ namespace Rml {

struct XmlParserData {
UnorderedMap<String, SharedPtr<XMLNodeHandler>> node_handlers;
UnorderedSet<String> cdata_tags;
SharedPtr<XMLNodeHandler> default_node_handler;
};

Expand All @@ -50,13 +52,30 @@ XMLParser::XMLParser(Element* root)
{
RegisterCDATATag("script");
RegisterCDATATag("style");
for (const String& cdata_tag : xml_parser_data->cdata_tags)
{
RegisterCDATATag(cdata_tag);
}

for (const String& name : Factory::GetStructuralDataViewAttributeNames())
RegisterInnerXMLAttribute(name);

// Add the first frame.
ParseFrame frame;
frame.element = root;
if (root != nullptr)
{
frame.tag = root->GetTagName();
auto itr = xml_parser_data->node_handlers.find(root->GetTagName());
if (itr != xml_parser_data->node_handlers.end())
{
frame.node_handler = itr->second.get();
}
else
{
frame.node_handler = xml_parser_data->default_node_handler.get();
}
}
stack.push(frame);

active_handler = nullptr;
Expand All @@ -66,6 +85,19 @@ XMLParser::XMLParser(Element* root)

XMLParser::~XMLParser() {}

void XMLParser::PreRegisterCDATATag(const String& _tag)
{
if (!xml_parser_data)
xml_parser_data.Initialize();

String tag = StringUtilities::ToLower(_tag);

if (tag.empty())
return;

xml_parser_data->cdata_tags.insert(tag);
}

XMLNodeHandler* XMLParser::RegisterNodeHandler(const String& _tag, SharedPtr<XMLNodeHandler> handler)
{
if (!xml_parser_data)
Expand Down
2 changes: 2 additions & 0 deletions Source/SVG/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ target_sources(rmlui_core PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/SVGCache.h"
"${CMAKE_CURRENT_SOURCE_DIR}/SVGPlugin.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/SVGPlugin.h"
"${CMAKE_CURRENT_SOURCE_DIR}/XMLNodeHandlerSVG.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/XMLNodeHandlerSVG.h"
"${PROJECT_SOURCE_DIR}/Include/RmlUi/SVG/ElementSVG.h"
)

Expand Down
2 changes: 1 addition & 1 deletion Source/SVG/DecoratorSVG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ namespace SVG {

DecoratorDataHandle DecoratorSVG::GenerateElementData(Element* element, BoxArea paint_area) const
{
SharedPtr<SVGData> handle = SVGCache::GetHandle(source_path, element, crop_to_content, paint_area);
SharedPtr<SVGData> handle = SVGCache::GetHandle(source_path, source_path, SVGCache::SOURCE_FILE, element, crop_to_content, paint_area);
if (!handle)
return {};

Expand Down
Loading