Browse Source

Merge branch 'develop'. We're now at RmlUi 3.0!

# Conflicts:
#	readme.md
Michael Ragazzon 6 years ago
parent
commit
c7fb5df5c3
100 changed files with 5726 additions and 3200 deletions
  1. 25 24
      .travis.yml
  2. 57 73
      CMake/FileList.cmake
  3. 0 0
      CMake/Modules/FindCarbon.cmake
  4. 0 0
      CMake/Modules/FindDirectX.cmake
  5. 0 0
      CMake/Modules/FindPkgMacros.cmake
  6. 0 0
      CMake/Modules/FindSDL2.cmake
  7. 0 0
      CMake/Modules/FindSDL2_image.cmake
  8. 0 0
      CMake/Modules/FindSFML.cmake
  9. 0 0
      CMake/Platform/iOS.cmake
  10. 0 0
      CMake/RmlUiConfig.cmake.build.in
  11. 0 0
      CMake/RmlUiConfig.cmake.install.in
  12. 32 14
      CMake/SampleFileList.cmake
  13. 0 0
      CMake/builddist.py
  14. 25 5
      CMake/gen_filelists.sh
  15. 5 5
      CMake/gen_samplelists.sh
  16. 0 0
      CMake/plist/RocketControlsOSX-Info.plist
  17. 0 0
      CMake/plist/RocketCoreOSX-Info.plist
  18. 0 0
      CMake/plist/RocketDebuggerOSX-Info.plist
  19. 47 20
      CMakeLists.txt
  20. 1 0
      Dependencies/.gitignore
  21. 0 62
      Include/RmlUi/Controls/Clipboard.h
  22. 2 4
      Include/RmlUi/Controls/DataFormatter.h
  23. 4 4
      Include/RmlUi/Controls/DataQuery.h
  24. 2 3
      Include/RmlUi/Controls/DataSource.h
  25. 1 1
      Include/RmlUi/Controls/DataSourceListener.h
  26. 4 6
      Include/RmlUi/Controls/ElementDataGrid.h
  27. 2 5
      Include/RmlUi/Controls/ElementDataGridCell.h
  28. 1 1
      Include/RmlUi/Controls/ElementDataGridExpandButton.h
  29. 7 12
      Include/RmlUi/Controls/ElementDataGridRow.h
  30. 1 1
      Include/RmlUi/Controls/ElementFormControl.h
  31. 7 7
      Include/RmlUi/Controls/ElementFormControlDataSelect.h
  32. 13 11
      Include/RmlUi/Controls/ElementFormControlInput.h
  33. 7 7
      Include/RmlUi/Controls/ElementFormControlSelect.h
  34. 11 9
      Include/RmlUi/Controls/ElementFormControlTextArea.h
  35. 7 14
      Include/RmlUi/Controls/ElementTabSet.h
  36. 1 1
      Include/RmlUi/Controls/SelectOption.h
  37. 3 5
      Include/RmlUi/Core/Animation.h
  38. 7 4
      Include/RmlUi/Core/BaseXMLParser.h
  39. 0 102
      Include/RmlUi/Core/BitmapFont/FontProvider.h
  40. 227 0
      Include/RmlUi/Core/ComputedValues.h
  41. 755 0
      Include/RmlUi/Core/Containers/chobo/flat_map.hpp
  42. 561 0
      Include/RmlUi/Core/Containers/chobo/flat_set.hpp
  43. 2056 0
      Include/RmlUi/Core/Containers/robin_hood.h
  44. 44 65
      Include/RmlUi/Core/Context.h
  45. 4 6
      Include/RmlUi/Core/ContextInstancer.h
  46. 54 20
      Include/RmlUi/Core/Core.h
  47. 16 9
      Include/RmlUi/Core/Debug.h
  48. 15 38
      Include/RmlUi/Core/Decorator.h
  49. 23 16
      Include/RmlUi/Core/DecoratorInstancer.h
  50. 22 109
      Include/RmlUi/Core/Dictionary.h
  51. 0 70
      Include/RmlUi/Core/Dictionary.inl
  52. 138 185
      Include/RmlUi/Core/Element.h
  53. 9 14
      Include/RmlUi/Core/Element.inl
  54. 54 61
      Include/RmlUi/Core/ElementDocument.h
  55. 65 11
      Include/RmlUi/Core/ElementInstancer.h
  56. 0 63
      Include/RmlUi/Core/ElementInstancerGeneric.inl
  57. 0 102
      Include/RmlUi/Core/ElementReference.h
  58. 5 7
      Include/RmlUi/Core/ElementScroll.h
  59. 5 5
      Include/RmlUi/Core/ElementText.h
  60. 7 38
      Include/RmlUi/Core/ElementUtilities.h
  61. 36 17
      Include/RmlUi/Core/Event.h
  62. 4 9
      Include/RmlUi/Core/EventInstancer.h
  63. 3 9
      Include/RmlUi/Core/EventListenerInstancer.h
  64. 42 40
      Include/RmlUi/Core/Factory.h
  65. 3 9
      Include/RmlUi/Core/FileInterface.h
  66. 0 130
      Include/RmlUi/Core/FontDatabase.h
  67. 14 46
      Include/RmlUi/Core/FontEffect.h
  68. 8 16
      Include/RmlUi/Core/FontEffectInstancer.h
  69. 137 0
      Include/RmlUi/Core/FontEngineInterface.h
  70. 21 11
      Include/RmlUi/Core/FontGlyph.h
  71. 0 71
      Include/RmlUi/Core/FontProvider.h
  72. 0 102
      Include/RmlUi/Core/FreeType/FontProvider.h
  73. 1 2
      Include/RmlUi/Core/Geometry.h
  74. 8 0
      Include/RmlUi/Core/GeometryUtilities.h
  75. 206 0
      Include/RmlUi/Core/ID.h
  76. 37 43
      Include/RmlUi/Core/Lua/Interpreter.h
  77. 9 11
      Include/RmlUi/Core/Lua/LuaType.h
  78. 23 40
      Include/RmlUi/Core/Lua/LuaType.inl
  79. 2 2
      Include/RmlUi/Core/Lua/Utilities.h
  80. 5 2
      Include/RmlUi/Core/Matrix4.h
  81. 13 0
      Include/RmlUi/Core/Matrix4.inl
  82. 1 1
      Include/RmlUi/Core/Platform.h
  83. 83 0
      Include/RmlUi/Core/Profiling.h
  84. 33 24
      Include/RmlUi/Core/PropertiesIteratorView.h
  85. 20 12
      Include/RmlUi/Core/Property.h
  86. 8 3
      Include/RmlUi/Core/PropertyDefinition.h
  87. 11 15
      Include/RmlUi/Core/PropertyDictionary.h
  88. 235 0
      Include/RmlUi/Core/PropertyIdSet.h
  89. 2 7
      Include/RmlUi/Core/PropertyParser.h
  90. 63 43
      Include/RmlUi/Core/PropertySpecification.h
  91. 0 78
      Include/RmlUi/Core/ReferenceCountable.h
  92. 9 29
      Include/RmlUi/Core/RenderInterface.h
  93. 9 12
      Include/RmlUi/Core/ScriptInterface.h
  94. 101 0
      Include/RmlUi/Core/Spritesheet.h
  95. 84 88
      Include/RmlUi/Core/Stream.h
  96. 13 17
      Include/RmlUi/Core/StreamMemory.h
  97. 0 101
      Include/RmlUi/Core/String.h
  98. 0 220
      Include/RmlUi/Core/StringBase.h
  99. 0 734
      Include/RmlUi/Core/StringBase.inl
  100. 150 37
      Include/RmlUi/Core/StringUtilities.h

+ 25 - 24
.travis.yml

@@ -1,25 +1,17 @@
 sudo: false
 sudo: false
-dist: trusty
+dist: bionic
+language: c++
 
 
 matrix:
 matrix:
   include:
   include:
     - os: osx
     - os: osx
-      osx_image: xcode10.2
-      env: ARCH=OSX
-      language: c++
+      osx_image: xcode10.3
       compiler: clang
       compiler: clang
     - os: linux
     - os: linux
-      language: c++
       compiler: clang
       compiler: clang
-      env: MATRIX_EVAL="CC=clang-6.0 CXX=clang++-6.0"
       addons:
       addons:
         apt:
         apt:
-          sources:
-            - ubuntu-toolchain-r-test
-            - llvm-toolchain-trusty-6.0
           packages:
           packages:
-            - clang-6.0
-            - libstdc++-7-dev
             - cmake
             - cmake
             - build-essential
             - build-essential
             - libsdl2-dev
             - libsdl2-dev
@@ -29,15 +21,27 @@ matrix:
             - liblua5.2-dev
             - liblua5.2-dev
             - libsfml-dev
             - libsfml-dev
     - os: linux
     - os: linux
-      language: c++
+      compiler: clang
+      env: NO_FONT_INTERFACE_DEFAULT="ON"
+      addons:
+        apt:
+          packages:
+            - cmake
+            - build-essential
+            - libsdl2-dev
+            - libsdl2-image-dev
+            - libfreetype6-dev
+            - libglew-dev
+            - liblua5.2-dev
+            - libsfml-dev
+    - os: linux
       compiler: gcc
       compiler: gcc
-      env: MATRIX_EVAL="CC=gcc-7 CXX=g++-7" VALGRIND_SAMPLES="1"
+      env: VALGRIND_SAMPLES="1"
+      services:
+        - xvfb
       addons:
       addons:
         apt:
         apt:
-          sources:
-            - ubuntu-toolchain-r-test
           packages:
           packages:
-            - g++-7
             - cmake
             - cmake
             - build-essential
             - build-essential
             - libsdl2-dev
             - libsdl2-dev
@@ -49,20 +53,17 @@ matrix:
             - mesa-utils
             - mesa-utils
             - valgrind
             - valgrind
 
 
-before_install:
-  - if [[ -n "${MATRIX_EVAL}" ]]; then eval "${MATRIX_EVAL}" ; fi
-
 install:
 install:
   - cd "$TRAVIS_BUILD_DIR"
   - cd "$TRAVIS_BUILD_DIR"
-  - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then cmake -DBUILD_LUA_BINDINGS=ON -DBUILD_SAMPLES=ON .; fi
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cmake -G Xcode .; fi
+  - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then cmake -DBUILD_LUA_BINDINGS=ON -DBUILD_SAMPLES=ON -DNO_FONT_INTERFACE_DEFAULT=${NO_FONT_INTERFACE_DEFAULT:-OFF} .; fi
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cmake -DNO_THIRDPARTY_CONTAINERS=ON -G Xcode .; fi
 
 
 before_script:
 before_script:
-  - if [[ "$VALGRIND_SAMPLES" == "1" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start && sleep 3; fi
+  - if [[ "$VALGRIND_SAMPLES" == "1" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0; fi
 
 
 script:
 script:
   - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then make -j4; fi
   - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then make -j4; fi
-  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then xcodebuild -project rmlui.xcodeproj/ -jobs 4 -configuration Release -target ALL_BUILD; fi
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then xcodebuild -project rmlui.xcodeproj/ -jobs 4 -configuration Release -scheme ALL_BUILD | xcpretty --color; test ${PIPESTATUS[0]} -eq 0; fi
   - if [[ "$VALGRIND_SAMPLES" == "1" ]]; then mkdir build && make DESTDIR=build install; fi
   - if [[ "$VALGRIND_SAMPLES" == "1" ]]; then mkdir build && make DESTDIR=build install; fi
   - |
   - |
     if [[ "$VALGRIND_SAMPLES" == "1" ]]; then
     if [[ "$VALGRIND_SAMPLES" == "1" ]]; then
@@ -72,7 +73,7 @@ script:
             cd `dirname $f`
             cd `dirname $f`
             sample=$(basename $f)
             sample=$(basename $f)
             printf "\033[0;36m$sample\033[0m\n"
             printf "\033[0;36m$sample\033[0m\n"
-            LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/build/usr/local/lib timeout --preserve-status --signal=TERM --kill-after=15 10 valgrind --leak-check=full --suppressions=$TRAVIS_BUILD_DIR/.travis.valgrind.supp --error-exitcode=1 --log-fd=1 ./$sample >>$vout
+            LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/build/usr/local/lib timeout --preserve-status --signal=TERM --kill-after=15 10 valgrind --leak-check=full --track-origins=yes --suppressions=$TRAVIS_BUILD_DIR/.travis.valgrind.supp --error-exitcode=1 --log-fd=1 ./$sample >>$vout
             cnt=$((cnt+1))
             cnt=$((cnt+1))
         done;
         done;
 
 

+ 57 - 73
Cmake/FileList.cmake → CMake/FileList.cmake

@@ -2,10 +2,11 @@
 
 
 set(Core_HDR_FILES
 set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Clock.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Clock.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/ComputeProperty.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DebugFont.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DebugFont.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNone.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNoneInstancer.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorGradient.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNinePatch.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiled.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiled.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBoxInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBoxInstancer.h
@@ -25,32 +26,17 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementHandle.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementHandle.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementImage.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementImage.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyle.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyle.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyleCache.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementTextDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementTextDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventDispatcher.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventDispatcher.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventIterators.h
     ${PROJECT_SOURCE_DIR}/Source/Core/EventIterators.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/EventSpecification.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNone.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNoneInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFaceHandle.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFaceLayer.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFace.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFaceHandle.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFamily.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/precompiled.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/BitmapFontDefinitions.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontParser.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFace.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFaceHandle.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFaceLayer.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFamily.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/precompiled.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/IdNameMap.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutEngine.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutEngine.h
@@ -60,6 +46,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PluginRegistry.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PluginRegistry.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Pool.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Pool.h
     ${PROJECT_SOURCE_DIR}/Source/Core/precompiled.h
     ${PROJECT_SOURCE_DIR}/Source/Core/precompiled.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/PropertiesIterator.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserAnimation.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserAnimation.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserColour.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserColour.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserKeyword.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserKeyword.h
@@ -92,7 +79,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/UnicodeRange.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/Utilities.h
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSlider.h
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSlider.h
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSliderScroll.h
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSliderScroll.h
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerBody.h
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerBody.h
@@ -112,6 +99,10 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Box.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Box.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.inl
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ComputedValues.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/chobo/flat_map.hpp
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/chobo/flat_set.hpp
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/robin_hood.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Context.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Context.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ContextInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ContextInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ConvolutionFilter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ConvolutionFilter.h
@@ -120,14 +111,10 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Decorator.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Decorator.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DecoratorInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/DecoratorInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Dictionary.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Dictionary.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Dictionary.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Element.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Element.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Element.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Element.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementDocument.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementDocument.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementInstancer.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementInstancerGeneric.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementInstancerGeneric.inl
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementReference.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementScroll.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementScroll.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementText.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementText.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ElementUtilities.h
@@ -137,53 +124,48 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/EventListenerInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/EventListenerInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Factory.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Factory.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FileInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FileInterface.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Font.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontDatabase.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontEffect.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontEffect.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontEffectInstancer.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontEffectInstancer.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontFace.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontFamily.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontEngineInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontGlyph.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontGlyph.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontProvider.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FreeType/FontProvider.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/BitmapFont/FontProvider.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Geometry.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Geometry.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/GeometryUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/GeometryUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Header.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Header.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ID.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Input.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Input.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Log.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Log.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Math.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Math.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/MathTypes.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.inl
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/MathTypes.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Platform.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Platform.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Plugin.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Plugin.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Profiling.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertiesIteratorView.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Property.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Property.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDefinition.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDefinition.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDictionary.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyDictionary.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyIdSet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ReferenceCountable.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Spritesheet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Stream.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Stream.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StreamMemory.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StreamMemory.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/String.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StringBase.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StringBase.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StringUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StringUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleSheet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleSheet.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleSheetKeywords.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleSheetSpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StyleSheetSpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/SystemInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/SystemInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Texture.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Texture.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Traits.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Transform.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Transform.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TransformState.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TransformPrimitive.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TransformPrimitive.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TransformState.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Tween.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Types.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Types.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Tween.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/URL.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/URL.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Variant.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Variant.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Variant.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Variant.inl
@@ -194,8 +176,6 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vector4.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vector4.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vector4.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vector4.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vertex.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Vertex.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ViewState.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/WString.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/XMLNodeHandler.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/XMLNodeHandler.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/XMLParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/XMLParser.h
 )
 )
@@ -204,15 +184,16 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/BaseXMLParser.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/BaseXMLParser.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Box.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Box.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Clock.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Clock.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/ComputeProperty.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Context.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Context.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ContextInstancerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ConvolutionFilter.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ConvolutionFilter.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Core.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Core.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Decorator.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Decorator.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorGradient.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNone.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNoneInstancer.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorNinePatch.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiled.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiled.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBoxInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledBoxInstancer.cpp
@@ -223,7 +204,6 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVertical.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVertical.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVerticalInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DecoratorTiledVerticalInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/Dictionary.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DocumentHeader.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/DocumentHeader.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Element.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Element.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementAnimation.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementAnimation.cpp
@@ -235,10 +215,8 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementHandle.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementHandle.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementImage.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementImage.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/ElementReference.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementScroll.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementScroll.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyle.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyle.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/ElementStyleCache.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementText.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementText.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementTextDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementTextDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ElementUtilities.cpp
@@ -247,35 +225,18 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventInstancerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventListenerInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/EventListenerInstancer.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/EventSpecification.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Factory.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Factory.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FileInterfaceDefault.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontDatabase.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffect.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffect.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNone.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectNoneInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutlineInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadowInstancer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFace.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFaceHandle.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFaceLayer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontFamily.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FontProvider.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFace.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFaceHandle.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontFamily.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/FreeType/FontProvider.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontParser.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFace.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFaceHandle.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFaceLayer.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontFamily.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/BitmapFont/FontProvider.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Geometry.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Geometry.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.cpp
@@ -288,6 +249,8 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Plugin.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Plugin.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PluginRegistry.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PluginRegistry.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/precompiled.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/precompiled.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/Profiling.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/PropertiesIteratorView.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Property.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Property.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyDefinition.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyDefinition.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyDictionary.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyDictionary.cpp
@@ -298,12 +261,11 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserString.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserString.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransform.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransform.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertySpecification.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertySpecification.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/ReferenceCountable.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/RenderInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/RenderInterface.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/Spritesheet.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Stream.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Stream.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamFile.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamFile.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamMemory.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamMemory.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/String.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StringCache.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StringCache.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StringUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StringUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StyleSheet.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/StyleSheet.cpp
@@ -334,17 +296,15 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Transform.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Transform.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/TransformState.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformPrimitive.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformPrimitive.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/UnicodeRange.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/TransformState.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/TypeConverter.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/URL.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/URL.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Variant.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Variant.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Vector3.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Vector3.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Vector4.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Vector4.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/ViewState.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSlider.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSlider.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSliderScroll.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetSliderScroll.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/WString.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandler.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandler.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerBody.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerBody.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerDefault.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/XMLNodeHandlerDefault.cpp
@@ -380,7 +340,6 @@ set(MASTER_Controls_PUB_HDR_FILES
 )
 )
 
 
 set(Controls_PUB_HDR_FILES
 set(Controls_PUB_HDR_FILES
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/Clipboard.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/Controls.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/Controls.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/DataFormatter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/DataFormatter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/DataQuery.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Controls/DataQuery.h
@@ -402,7 +361,6 @@ set(Controls_PUB_HDR_FILES
 )
 )
 
 
 set(Controls_SRC_FILES
 set(Controls_SRC_FILES
-    ${PROJECT_SOURCE_DIR}/Source/Controls/Clipboard.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/Controls.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/Controls.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/DataFormatter.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/DataFormatter.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/DataQuery.cpp
     ${PROJECT_SOURCE_DIR}/Source/Controls/DataQuery.cpp
@@ -474,6 +432,32 @@ set(Debugger_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Debugger/SystemInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Debugger/SystemInterface.cpp
 )
 )
 
 
+if(NOT NO_FONT_INTERFACE_DEFAULT)
+    set(Core_HDR_FILES
+        ${Core_HDR_FILES}
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFace.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceHandleDefault.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceLayer.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFamily.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontProvider.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontTypes.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FreeTypeInterface.h
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/precompiled.h
+    )
+
+    set(Core_SRC_FILES
+        ${Core_SRC_FILES}
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFace.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFaceLayer.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontFamily.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FontProvider.cpp
+        ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineDefault/FreeTypeInterface.cpp
+    )
+endif()
+
 set(LuaCore_HDR_FILES
 set(LuaCore_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Lua/Colourb.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Lua/Colourb.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Lua/Colourf.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Lua/Colourf.h

+ 0 - 0
Cmake/Modules/FindCarbon.cmake → CMake/Modules/FindCarbon.cmake


+ 0 - 0
Cmake/Modules/FindDirectX.cmake → CMake/Modules/FindDirectX.cmake


+ 0 - 0
Cmake/Modules/FindPkgMacros.cmake → CMake/Modules/FindPkgMacros.cmake


+ 0 - 0
Cmake/Modules/FindSDL2.cmake → CMake/Modules/FindSDL2.cmake


+ 0 - 0
Cmake/Modules/FindSDL2_image.cmake → CMake/Modules/FindSDL2_image.cmake


+ 0 - 0
Cmake/Modules/FindSFML.cmake → CMake/Modules/FindSFML.cmake


+ 0 - 0
Cmake/Platform/iOS.cmake → CMake/Platform/iOS.cmake


+ 0 - 0
Cmake/RmlUiConfig.cmake.build.in → CMake/RmlUiConfig.cmake.build.in


+ 0 - 0
Cmake/RmlUiConfig.cmake.install.in → CMake/RmlUiConfig.cmake.install.in


+ 32 - 14
Cmake/SampleFileList.cmake → CMake/SampleFileList.cmake

@@ -18,6 +18,31 @@ set(shell_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Samples/shell/src/ShellSystemInterface.cpp
     ${PROJECT_SOURCE_DIR}/Samples/shell/src/ShellSystemInterface.cpp
 )
 )
 
 
+set(animation_HDR_FILES
+)
+
+set(animation_SRC_FILES
+    ${PROJECT_SOURCE_DIR}/Samples/basic/animation/src/main.cpp
+)
+
+set(benchmark_HDR_FILES
+)
+
+set(benchmark_SRC_FILES
+    ${PROJECT_SOURCE_DIR}/Samples/basic/benchmark/src/main.cpp
+)
+
+set(bitmapfont_HDR_FILES
+    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/FontEngineBitmap.h
+    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.h
+)
+
+set(bitmapfont_SRC_FILES
+    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/FontEngineBitmap.cpp
+    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.cpp
+    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/main.cpp
+)
+
 set(customlog_HDR_FILES
 set(customlog_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/customlog/src/SystemInterface.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/customlog/src/SystemInterface.h
 )
 )
@@ -27,6 +52,13 @@ set(customlog_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/customlog/src/SystemInterface.cpp
     ${PROJECT_SOURCE_DIR}/Samples/basic/customlog/src/SystemInterface.cpp
 )
 )
 
 
+set(demo_HDR_FILES
+)
+
+set(demo_SRC_FILES
+    ${PROJECT_SOURCE_DIR}/Samples/basic/demo/src/main.cpp
+)
+
 set(drag_HDR_FILES
 set(drag_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/drag/src/DragListener.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/drag/src/DragListener.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/drag/src/Inventory.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/drag/src/Inventory.h
@@ -45,13 +77,6 @@ set(loaddocument_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/loaddocument/src/main.cpp
     ${PROJECT_SOURCE_DIR}/Samples/basic/loaddocument/src/main.cpp
 )
 )
 
 
-set(bitmapfont_HDR_FILES
-)
-
-set(bitmapfont_SRC_FILES
-    ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/src/main.cpp
-)
-
 set(treeview_HDR_FILES
 set(treeview_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/treeview/src/FileFormatter.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/treeview/src/FileFormatter.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/treeview/src/FileSystem.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/treeview/src/FileSystem.h
@@ -70,13 +95,6 @@ set(transform_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/transform/src/main.cpp
     ${PROJECT_SOURCE_DIR}/Samples/basic/transform/src/main.cpp
 )
 )
 
 
-set(animation_HDR_FILES
-)
-
-set(animation_SRC_FILES
-    ${PROJECT_SOURCE_DIR}/Samples/basic/animation/src/main.cpp
-)
-
 set(sdl2_HDR_FILES
 set(sdl2_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Samples/basic/sdl2/src/RenderInterfaceSDL2.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/sdl2/src/RenderInterfaceSDL2.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/sdl2/src/SystemInterfaceSDL2.h
     ${PROJECT_SOURCE_DIR}/Samples/basic/sdl2/src/SystemInterfaceSDL2.h

+ 0 - 0
Cmake/builddist.py → CMake/builddist.py


+ 25 - 5
Cmake/gen_filelists.sh → CMake/gen_filelists.sh

@@ -1,20 +1,23 @@
 #!/usr/bin/env bash
 #!/usr/bin/env bash
 
 
-basedir="../.."
-file=Build/cmake/FileList.cmake
+basedir=".."
+file=CMake/FileList.cmake
 src='set(lib_SRC_FILES'
 src='set(lib_SRC_FILES'
 hdr='set(lib_HDR_FILES'
 hdr='set(lib_HDR_FILES'
 masterpubhdr='set(MASTER_lib_PUB_HDR_FILES'
 masterpubhdr='set(MASTER_lib_PUB_HDR_FILES'
 pubhdr='set(lib_PUB_HDR_FILES'
 pubhdr='set(lib_PUB_HDR_FILES'
 srcdir='${PROJECT_SOURCE_DIR}'
 srcdir='${PROJECT_SOURCE_DIR}'
+fontdefaultbegin='if(NOT NO_FONT_INTERFACE_DEFAULT)'
+fontdefaultend='endif()'
 srcpath=Source
 srcpath=Source
 hdrpath=Include/RmlUi
 hdrpath=Include/RmlUi
 luapath=Lua
 luapath=Lua
+fontdefaultpath=FontEngineDefault
 
 
 printfiles() {
 printfiles() {
     # Print headers
     # Print headers
     echo ${hdr/lib/$1} >>$file
     echo ${hdr/lib/$1} >>$file
-    find  $srcpath/$1 -maxdepth 1 -iname "*.h" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
+    find  $srcpath/$1 -maxdepth 3 -path */$luapath -prune -o -path */$fontdefaultpath -prune -o \( -iname "*.h" -o -iname "*.hpp" \) -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
     echo -e ')\n' >>$file
     echo -e ')\n' >>$file
     # Print master header for library
     # Print master header for library
     echo ${masterpubhdr/lib/$1} >>$file
     echo ${masterpubhdr/lib/$1} >>$file
@@ -22,14 +25,29 @@ printfiles() {
     echo -e ')\n' >>$file
     echo -e ')\n' >>$file
     # Print public headers sub directory
     # Print public headers sub directory
     echo ${pubhdr/lib/$1} >>$file
     echo ${pubhdr/lib/$1} >>$file
-    find  $hdrpath/$1 -maxdepth 1 \( -iname "*.h" -o -iname "*.inl" \) -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
+    find  $hdrpath/$1 -maxdepth 3 -path */$luapath -prune -o -path */$fontdefaultpath -prune -o \( -iname "*.h" -o -iname "*.inl" -o -iname "*.hpp" \) -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
     echo -e ')\n' >>$file
     echo -e ')\n' >>$file
     # Print source files
     # Print source files
     echo ${src/lib/$1} >>$file
     echo ${src/lib/$1} >>$file
-    find  $srcpath/$1 -maxdepth 1 -iname "*.cpp" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
+    find  $srcpath/$1 -maxdepth 3 -path */$luapath -prune -o -path */$fontdefaultpath -prune -o -iname "*.cpp" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
     echo -e ')\n' >>$file
     echo -e ')\n' >>$file
 }
 }
 
 
+printfontdefaultfiles() {
+    # Print headers
+	echo $fontdefaultbegin >>$file
+    echo '    '${hdr/lib/$1} >>$file
+    echo '        ${'$1'_HDR_FILES}' >>$file
+    find  $srcpath/$1/$fontdefaultpath -iname "*.h" -exec echo '        '$srcdir/{} \; 2>/dev/null | sort -f >>$file
+    echo -e '    )\n' >>$file
+    # Print source files
+    echo '    '${src/lib/$1} >>$file
+    echo '        ${'$1'_SRC_FILES}' >>$file
+    find  $srcpath/$1/$fontdefaultpath -iname "*.cpp" -exec echo '        '$srcdir/{} \; 2>/dev/null | sort -f >>$file
+    echo -e '    )' >>$file
+	echo -e $fontdefaultend'\n' >>$file
+}
+
 printluafiles() {
 printluafiles() {
     # Print headers
     # Print headers
     echo ${hdr/lib/Lua${1}} >>$file
     echo ${hdr/lib/Lua${1}} >>$file
@@ -51,6 +69,8 @@ for lib in "Core" "Controls" "Debugger"; do
     printfiles $lib
     printfiles $lib
 done
 done
 
 
+printfontdefaultfiles "Core"
+
 for lib in "Core" "Controls"; do
 for lib in "Core" "Controls"; do
     printluafiles $lib
     printluafiles $lib
 done
 done

+ 5 - 5
Cmake/gen_samplelists.sh → CMake/gen_samplelists.sh

@@ -1,23 +1,23 @@
 #!/usr/bin/env bash
 #!/usr/bin/env bash
 
 
-basedir="../.."
-file=Build/cmake/SampleFileList.cmake
+basedir=".."
+file=CMake/SampleFileList.cmake
 src='set(sample_SRC_FILES'
 src='set(sample_SRC_FILES'
 hdr='set(sample_HDR_FILES'
 hdr='set(sample_HDR_FILES'
 srcdir='${PROJECT_SOURCE_DIR}'
 srcdir='${PROJECT_SOURCE_DIR}'
 srcpath=Samples
 srcpath=Samples
 samples=( 'shell'
 samples=( 'shell'
-	'basic/customlog' 'basic/drag' 'basic/loaddocument' 'basic/treeview' 'basic/transform'
+	'basic/animation' 'basic/benchmark' 'basic/bitmapfont' 'basic/customlog' 'basic/demo' 'basic/drag' 'basic/loaddocument' 'basic/treeview' 'basic/transform'
 	'basic/sdl2' 'basic/sfml2'
 	'basic/sdl2' 'basic/sfml2'
 	'basic/directx10'
 	'basic/directx10'
-	'tutorial/template' 'tutorial/datagrid' 'tutorial/datagrid_tree' 'tutorial/tutorial_drag'
+	'tutorial/template' 'tutorial/datagrid' 'tutorial/datagrid_tree' 'tutorial/drag'
 	'invaders' 'luainvaders'
 	'invaders' 'luainvaders'
 )
 )
 
 
 printfiles() {
 printfiles() {
     # Print headers
     # Print headers
     name=${1//basic\//} #substitute basic/ for nothing
     name=${1//basic\//} #substitute basic/ for nothing
-    name=${name//tutorial\/} #substitute tutorial/ for nothing
+    name=${name//tutorial\//tutorial_} #substitute 'tutorial/' for 'tutorial_'
     echo ${hdr/sample/$name} >>$file
     echo ${hdr/sample/$name} >>$file
     find  $srcpath/$1/src -maxdepth 1 -iname "*.h" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
     find  $srcpath/$1/src -maxdepth 1 -iname "*.h" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file
     find  $srcpath/$1/include -maxdepth 1 -iname "*.h" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file 2>/dev/null
     find  $srcpath/$1/include -maxdepth 1 -iname "*.h" -exec echo '    '$srcdir/{} \; 2>/dev/null | sort -f >>$file 2>/dev/null

+ 0 - 0
Cmake/plist/RocketControlsOSX-Info.plist → CMake/plist/RocketControlsOSX-Info.plist


+ 0 - 0
Cmake/plist/RocketCoreOSX-Info.plist → CMake/plist/RocketCoreOSX-Info.plist


+ 0 - 0
Cmake/plist/RocketDebuggerOSX-Info.plist → CMake/plist/RocketDebuggerOSX-Info.plist


+ 47 - 20
CMakeLists.txt

@@ -8,7 +8,7 @@ if(APPLE)
 	# This has to be before most other options so CMake properly handles the 
 	# This has to be before most other options so CMake properly handles the 
 	# compiler variables, it MUST bebefore the project() definition
 	# compiler variables, it MUST bebefore the project() definition
 	if(IOS_PLATFORM)
 	if(IOS_PLATFORM)
-		set(CMAKE_TOOLCHAIN_FILE Cmake/Platform/iOS.cmake)
+		set(CMAKE_TOOLCHAIN_FILE CMake/Platform/iOS.cmake)
 	endif(IOS_PLATFORM)
 	endif(IOS_PLATFORM)
 
 
 	option(BUILD_UNIVERSAL_BINARIES "Build universal binaries for all architectures supported" ON)
 	option(BUILD_UNIVERSAL_BINARIES "Build universal binaries for all architectures supported" ON)
@@ -55,14 +55,14 @@ project(RmlUi C CXX)
 # paths
 # paths
 include(GNUInstallDirs)
 include(GNUInstallDirs)
 
 
-set(RMLUI_VERSION_MAJOR 2)
+set(RMLUI_VERSION_MAJOR 3)
 set(RMLUI_VERSION_MINOR 0)
 set(RMLUI_VERSION_MINOR 0)
 set(RMLUI_VERSION_PATCH 0)
 set(RMLUI_VERSION_PATCH 0)
 set(RMLUI_VERSION_TWEAK 0)
 set(RMLUI_VERSION_TWEAK 0)
 set(PROJECT_VERSION ${RMLUI_VERSION_MAJOR}.${RMLUI_VERSION_MINOR}.${RMLUI_VERSION_PATCH}.${RMLUI_VERSION_TWEAK})
 set(PROJECT_VERSION ${RMLUI_VERSION_MAJOR}.${RMLUI_VERSION_MINOR}.${RMLUI_VERSION_PATCH}.${RMLUI_VERSION_TWEAK})
 
 
 # Search in the 'cmake' directory for additional CMake modules.
 # Search in the 'cmake' directory for additional CMake modules.
-list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/Cmake;${PROJECT_SOURCE_DIR}/Cmake/Modules)
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake;${PROJECT_SOURCE_DIR}/CMake/Modules)
 
 
 #===================================
 #===================================
 # Environment tests ================
 # Environment tests ================
@@ -155,21 +155,39 @@ else(APPLE)
 	endif()
 	endif()
 endif(APPLE)
 endif(APPLE)
 
 
+option(NO_FONT_INTERFACE_DEFAULT "Do not include the default font engine in the build. Allows building without the FreeType dependency, but a custom font engine must be created and set." OFF)
+if(NO_FONT_INTERFACE_DEFAULT)
+	add_definitions(-DRMLUI_NO_FONT_INTERFACE_DEFAULT)
+endif()
+
 if(NOT BUILD_SHARED_LIBS)
 if(NOT BUILD_SHARED_LIBS)
 	add_definitions(-DRMLUI_STATIC_LIB)
 	add_definitions(-DRMLUI_STATIC_LIB)
 endif()
 endif()
 
 
+option(NO_THIRDPARTY_CONTAINERS "Only use standard library containers." OFF)
+if( NO_THIRDPARTY_CONTAINERS )
+	add_definitions(-DRMLUI_NO_THIRDPARTY_CONTAINERS)
+	message("-- No third-party containers will be used: Make sure to #define RMLUI_NO_THIRDPARTY_CONTAINERS before including RmlUi in your project.")
+endif()
+
+option(ENABLE_TRACY_PROFILING "Enable tracy profiling, requires that Tracy is placed in Dependencies/tracy." OFF)
+if( ENABLE_TRACY_PROFILING )
+	add_definitions(-DRMLUI_ENABLE_PROFILING)
+endif()
+
 #===================================
 #===================================
 # Find dependencies ================
 # Find dependencies ================
 #===================================
 #===================================
 
 
 # FreeType
 # FreeType
-find_package(Freetype REQUIRED)	
+if(NOT NO_FONT_INTERFACE_DEFAULT)
+	find_package(Freetype REQUIRED)	
 
 
-if(FREETYPE_FOUND)
-	include_directories(${FREETYPE_INCLUDE_DIRS})
-	link_directories(${FREETYPE_LINK_DIRS})
-	list(APPEND CORE_LINK_LIBS ${FREETYPE_LIBRARY})
+	if(FREETYPE_FOUND)
+		include_directories(${FREETYPE_INCLUDE_DIRS})
+		link_directories(${FREETYPE_LINK_DIRS})
+		list(APPEND CORE_LINK_LIBS ${FREETYPE_LIBRARY})
+	endif()
 endif()
 endif()
 
 
 #Lua
 #Lua
@@ -219,7 +237,7 @@ foreach(library ${LIBRARIES})
 		target_compile_options(${NAME} PUBLIC "/MP")
 		target_compile_options(${NAME} PUBLIC "/MP")
 	endif(MSVC)
 	endif(MSVC)
 	
 	
-	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 11)
+	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 14)
 	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
 	set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
 	
 	
 	install(TARGETS ${NAME}
 	install(TARGETS ${NAME}
@@ -321,7 +339,7 @@ if(BUILD_LUA_BINDINGS)
 		   SOVERSION ${RMLUI_VERSION_MAJOR}
 		   SOVERSION ${RMLUI_VERSION_MAJOR}
 		)
 		)
 
 
-		set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 11)
+		set_property(TARGET ${NAME} PROPERTY CXX_STANDARD 14)
 		set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
 		set_property(TARGET ${NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
 
 
 		install(TARGETS ${NAME}
 		install(TARGETS ${NAME}
@@ -387,7 +405,7 @@ endmacro()
 if(BUILD_SAMPLES)
 if(BUILD_SAMPLES)
 	include(SampleFileList)
 	include(SampleFileList)
 
 
-	set(samples treeview customlog drag loaddocument transform bitmapfont animation)
+	set(samples treeview customlog drag loaddocument transform bitmapfont animation benchmark demo)
 	set(tutorials template datagrid datagrid_tree drag)
 	set(tutorials template datagrid datagrid_tree drag)
 	
 	
 if(NOT BUILD_FRAMEWORK)
 if(NOT BUILD_FRAMEWORK)
@@ -484,14 +502,14 @@ endif(NOT BUILD_FRAMEWORK)
 
 
 	# Build and install sample shell library
 	# Build and install sample shell library
 	add_library(shell STATIC ${shell_SRC_FILES} ${shell_HDR_FILES})
 	add_library(shell STATIC ${shell_SRC_FILES} ${shell_HDR_FILES})
-	set_property(TARGET shell PROPERTY CXX_STANDARD 11)
+	set_property(TARGET shell PROPERTY CXX_STANDARD 14)
 	set_property(TARGET shell PROPERTY CXX_STANDARD_REQUIRED ON)
 	set_property(TARGET shell PROPERTY CXX_STANDARD_REQUIRED ON)
 
 
 	# Build and install the basic samples
 	# Build and install the basic samples
 	foreach(sample ${samples})
 	foreach(sample ${samples})
 		bl_sample(${sample} ${sample_LIBRARIES})
 		bl_sample(${sample} ${sample_LIBRARIES})
 		
 		
-		set_property(TARGET ${sample} PROPERTY CXX_STANDARD 11)
+		set_property(TARGET ${sample} PROPERTY CXX_STANDARD 14)
 		set_property(TARGET ${sample} PROPERTY CXX_STANDARD_REQUIRED ON)
 		set_property(TARGET ${sample} PROPERTY CXX_STANDARD_REQUIRED ON)
 
 
 		# The samples always set this as their current working directory
 		# The samples always set this as their current working directory
@@ -526,7 +544,7 @@ endif(NOT BUILD_FRAMEWORK)
 			include_directories(${SDL2_INCLUDE_DIR} ${GLEW_INCLUDE_DIR})
 			include_directories(${SDL2_INCLUDE_DIR} ${GLEW_INCLUDE_DIR})
 
 
 			bl_sample(sdl2 ${sample_LIBRARIES}  ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARY} ${GLEW_LIBRARY})
 			bl_sample(sdl2 ${sample_LIBRARIES}  ${SDL2_LIBRARY} ${SDL2_IMAGE_LIBRARY} ${GLEW_LIBRARY})
-			set_property(TARGET sdl2 PROPERTY CXX_STANDARD 11)
+			set_property(TARGET sdl2 PROPERTY CXX_STANDARD 14)
 			set_property(TARGET sdl2 PROPERTY CXX_STANDARD_REQUIRED ON)
 			set_property(TARGET sdl2 PROPERTY CXX_STANDARD_REQUIRED ON)
 		
 		
 			# The samples always set this as their current working directory
 			# The samples always set this as their current working directory
@@ -566,7 +584,7 @@ endif(NOT BUILD_FRAMEWORK)
 			bl_sample(sfml2 ${sample_LIBRARIES} ${SFML_LIBRARIES})
 			bl_sample(sfml2 ${sample_LIBRARIES} ${SFML_LIBRARIES})
 		endif()
 		endif()
 		
 		
-			set_property(TARGET sfml2 PROPERTY CXX_STANDARD 11)
+			set_property(TARGET sfml2 PROPERTY CXX_STANDARD 14)
 			set_property(TARGET sfml2 PROPERTY CXX_STANDARD_REQUIRED ON)
 			set_property(TARGET sfml2 PROPERTY CXX_STANDARD_REQUIRED ON)
 			
 			
 			# The samples always set this as their current working directory
 			# The samples always set this as their current working directory
@@ -581,7 +599,7 @@ endif(NOT BUILD_FRAMEWORK)
 		set(tutorial_fullname tutorial_${tutorial})
 		set(tutorial_fullname tutorial_${tutorial})
 		bl_sample(${tutorial_fullname} ${sample_LIBRARIES})
 		bl_sample(${tutorial_fullname} ${sample_LIBRARIES})
 
 
-		set_property(TARGET ${tutorial_fullname} PROPERTY CXX_STANDARD 11)
+		set_property(TARGET ${tutorial_fullname} PROPERTY CXX_STANDARD 14)
 		set_property(TARGET ${tutorial_fullname} PROPERTY CXX_STANDARD_REQUIRED ON)
 		set_property(TARGET ${tutorial_fullname} PROPERTY CXX_STANDARD_REQUIRED ON)
 		
 		
 		# The tutorials always set this as their current working directory
 		# The tutorials always set this as their current working directory
@@ -593,7 +611,7 @@ endif(NOT BUILD_FRAMEWORK)
 
 
 	# Build and install invaders sample
 	# Build and install invaders sample
 	bl_sample(invaders ${sample_LIBRARIES})
 	bl_sample(invaders ${sample_LIBRARIES})
-	set_property(TARGET invaders PROPERTY CXX_STANDARD 11)
+	set_property(TARGET invaders PROPERTY CXX_STANDARD 14)
 	set_property(TARGET invaders PROPERTY CXX_STANDARD_REQUIRED ON)
 	set_property(TARGET invaders PROPERTY CXX_STANDARD_REQUIRED ON)
 	install(DIRECTORY DESTINATION ${SAMPLES_DIR}/invaders)
 	install(DIRECTORY DESTINATION ${SAMPLES_DIR}/invaders)
 	install(TARGETS invaders 
 	install(TARGETS invaders 
@@ -602,7 +620,7 @@ endif(NOT BUILD_FRAMEWORK)
 
 
 	if(BUILD_LUA_BINDINGS)
 	if(BUILD_LUA_BINDINGS)
 		bl_sample(luainvaders RmlCoreLua RmlControlsLua ${sample_LIBRARIES} ${LUA_BINDINGS_LINK_LIBS})
 		bl_sample(luainvaders RmlCoreLua RmlControlsLua ${sample_LIBRARIES} ${LUA_BINDINGS_LINK_LIBS})
-		set_property(TARGET luainvaders PROPERTY CXX_STANDARD 11)
+		set_property(TARGET luainvaders PROPERTY CXX_STANDARD 14)
 		set_property(TARGET luainvaders PROPERTY CXX_STANDARD_REQUIRED ON)
 		set_property(TARGET luainvaders PROPERTY CXX_STANDARD_REQUIRED ON)
 		install(DIRECTORY DESTINATION ${SAMPLES_DIR}/luainvaders)
 		install(DIRECTORY DESTINATION ${SAMPLES_DIR}/luainvaders)
 		install(TARGETS luainvaders 
 		install(TARGETS luainvaders 
@@ -647,6 +665,15 @@ if(BUILD_SAMPLES)
 	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/animation/data
 	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/animation/data
 			DESTINATION ${SAMPLES_DIR}/basic/animation
 			DESTINATION ${SAMPLES_DIR}/basic/animation
 	)
 	)
+	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/benchmark/data
+			DESTINATION ${SAMPLES_DIR}/basic/benchmark
+	)
+	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/bitmapfont/data
+			DESTINATION ${SAMPLES_DIR}/basic/bitmapfont
+	)
+	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/demo/data
+			DESTINATION ${SAMPLES_DIR}/basic/demo
+	)
 	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/transform/data
 	install(DIRECTORY ${PROJECT_SOURCE_DIR}/Samples/basic/transform/data
 			DESTINATION ${SAMPLES_DIR}/basic/transform
 			DESTINATION ${SAMPLES_DIR}/basic/transform
 	)
 	)
@@ -684,7 +711,7 @@ if(PkgHelpers_AVAILABLE)
 	set (INCLUDE_DIR "${PROJECT_SOURCE_DIR}/Include")
 	set (INCLUDE_DIR "${PROJECT_SOURCE_DIR}/Include")
 
 
 	# generate configuration for install tree
 	# generate configuration for install tree
-	configure_package_config_file(${PROJECT_SOURCE_DIR}/Cmake/RmlUiConfig.cmake.install.in
+	configure_package_config_file(${PROJECT_SOURCE_DIR}/CMake/RmlUiConfig.cmake.install.in
 		${CMAKE_CURRENT_BINARY_DIR}/install/RmlUiConfig.cmake
 		${CMAKE_CURRENT_BINARY_DIR}/install/RmlUiConfig.cmake
 		INSTALL_DESTINATION ${LIB_INSTALL_DIR}/RmlUi/cmake
 		INSTALL_DESTINATION ${LIB_INSTALL_DIR}/RmlUi/cmake
 		PATH_VARS INCLUDE_INSTALL_DIR LIB_INSTALL_DIR)
 		PATH_VARS INCLUDE_INSTALL_DIR LIB_INSTALL_DIR)
@@ -699,7 +726,7 @@ if(PkgHelpers_AVAILABLE)
 		DESTINATION ${LIB_INSTALL_DIR}/RmlUi/cmake)
 		DESTINATION ${LIB_INSTALL_DIR}/RmlUi/cmake)
 
 
 	# generate configuration for build tree
 	# generate configuration for build tree
-	configure_package_config_file(${PROJECT_SOURCE_DIR}/Cmake/RmlUiConfig.cmake.build.in
+	configure_package_config_file(${PROJECT_SOURCE_DIR}/CMake/RmlUiConfig.cmake.build.in
 		${CMAKE_CURRENT_BINARY_DIR}/RmlUiConfig.cmake
 		${CMAKE_CURRENT_BINARY_DIR}/RmlUiConfig.cmake
 		INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
 		INSTALL_DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
 		PATH_VARS INCLUDE_DIR CMAKE_CURRENT_BINARY_DIR)
 		PATH_VARS INCLUDE_DIR CMAKE_CURRENT_BINARY_DIR)

+ 1 - 0
Dependencies/.gitignore

@@ -1,2 +1,3 @@
 include/
 include/
 lib/
 lib/
+tracy/

+ 0 - 62
Include/RmlUi/Controls/Clipboard.h

@@ -1,62 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICONTROLSCLIPBOARD_H
-#define RMLUICONTROLSCLIPBOARD_H
-
-#include "../Core/WString.h"
-
-namespace Rml {
-namespace Controls {
-
-/**
-	The clipboard temporarily stores text that is cut or copied from a text widget.
-
-	@author Peter Curry
- */
-
-class Clipboard
-{
-public:
-	/// Get the current contents of the clipboard.
-	static Rml::Core::WString Get();
-
-	/// Set the contents of the clipboard.
-	static void Set(const Rml::Core::WString& content);
-
-	#if defined RMLUI_PLATFORM_WIN32
-	/// Set the window handle of the application. This shouldn't need to be called unless the host
-	/// application opens multiple windows, or opens and closes windows, etc.
-	static void SetHWND(void* hwnd);
-	#endif
-};
-
-}
-}
-
-#endif

+ 2 - 4
Include/RmlUi/Controls/DataFormatter.h

@@ -29,15 +29,13 @@
 #ifndef RMLUICONTROLSDATAFORMATTER_H
 #ifndef RMLUICONTROLSDATAFORMATTER_H
 #define RMLUICONTROLSDATAFORMATTER_H
 #define RMLUICONTROLSDATAFORMATTER_H
 
 
+#include "../Core/Types.h"
 #include "../Core/ScriptInterface.h"
 #include "../Core/ScriptInterface.h"
-#include "../Core/String.h"
 #include "Header.h"
 #include "Header.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Controls {
 namespace Controls {
 
 
-class Element;
-
 /**
 /**
 	Abstract base class for a data formatter. A data formatter takes raw data
 	Abstract base class for a data formatter. A data formatter takes raw data
 	and processes it into a final string. They are usually used in conjunction
 	and processes it into a final string. They are usually used in conjunction
@@ -59,7 +57,7 @@ public:
 	/// @parameter [in] data_formatter_name The name of the data formatter to
 	/// @parameter [in] data_formatter_name The name of the data formatter to
 	/// be returned.
 	/// be returned.
 	/// @return If the data formatter with the specified name has been
 	/// @return If the data formatter with the specified name has been
-	/// constructed, a pointer to it will be returned. Otherwise, NULL.
+	/// constructed, a pointer to it will be returned. Otherwise, nullptr.
 	static DataFormatter* GetDataFormatter(const Rml::Core::String& data_formatter_name);
 	static DataFormatter* GetDataFormatter(const Rml::Core::String& data_formatter_name);
 
 
 	/// Formats the raw results of a data source request into RML.
 	/// Formats the raw results of a data source request into RML.

+ 4 - 4
Include/RmlUi/Controls/DataQuery.h

@@ -64,7 +64,7 @@ public:
 		FieldIndices::const_iterator itr = field_indices.find(field_name);
 		FieldIndices::const_iterator itr = field_indices.find(field_name);
 		if (itr == field_indices.end())
 		if (itr == field_indices.end())
 		{
 		{
-			Rml::Core::Log::Message(Rml::Core::Log::LT_ERROR, "Field %s not found in query", field_name.CString());
+			Rml::Core::Log::Message(Rml::Core::Log::LT_ERROR, "Field %s not found in query", field_name.c_str());
 			return default_value;
 			return default_value;
 		}		
 		}		
 		
 		
@@ -81,7 +81,7 @@ public:
 		FieldIndices::const_iterator itr = field_indices.find(field_name);
 		FieldIndices::const_iterator itr = field_indices.find(field_name);
 		if (itr == field_indices.end())
 		if (itr == field_indices.end())
 		{
 		{
-			Rml::Core::Log::Message(Rml::Core::Log::LT_ERROR, "Field %s not found in query", field_name.CString());
+			Rml::Core::Log::Message(Rml::Core::Log::LT_ERROR, "Field %s not found in query", field_name.c_str());
 			return false;
 			return false;
 		}		
 		}		
 
 
@@ -124,8 +124,8 @@ private:
 	int limit;
 	int limit;
 
 
 	typedef std::vector< Rml::Core::StringList > Rows;
 	typedef std::vector< Rml::Core::StringList > Rows;
-	Rows rows;
-	typedef std::map< Rml::Core::String, size_t > FieldIndices;
+	Rows rows;    
+	typedef Core::UnorderedMap< Rml::Core::String, size_t > FieldIndices;
 	FieldIndices field_indices;
 	FieldIndices field_indices;
 	
 	
 	void LoadRow();
 	void LoadRow();

+ 2 - 3
Include/RmlUi/Controls/DataSource.h

@@ -30,9 +30,8 @@
 #define RMLUICONTROLSDATASOURCE_H
 #define RMLUICONTROLSDATASOURCE_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "../Core/String.h"
+#include "../Core/Types.h"
 #include <list>
 #include <list>
-#include <map>
 
 
 namespace Rml {
 namespace Rml {
 namespace Controls {
 namespace Controls {
@@ -97,7 +96,7 @@ class RMLUICONTROLS_API DataSource
 		void NotifyRowChange(const Rml::Core::String& table);
 		void NotifyRowChange(const Rml::Core::String& table);
 
 
 		/// Helper function for building a result set.
 		/// Helper function for building a result set.
-		typedef std::map< Rml::Core::String, Rml::Core::String > RowMap;
+		typedef Core::UnorderedMap< Rml::Core::String, Rml::Core::String > RowMap;
 		void BuildRowEntries(Rml::Core::StringList& row, const RowMap& row_map, const Rml::Core::StringList& columns);
 		void BuildRowEntries(Rml::Core::StringList& row, const RowMap& row_map, const Rml::Core::StringList& columns);
 
 
 	private:
 	private:

+ 1 - 1
Include/RmlUi/Controls/DataSourceListener.h

@@ -30,7 +30,7 @@
 #define RMLUICONTROLSDATASOURCELISTENER_H
 #define RMLUICONTROLSDATASOURCELISTENER_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "../Core/String.h"
+#include "../Core/Types.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Controls {
 namespace Controls {

+ 4 - 6
Include/RmlUi/Controls/ElementDataGrid.h

@@ -94,7 +94,7 @@ public:
 	/// @param[in] formatter The name of the data formatter to be used to format the raw column data into RML.
 	/// @param[in] formatter The name of the data formatter to be used to format the raw column data into RML.
 	/// @param[in] initial_width The initial width, in pixels, of the column.
 	/// @param[in] initial_width The initial width, in pixels, of the column.
 	/// @param[in] header_element The element hierarchy to use as the column header.
 	/// @param[in] header_element The element hierarchy to use as the column header.
-	void AddColumn(const Rml::Core::String& fields, const Rml::Core::String& formatter, float initial_width, Core::Element* header_element);
+	void AddColumn(const Rml::Core::String& fields, const Rml::Core::String& formatter, float initial_width, Core::ElementPtr header_element);
 	/// Returns the number of columns in this table
 	/// Returns the number of columns in this table
 	int GetNumColumns();
 	int GetNumColumns();
 	/// Returns the column at the specified index.
 	/// Returns the column at the specified index.
@@ -119,13 +119,13 @@ public:
 	ElementDataGridRow* GetRow(int index) const;
 	ElementDataGridRow* GetRow(int index) const;
 
 
 protected:
 protected:
-	virtual void OnUpdate();
+	void OnUpdate() override;
 
 
-	virtual void ProcessEvent(Core::Event& event);
+	void OnResize() override;
 
 
 	/// Gets the markup and content of the element.
 	/// Gets the markup and content of the element.
 	/// @param content[out] The content of the element.
 	/// @param content[out] The content of the element.
-	virtual void GetInnerRML(Rml::Core::String& content) const;
+	void GetInnerRML(Rml::Core::String& content) const override;
 
 
 private:
 private:
 	typedef std::vector< Column > ColumnList;
 	typedef std::vector< Column > ColumnList;
@@ -146,8 +146,6 @@ private:
 
 
 	// The block element that contains all our rows. Only used for applying styles.
 	// The block element that contains all our rows. Only used for applying styles.
 	Core::Element* body;
 	Core::Element* body;
-	// Stores if the body has already been made visible by having enough rows added.
-	bool body_visible;
 };
 };
 
 
 }
 }

+ 2 - 5
Include/RmlUi/Controls/ElementDataGridCell.h

@@ -42,7 +42,7 @@ namespace Controls {
 	@author Robert Curry
 	@author Robert Curry
  */
  */
 
 
-class RMLUICONTROLS_API ElementDataGridCell : public Core::Element, public Core::EventListener
+class RMLUICONTROLS_API ElementDataGridCell : public Core::Element
 {
 {
 public:
 public:
 	ElementDataGridCell(const Rml::Core::String& tag);
 	ElementDataGridCell(const Rml::Core::String& tag);
@@ -50,10 +50,7 @@ public:
 
 
 	void Initialise(int column, Core::Element* header);
 	void Initialise(int column, Core::Element* header);
 	int GetColumn();
 	int GetColumn();
-
-protected:
-	virtual void ProcessEvent(Core::Event& event);
-
+	
 private:
 private:
 	int column;
 	int column;
 	Core::Element* header;
 	Core::Element* header;

+ 1 - 1
Include/RmlUi/Controls/ElementDataGridExpandButton.h

@@ -46,7 +46,7 @@ public:
 	virtual ~ElementDataGridExpandButton();
 	virtual ~ElementDataGridExpandButton();
 
 
 protected:
 protected:
-	void ProcessEvent(Core::Event& event);
+	void ProcessDefaultAction(Core::Event& event) override;
 };
 };
 
 
 }
 }

+ 7 - 12
Include/RmlUi/Controls/ElementDataGridRow.h

@@ -54,7 +54,7 @@ public:
 	ElementDataGridRow(const Rml::Core::String& tag);
 	ElementDataGridRow(const Rml::Core::String& tag);
 	virtual ~ElementDataGridRow();
 	virtual ~ElementDataGridRow();
 
 
-	void Initialise(ElementDataGrid* parent_grid, ElementDataGridRow* parent_row = NULL, int child_index = -1, ElementDataGridRow* header_row = NULL, int depth = -1);
+	void Initialise(ElementDataGrid* parent_grid, ElementDataGridRow* parent_row = nullptr, int child_index = -1, ElementDataGridRow* header_row = nullptr, int depth = -1);
 	void SetChildIndex(int child_index);
 	void SetChildIndex(int child_index);
 	int GetDepth();
 	int GetDepth();
 
 
@@ -67,8 +67,7 @@ public:
 	/// Returns the number of children that aren't dirty (have been loaded)
 	/// Returns the number of children that aren't dirty (have been loaded)
 	int GetNumLoadedChildren();
 	int GetNumLoadedChildren();
 
 
-	// Removes all the child cells and fetches them again from the data
-	// source.
+	/// Removes all the child cells and fetches them again from the data source.
 	void RefreshRows();
 	void RefreshRows();
 
 
 	/// Returns whether this row is expanded or not.
 	/// Returns whether this row is expanded or not.
@@ -90,11 +89,11 @@ public:
 	ElementDataGrid* GetParentGrid();
 	ElementDataGrid* GetParentGrid();
 
 
 protected:
 protected:
-	virtual void OnDataSourceDestroy(DataSource* data_source);
-	virtual void OnRowAdd(DataSource* data_source, const Rml::Core::String& table, int first_row_added, int num_rows_added);
-	virtual void OnRowRemove(DataSource* data_source, const Rml::Core::String& table, int first_row_removed, int num_rows_removed);
-	virtual void OnRowChange(DataSource* data_source, const Rml::Core::String& table, int first_row_changed, int num_rows_changed);
-	virtual void OnRowChange(DataSource* data_source, const Rml::Core::String& table);
+	void OnDataSourceDestroy(DataSource* data_source) override;
+	void OnRowAdd(DataSource* data_source, const Rml::Core::String& table, int first_row_added, int num_rows_added) override;
+	void OnRowRemove(DataSource* data_source, const Rml::Core::String& table, int first_row_removed, int num_rows_removed) override;
+	void OnRowChange(DataSource* data_source, const Rml::Core::String& table, int first_row_changed, int num_rows_changed) override;
+	void OnRowChange(DataSource* data_source, const Rml::Core::String& table) override;
 
 
 private:
 private:
 	typedef std::queue< ElementDataGridRow* > RowQueue;
 	typedef std::queue< ElementDataGridRow* > RowQueue;
@@ -135,10 +134,6 @@ private:
 	// Loads a specific set of children. Called by the above function.
 	// Loads a specific set of children. Called by the above function.
 	void LoadChildren(int first_row_to_load, int num_rows_to_load, Rml::Core::Time time_slice);
 	void LoadChildren(int first_row_to_load, int num_rows_to_load, Rml::Core::Time time_slice);
 
 
-	// If the cells need reloading, this takes care of it. If any children
-	// need updating, they are added to the queue.
-	void UpdateCellsAndChildren(RowQueue& dirty_rows);
-
 	// Sets the dirty_cells flag on this row, and lets our ancestors know.
 	// Sets the dirty_cells flag on this row, and lets our ancestors know.
 	void DirtyCells();
 	void DirtyCells();
 	// Sets the dirty children flag on this row and the row's ancestors.
 	// Sets the dirty children flag on this row and the row's ancestors.

+ 1 - 1
Include/RmlUi/Controls/ElementFormControl.h

@@ -78,7 +78,7 @@ public:
 protected:
 protected:
 	/// Checks for changes to the 'disabled' attribute.
 	/// Checks for changes to the 'disabled' attribute.
 	/// @param[in] changed_attributes List of changed attributes on the element.
 	/// @param[in] changed_attributes List of changed attributes on the element.
-	virtual void OnAttributeChange(const Core::AttributeNameList& changed_attributes);
+	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 };
 };
 
 
 }
 }

+ 7 - 7
Include/RmlUi/Controls/ElementFormControlDataSelect.h

@@ -60,22 +60,22 @@ public:
 protected:
 protected:
 	/// If a new data source has been set on the control, this will attach to it and build the
 	/// If a new data source has been set on the control, this will attach to it and build the
 	/// initial options.
 	/// initial options.
-	virtual void OnUpdate();
+	void OnUpdate() override;
 
 
 	/// Checks for changes to the data source or formatting attributes.
 	/// Checks for changes to the data source or formatting attributes.
 	/// @param[in] changed_attributes List of changed attributes on the element.
 	/// @param[in] changed_attributes List of changed attributes on the element.
-	virtual void OnAttributeChange(const Core::AttributeNameList& changed_attributes);
+	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 
 
 	/// Detaches from the data source and rebuilds the options.
 	/// Detaches from the data source and rebuilds the options.
-	virtual void OnDataSourceDestroy(DataSource* data_source);
+	void OnDataSourceDestroy(DataSource* data_source) override;
 	/// Rebuilds the available options from the data source.
 	/// Rebuilds the available options from the data source.
-	virtual void OnRowAdd(DataSource* data_source, const Rml::Core::String& table, int first_row_added, int num_rows_added);
+	void OnRowAdd(DataSource* data_source, const Rml::Core::String& table, int first_row_added, int num_rows_added) override;
 	/// Rebuilds the available options from the data source.
 	/// Rebuilds the available options from the data source.
-	virtual void OnRowRemove(DataSource* data_source, const Rml::Core::String& table, int first_row_removed, int num_rows_removed);
+	void OnRowRemove(DataSource* data_source, const Rml::Core::String& table, int first_row_removed, int num_rows_removed) override;
 	/// Rebuilds the available options from the data source.
 	/// Rebuilds the available options from the data source.
-	virtual void OnRowChange(DataSource* data_source, const Rml::Core::String& table, int first_row_changed, int num_rows_changed);
+	void OnRowChange(DataSource* data_source, const Rml::Core::String& table, int first_row_changed, int num_rows_changed) override;
 	/// Rebuilds the available options from the data source.
 	/// Rebuilds the available options from the data source.
-	virtual void OnRowChange(DataSource* data_source, const Rml::Core::String& table);
+	void OnRowChange(DataSource* data_source, const Rml::Core::String& table) override;
 
 
 private:
 private:
 	// Builds the option list from the data source.
 	// Builds the option list from the data source.

+ 13 - 11
Include/RmlUi/Controls/ElementFormControlInput.h

@@ -54,41 +54,43 @@ public:
 
 
 	/// Returns a string representation of the current value of the form control.
 	/// Returns a string representation of the current value of the form control.
 	/// @return The value of the form control.
 	/// @return The value of the form control.
-	virtual Rml::Core::String GetValue() const;
+	Rml::Core::String GetValue() const override;
 	/// Sets the current value of the form control.
 	/// Sets the current value of the form control.
 	/// @param value[in] The new value of the form control.
 	/// @param value[in] The new value of the form control.
-	virtual void SetValue(const Rml::Core::String& value);
+	void SetValue(const Rml::Core::String& value) override;
 	/// Returns if this value's type should be submitted with the form.
 	/// Returns if this value's type should be submitted with the form.
 	/// @return True if the form control is to be submitted, false otherwise.
 	/// @return True if the form control is to be submitted, false otherwise.
-	virtual bool IsSubmitted();
+	bool IsSubmitted() override;
 
 
 protected:
 protected:
 	/// Updates the element's underlying type.
 	/// Updates the element's underlying type.
-	virtual void OnUpdate();
+	void OnUpdate() override;
 	/// Renders the element's underlying type.
 	/// Renders the element's underlying type.
-	virtual void OnRender();
+	void OnRender() override;
+	/// Calls the element's underlying type.
+	void OnResize() override;
 
 
 	/// Checks for necessary functional changes in the control as a result of changed attributes.
 	/// Checks for necessary functional changes in the control as a result of changed attributes.
 	/// @param[in] changed_attributes The list of changed attributes.
 	/// @param[in] changed_attributes The list of changed attributes.
-	virtual void OnAttributeChange(const Core::AttributeNameList& changed_attributes);
+	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 	/// Called when properties on the control are changed.
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
 	/// @param[in] changed_properties The properties changed on the element.
-	virtual void OnPropertyChange(const Core::PropertyNameList& changed_properties);
+	void OnPropertyChange(const Core::PropertyIdSet& changed_properties) override;
 
 
 	/// If we are the added element, this will pass the call onto our type handler.
 	/// If we are the added element, this will pass the call onto our type handler.
 	/// @param[in] child The new member of the hierarchy.
 	/// @param[in] child The new member of the hierarchy.
-	virtual void OnChildAdd(Rml::Core::Element* child);
+	void OnChildAdd(Rml::Core::Element* child) override;
 	/// If we are the removed element, this will pass the call onto our type handler.
 	/// If we are the removed element, this will pass the call onto our type handler.
 	/// @param[in] child The member of the hierarchy that was just removed.
 	/// @param[in] child The member of the hierarchy that was just removed.
-	virtual void OnChildRemove(Rml::Core::Element* child);
+	void OnChildRemove(Rml::Core::Element* child) override;
 
 
 	/// Checks for necessary functional changes in the control as a result of the event.
 	/// Checks for necessary functional changes in the control as a result of the event.
 	/// @param[in] event The event to process.
 	/// @param[in] event The event to process.
-	virtual void ProcessEvent(Core::Event& event);
+	void ProcessDefaultAction(Core::Event& event) override;
 
 
 	/// Sizes the dimensions to the element's inherent size.
 	/// Sizes the dimensions to the element's inherent size.
 	/// @return True.
 	/// @return True.
-	virtual bool GetIntrinsicDimensions(Rml::Core::Vector2f& dimensions);
+	bool GetIntrinsicDimensions(Rml::Core::Vector2f& dimensions) override;
 
 
 private:
 private:
 	InputType* type;
 	InputType* type;

+ 7 - 7
Include/RmlUi/Controls/ElementFormControlSelect.h

@@ -55,10 +55,10 @@ public:
 
 
 	/// Returns a string representation of the current value of the form control.
 	/// Returns a string representation of the current value of the form control.
 	/// @return The value of the form control.
 	/// @return The value of the form control.
-	virtual Rml::Core::String GetValue() const;
+	Rml::Core::String GetValue() const override;
 	/// Sets the current value of the form control.
 	/// Sets the current value of the form control.
 	/// @param[in] value The new value of the form control.
 	/// @param[in] value The new value of the form control.
-	virtual void SetValue(const Rml::Core::String& value);
+	void SetValue(const Rml::Core::String& value) override;
 
 
 	/// Sets the index of the selection. If the new index lies outside of the bounds, it will be clamped.
 	/// Sets the index of the selection. If the new index lies outside of the bounds, it will be clamped.
 	/// @param[in] selection The new selection index.
 	/// @param[in] selection The new selection index.
@@ -69,7 +69,7 @@ public:
 
 
 	/// Returns one of the select control's option elements.
 	/// Returns one of the select control's option elements.
 	/// @param[in] The index of the desired option element.
 	/// @param[in] The index of the desired option element.
-	/// @return The option element at the given index. This will be NULL if the index is out of bounds.
+	/// @return The option element at the given index. This will be nullptr if the index is out of bounds.
 	SelectOption* GetOption(int index);
 	SelectOption* GetOption(int index);
 	/// Returns the number of options in the select control.
 	/// Returns the number of options in the select control.
 	/// @return The number of options.
 	/// @return The number of options.
@@ -91,17 +91,17 @@ public:
 
 
 protected:
 protected:
 	/// Moves all children to be under control of the widget.
 	/// Moves all children to be under control of the widget.
-	virtual void OnUpdate();
+	void OnUpdate() override;
 	/// Updates the layout of the widget's elements.
 	/// Updates the layout of the widget's elements.
-	virtual void OnRender();
+	void OnRender() override;
 
 
 	/// Forces an internal layout.
 	/// Forces an internal layout.
-	virtual void OnLayout();
+	void OnLayout() override;
 
 
 	/// Returns true to mark this element as replaced.
 	/// Returns true to mark this element as replaced.
 	/// @param[out] intrinsic_dimensions Set to the arbitrary dimensions of 128 x 16 just to give this element a size. Resize with the 'width' and 'height' properties.
 	/// @param[out] intrinsic_dimensions Set to the arbitrary dimensions of 128 x 16 just to give this element a size. Resize with the 'width' and 'height' properties.
 	/// @return True.
 	/// @return True.
-	virtual bool GetIntrinsicDimensions(Rml::Core::Vector2f& intrinsic_dimensions);
+	bool GetIntrinsicDimensions(Rml::Core::Vector2f& intrinsic_dimensions) override;
 
 
 	WidgetDropDown* widget;
 	WidgetDropDown* widget;
 };
 };

+ 11 - 9
Include/RmlUi/Controls/ElementFormControlTextArea.h

@@ -55,10 +55,10 @@ public:
 	/// Returns a string representation of the current value of the form control. This is the value of the control
 	/// Returns a string representation of the current value of the form control. This is the value of the control
 	/// regardless of whether it has been selected / checked (as appropriate for the control).
 	/// regardless of whether it has been selected / checked (as appropriate for the control).
 	/// @return The value of the form control.
 	/// @return The value of the form control.
-	virtual Rml::Core::String GetValue() const;
+	Rml::Core::String GetValue() const override;
 	/// Sets the current value of the form control.
 	/// Sets the current value of the form control.
 	/// @param[in] value The new value of the form control.
 	/// @param[in] value The new value of the form control.
-	virtual void SetValue(const Rml::Core::String& value);
+	void SetValue(const Rml::Core::String& value) override;
 
 
 	/// Sets the number of characters visible across the text area. Note that this will only be precise when using
 	/// Sets the number of characters visible across the text area. Note that this will only be precise when using
 	/// a fixed-width font.
 	/// a fixed-width font.
@@ -92,25 +92,27 @@ public:
 
 
 	/// Returns the control's inherent size, based on the length of the input field and the current font size.
 	/// Returns the control's inherent size, based on the length of the input field and the current font size.
 	/// @return True.
 	/// @return True.
-	virtual bool GetIntrinsicDimensions(Rml::Core::Vector2f& dimensions);
+	bool GetIntrinsicDimensions(Rml::Core::Vector2f& dimensions) override;
 
 
 protected:
 protected:
 	/// Updates the control's widget.
 	/// Updates the control's widget.
-	void OnUpdate();
+	void OnUpdate() override;
 	/// Renders the control's widget.
 	/// Renders the control's widget.
-	void OnRender();
+	void OnRender() override;
+	/// Resizes and positions the control's widget.
+	void OnResize() override;
 	/// Formats the element.
 	/// Formats the element.
-	void OnLayout();
+	void OnLayout() override;
 
 
 	/// Called when attributes on the element are changed.
 	/// Called when attributes on the element are changed.
-	virtual void OnAttributeChange(const Core::AttributeNameList& changed_attributes);
+	void OnAttributeChange(const Core::ElementAttributes& changed_attributes) override;
 	/// Called when properties on the control are changed.
 	/// Called when properties on the control are changed.
 	/// @param[in] changed_properties The properties changed on the element.
 	/// @param[in] changed_properties The properties changed on the element.
-	virtual void OnPropertyChange(const Core::PropertyNameList& changed_properties);
+	void OnPropertyChange(const Core::PropertyIdSet& changed_properties) override;
 
 
 	/// Returns the text content of the element.
 	/// Returns the text content of the element.
 	/// @param[out] content The content of the element.
 	/// @param[out] content The content of the element.
-	virtual void GetInnerRML(Rml::Core::String& content) const;
+	void GetInnerRML(Rml::Core::String& content) const override;
 
 
 private:
 private:
 	WidgetTextInput* widget;		
 	WidgetTextInput* widget;		

+ 7 - 14
Include/RmlUi/Controls/ElementTabSet.h

@@ -42,7 +42,7 @@ namespace Controls {
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICONTROLS_API ElementTabSet : public Core::Element, public Core::EventListener
+class RMLUICONTROLS_API ElementTabSet : public Core::Element
 {
 {
 public:
 public:
 	ElementTabSet(const Rml::Core::String& tag);
 	ElementTabSet(const Rml::Core::String& tag);
@@ -60,11 +60,11 @@ public:
 	/// Set the specifed tab index's title element.
 	/// Set the specifed tab index's title element.
 	/// @param[in] tab_index The tab index to set. If it doesn't already exist, it will be created.
 	/// @param[in] tab_index The tab index to set. If it doesn't already exist, it will be created.
 	/// @param[in] element The root of the element tree to set as the tab title.
 	/// @param[in] element The root of the element tree to set as the tab title.
-	void SetTab(int tab_index, Core::Element* element);
+	void SetTab(int tab_index, Core::ElementPtr element);
 	/// Set the specified tab index's body element.
 	/// Set the specified tab index's body element.
 	/// @param[in] tab_index The tab index to set. If it doesn't already exist, it will be created.
 	/// @param[in] tab_index The tab index to set. If it doesn't already exist, it will be created.
 	/// @param[in] element The root of the element tree to set as the window.
 	/// @param[in] element The root of the element tree to set as the window.
-	void SetPanel(int tab_index, Core::Element* element);
+	void SetPanel(int tab_index, Core::ElementPtr element);
 
 
 	/// Remove one of the tab set's panels and its corresponding tab.
 	/// Remove one of the tab set's panels and its corresponding tab.
 	/// @param[in] tab_index The tab index to remove. If no tab matches this index, nothing will be removed.
 	/// @param[in] tab_index The tab index to remove. If no tab matches this index, nothing will be removed.
@@ -82,19 +82,12 @@ public:
 	/// @return The index of the active tab.
 	/// @return The index of the active tab.
 	int GetActiveTab() const;
 	int GetActiveTab() const;
 
 
-	/// Process the incoming event.
-	void ProcessEvent(Core::Event& event);
-
-	/// Called when the listener has been attached to a new Element
-	void OnAttach(Element* element);
-
-	/// Called when the listener has been detached from a Element
-	void OnDetach(Element* element);
+	/// Capture clicks on our tabs.
+	void ProcessDefaultAction(Core::Event& event) override;
 
 
 protected:
 protected:
-	// Catch child add/removes so we can correctly set up their events.
-	virtual void OnChildAdd(Core::Element* child);
-	virtual void OnChildRemove(Core::Element* child);
+	// Catch child add so we can correctly set up its properties.
+	void OnChildAdd(Core::Element* child) override;
 
 
 private:
 private:
 	Core::Element* GetChildByTag(const Rml::Core::String& tag);
 	Core::Element* GetChildByTag(const Rml::Core::String& tag);

+ 1 - 1
Include/RmlUi/Controls/SelectOption.h

@@ -30,7 +30,7 @@
 #define RMLUICONTROLSSELECTOPTION_H
 #define RMLUICONTROLSSELECTOPTION_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "../Core/String.h"
+#include "../Core/Types.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {

+ 3 - 5
Include/RmlUi/Core/Animation.h

@@ -31,7 +31,7 @@
 #define RMLUICOREANIMATION_H
 #define RMLUICOREANIMATION_H
 
 
 
 
-#include "String.h"
+#include "Types.h"
 #include "Tween.h"
 #include "Tween.h"
 
 
 namespace Rml {
 namespace Rml {
@@ -48,11 +48,9 @@ struct Animation {
 	String name;
 	String name;
 };
 };
 
 
-typedef std::vector<Animation> AnimationList;
-
 /* Data parsed from the 'transition' property. */
 /* Data parsed from the 'transition' property. */
 struct Transition {
 struct Transition {
-	String name;
+	PropertyId id;
 	Tween tween;
 	Tween tween;
 	float duration = 0.0f;
 	float duration = 0.0f;
 	float delay = 0.0f;
 	float delay = 0.0f;
@@ -71,7 +69,7 @@ struct TransitionList {
 
 
 inline bool operator==(const Animation& a, const Animation& b) { return a.duration == b.duration && a.tween == b.tween && a.delay == b.delay && a.alternate == b.alternate && a.paused == b.paused && a.num_iterations == b.num_iterations && a.name == b.name; }
 inline bool operator==(const Animation& a, const Animation& b) { return a.duration == b.duration && a.tween == b.tween && a.delay == b.delay && a.alternate == b.alternate && a.paused == b.paused && a.num_iterations == b.num_iterations && a.name == b.name; }
 inline bool operator!=(const Animation& a, const Animation& b) { return !(a == b); }
 inline bool operator!=(const Animation& a, const Animation& b) { return !(a == b); }
-inline bool operator==(const Transition& a, const Transition& b) { return a.name == b.name && a.tween == b.tween && a.duration == b.duration && a.delay == b.delay && a.reverse_adjustment_factor == b.reverse_adjustment_factor; }
+inline bool operator==(const Transition& a, const Transition& b) { return a.id == b.id && a.tween == b.tween && a.duration == b.duration && a.delay == b.delay && a.reverse_adjustment_factor == b.reverse_adjustment_factor; }
 inline bool operator!=(const Transition& a, const Transition& b) { return !(a == b); }
 inline bool operator!=(const Transition& a, const Transition& b) { return !(a == b); }
 inline bool operator==(const TransitionList& a, const TransitionList& b) { return a.none == b.none && a.all == b.all && a.transitions == b.transitions; }
 inline bool operator==(const TransitionList& a, const TransitionList& b) { return a.none == b.none && a.all == b.all && a.transitions == b.transitions; }
 inline bool operator!=(const TransitionList& a, const TransitionList& b) { return !(a == b); }
 inline bool operator!=(const TransitionList& a, const TransitionList& b) { return !(a == b); }

+ 7 - 4
Include/RmlUi/Core/BaseXMLParser.h

@@ -62,7 +62,9 @@ class RMLUICORE_API BaseXMLParser
 
 
 		/// Get the line number in the stream.
 		/// Get the line number in the stream.
 		/// @return The line currently being processed in the XML stream.
 		/// @return The line currently being processed in the XML stream.
-		int GetLineNumber();
+		int GetLineNumber() const;
+		/// Get the line number of the last open tag in the stream.
+		int GetLineNumberOpenTag() const;
 
 
 		/// Called when the parser finds the beginning of an element tag.
 		/// Called when the parser finds the beginning of an element tag.
 		virtual void HandleElementStart(const String& name, const XMLAttributes& attributes);
 		virtual void HandleElementStart(const String& name, const XMLAttributes& attributes);
@@ -82,12 +84,12 @@ class RMLUICORE_API BaseXMLParser
 		bool ReadOpenTag();
 		bool ReadOpenTag();
 		bool ReadCloseTag();
 		bool ReadCloseTag();
 		bool ReadAttributes(XMLAttributes& attributes);
 		bool ReadAttributes(XMLAttributes& attributes);
-		bool ReadCDATA(const char* terminator = NULL);
+		bool ReadCDATA(const char* terminator = nullptr);
 
 
 		// Reads from the stream until a complete word is found.
 		// Reads from the stream until a complete word is found.
 		// @param[out] word Word thats been found
 		// @param[out] word Word thats been found
 		// @param[in] terminators List of characters that terminate the search
 		// @param[in] terminators List of characters that terminate the search
-		bool FindWord(String& word, const char* terminators = NULL);
+		bool FindWord(String& word, const char* terminators = nullptr);
 		// Reads from the stream until the given character set is found. All
 		// Reads from the stream until the given character set is found. All
 		// intervening characters will be returned in data.
 		// intervening characters will be returned in data.
 		bool FindString(const unsigned char* string, String& data);
 		bool FindString(const unsigned char* string, String& data);
@@ -104,6 +106,7 @@ class RMLUICORE_API BaseXMLParser
 		int buffer_size;
 		int buffer_size;
 		int buffer_used;
 		int buffer_used;
 		int line_number;
 		int line_number;
+		int line_number_open_tag;
 		int open_tag_depth;
 		int open_tag_depth;
 
 
 		// The element attributes being read.
 		// The element attributes being read.
@@ -111,7 +114,7 @@ class RMLUICORE_API BaseXMLParser
 		// The loose data being read.
 		// The loose data being read.
 		String data;
 		String data;
 
 
-		std::unordered_set< String > cdata_tags;
+		SmallUnorderedSet< String > cdata_tags;
 };
 };
 
 
 }
 }

+ 0 - 102
Include/RmlUi/Core/BitmapFont/FontProvider.h

@@ -1,102 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREBITMAPFONTFONTPROVIDER_H
-#define RMLUICOREBITMAPFONTFONTPROVIDER_H
-
-#include "../StringUtilities.h"
-#include "../Font.h"
-#include "../FontProvider.h"
-
-namespace Rml {
-namespace Core {
-
-
-class FontEffect;
-class FontFaceHandle;
-class PropertyDictionary;
-
-namespace BitmapFont {
-
-class FontFamily;
-
-/**
-    The font database contains all font families currently in use by RmlUi.
-    @author Peter Curry
- */
-
-class RMLUICORE_API FontProvider : public Rml::Core::FontProvider
-{
-public:
-    static bool Initialise();
-    static void Shutdown();
-
-    /// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
-    /// @param[in] file_name The file to load the face from.
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const String& file_name);
-    /// Adds a new font face to the database, ignoring any family, style and weight information stored in the face itself.
-    /// @param[in] file_name The file to load the face from.
-    /// @param[in] family The family to add the face to.
-    /// @param[in] style The style of the face (normal or italic).
-    /// @param[in] weight The weight of the face (normal or bold).
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const String& file_name, const String& family, Font::Style style, Font::Weight weight);
-    /// Adds a new font face to the database, loading from memory. The face's family, style and weight will be determined from the face itself.
-    /// @param[in] data The font data.
-    /// @param[in] data_length Length of the data.
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const byte* data, int data_length);
-    /// Adds a new font face to the database, loading from memory.
-    /// @param[in] data The font data.
-    /// @param[in] data_length Length of the data.
-    /// @param[in] family The family to add the face to.
-    /// @param[in] style The style of the face (normal or italic).
-    /// @param[in] weight The weight of the face (normal or bold).
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const byte* data, int data_length, const String& family, Font::Style style, Font::Weight weight);
-
-private:
-    FontProvider(void);
-    ~FontProvider(void);
-
-    // Adds a loaded face to the appropriate font family.
-    bool AddFace(void* face, const String& family, Font::Style style, Font::Weight weight, bool release_stream);
-    // Loads a FreeType face.
-    void* LoadFace(const String& file_name);
-    // Loads a FreeType face from memory.
-    void* LoadFace(const byte* data, int data_length, const String& source, bool local_data);
-
-    static FontProvider* instance;
-};
-
-}
-}
-}
-
-#endif

+ 227 - 0
Include/RmlUi/Core/ComputedValues.h

@@ -0,0 +1,227 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICORECOMPUTEDVALUES_H
+#define RMLUICORECOMPUTEDVALUES_H
+
+#include "Types.h"
+#include "Animation.h"
+
+namespace Rml {
+namespace Core {
+
+namespace Style
+{
+
+struct LengthPercentageAuto {
+	enum Type { Auto, Length, Percentage } type = Length;
+	float value = 0;
+	LengthPercentageAuto() {}
+	LengthPercentageAuto(Type type, float value = 0) : type(type), value(value) {}
+};
+struct LengthPercentage {
+	enum Type { Length, Percentage } type = Length;
+	float value = 0;
+	LengthPercentage() {}
+	LengthPercentage(Type type, float value = 0) : type(type), value(value) {}
+};
+
+struct NumberAuto {
+	enum Type { Auto, Number } type = Number;
+	float value = 0;
+	NumberAuto() {}
+	NumberAuto(Type type, float value = 0) : type(type), value(value) {}
+};
+
+
+using Margin = LengthPercentageAuto;
+using Padding = LengthPercentage;
+
+enum class Display : uint8_t { None, Block, Inline, InlineBlock };
+enum class Position : uint8_t { Static, Relative, Absolute, Fixed };
+
+using Top = LengthPercentageAuto;
+using Right = LengthPercentageAuto;
+using Bottom = LengthPercentageAuto;
+using Left = LengthPercentageAuto;
+
+enum class Float : uint8_t { None, Left, Right };
+enum class Clear : uint8_t { None, Left, Right, Both };
+
+using ZIndex = NumberAuto;
+
+using Width = LengthPercentageAuto;
+using MinWidth = LengthPercentage;
+using MaxWidth = LengthPercentage;
+
+using Height = LengthPercentageAuto;
+using MinHeight = LengthPercentage;
+using MaxHeight = LengthPercentage;
+
+struct LineHeight {
+	float value = 12.f * 1.2f; // The computed value (length)
+	enum InheritType { Number, Length } inherit_type = Number;
+	float inherit_value = 1.2f;
+	LineHeight() {}
+	LineHeight(float value, InheritType inherit_type, float inherit_value) : value(value), inherit_type(inherit_type), inherit_value(inherit_value) {}
+};
+struct VerticalAlign {
+	enum Type { Baseline, Middle, Sub, Super, TextTop, TextBottom, Top, Bottom, Length } type;
+	float value; // For length type
+	VerticalAlign(Type type = Baseline) : type(type), value(0) {}
+	VerticalAlign(float value) : type(Length), value(value) {}
+};
+
+enum class Overflow : uint8_t { Visible, Hidden, Auto, Scroll };
+struct Clip {
+	enum class Type : uint8_t { Auto, None, Number };
+	// Note, internally 'number' is encoded with Auto as 0 and None as -1. However, the enum must correspond to the keywords in StyleSheetSpec.
+	int number = 0;
+	Clip() {}
+	Clip(Type type, int number = 0) : number(type == Type::Auto ? 0 : (type == Type::None ? -1 : number)) {}
+};
+
+enum class Visibility : uint8_t { Visible, Hidden };
+
+enum class FontStyle : uint8_t { Normal, Italic };
+enum class FontWeight : uint8_t { Normal, Bold };
+
+enum class TextAlign : uint8_t { Left, Right, Center, Justify };
+enum class TextDecoration : uint8_t { None, Underline, Overline, LineThrough };
+enum class TextTransform : uint8_t { None, Capitalize, Uppercase, Lowercase };
+enum class WhiteSpace : uint8_t { Normal, Pre, Nowrap, Prewrap, Preline };
+
+enum class Drag : uint8_t { None, Drag, DragDrop, Block, Clone };
+enum class TabIndex : uint8_t { None, Auto };
+enum class Focus : uint8_t { None, Auto };
+enum class PointerEvents : uint8_t { None, Auto };
+
+using PerspectiveOrigin = LengthPercentage;
+using TransformOrigin = LengthPercentage;
+
+enum class OriginX : uint8_t { Left, Center, Right };
+enum class OriginY : uint8_t { Top, Center, Bottom };
+
+
+/* 
+	A computed value is a value resolved as far as possible :before: introducing layouting. See CSS specs for details of each property.
+
+	Note: Enums and default values must correspond to the keywords and defaults in `StyleSheetSpecification.cpp`.
+*/
+
+struct ComputedValues
+{
+	Margin margin_top, margin_right, margin_bottom, margin_left;
+	Padding padding_top, padding_right, padding_bottom, padding_left;
+	float border_top_width = 0, border_right_width = 0, border_bottom_width = 0, border_left_width = 0;
+	Colourb border_top_color{ 255, 255, 255 }, border_right_color{ 255, 255, 255 }, border_bottom_color{ 255, 255, 255 }, border_left_color{ 255, 255, 255 };
+
+	Display display = Display::Inline;
+	Position position = Position::Static;
+
+	Top top{ Top::Auto };
+	Right right{ Right::Auto };
+	Bottom bottom{ Bottom::Auto };
+	Left left{ Left::Auto };
+
+	Float float_ = Float::None;
+	Clear clear = Clear::None;
+
+	ZIndex z_index = { ZIndex::Auto };
+
+	Width width = { Width::Auto };
+	MinWidth min_width;
+	MaxWidth max_width{ MaxWidth::Length, -1.f };
+	Height height = { Height::Auto };
+	MinHeight min_height;
+	MaxHeight max_height{ MaxHeight::Length, -1.f };
+
+	LineHeight line_height;
+	VerticalAlign vertical_align;
+
+	Overflow overflow_x = Overflow::Visible, overflow_y = Overflow::Visible;
+	Clip clip;
+
+	Visibility visibility = Visibility::Visible;
+
+	Colourb background_color = Colourb(255, 255, 255, 0);
+	Colourb color = Colourb(255, 255, 255);
+	Colourb image_color = Colourb(255, 255, 255);
+	float opacity = 1;
+
+	String font_family;
+	FontStyle font_style = FontStyle::Normal;
+	FontWeight font_weight = FontWeight::Normal;
+	float font_size = 12.f;
+	// Font face used to render text and resolve ex properties. Does not represent a true property
+	// like most computed values, but placed here as it is used and inherited in a similar manner.
+	FontFaceHandle font_face_handle = 0;
+
+	TextAlign text_align = TextAlign::Left;
+	TextDecoration text_decoration = TextDecoration::None;
+	TextTransform text_transform = TextTransform::None;
+	WhiteSpace white_space = WhiteSpace::Normal;
+
+	String cursor;
+
+	Drag drag = Drag::None;
+	TabIndex tab_index = TabIndex::None;
+	Focus focus = Focus::Auto;
+	float scrollbar_margin = 0;
+	PointerEvents pointer_events = PointerEvents::Auto;
+
+	float perspective = 0;
+	PerspectiveOrigin perspective_origin_x = { PerspectiveOrigin::Percentage, 50.f };
+	PerspectiveOrigin perspective_origin_y = { PerspectiveOrigin::Percentage, 50.f };
+
+	TransformPtr transform;
+	TransformOrigin transform_origin_x = { TransformOrigin::Percentage, 50.f };
+	TransformOrigin transform_origin_y = { TransformOrigin::Percentage, 50.f };
+	float transform_origin_z = 0.0f;
+
+	TransitionList transition;
+	AnimationList animation;
+
+	DecoratorsPtr decorator;
+	FontEffectsPtr font_effect; // Sorted by layer first (back then front), then by declaration order.
+};
+}
+
+
+// Resolves a computed LengthPercentage value to the base unit 'px'. 
+// Percentages are scaled by the base_value.
+// Note: Auto must be manually handled during layout, here it returns zero.
+RMLUICORE_API float ResolveValue(Style::LengthPercentageAuto length, float base_value);
+RMLUICORE_API float ResolveValue(Style::LengthPercentage length, float base_value);
+
+
+using ComputedValues = Style::ComputedValues;
+
+}
+}
+
+#endif

+ 755 - 0
Include/RmlUi/Core/Containers/chobo/flat_map.hpp

@@ -0,0 +1,755 @@
+// chobo-flat-map v1.01
+//
+// std::map-like class with an underlying vector
+//
+// MIT License:
+// Copyright(c) 2016 Chobolabs Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files(the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and / or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions :
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+//                  VERSION HISTORY
+//
+//  1.01 (2016-09-27) Fix for keys with no operator==. Clean up of assignment.
+//                    Added swap method.
+//  1.00 (2016-09-23) First public release
+//
+//
+//                  DOCUMENTATION
+//
+// Simply include this file wherever you need.
+// It defines the class chobo::flat_map, which is an almsot drop-in replacement
+// of std::map. Flat map has an optional underlying container which by default
+// is std::vector. Thus the items in the map are in a continuous block of
+// memory. Thus iterating over the map is cache friendly, at the cost of
+// O(n) for insert and erase.
+//
+// The elements inside (like in std::map) are kept in an order sorted by key.
+// Getting a value by key is O(log2 n)
+//
+// It generally performs much faster than std::map for smaller sets of elements
+//
+// The difference with std::map, which makes flat_map an not-exactly-drop-in
+// replacement is the last template argument:
+// * std::map has <key, value, compare, allocator>
+// * chobo::flat_map has <key, value, compare, container>
+// The container must be an std::vector compatible type (chobo::static_vector
+// and chobo::vector_ptr are, for example, viable). The container value type
+// must be std::pair<key, value>.
+//
+//                  Changing the allocator.
+//
+// If you want to change the allocator of flat map, you'll have to provide a
+// container with the appriate one. Example:
+//
+// chobo::flat_map<
+//      string,
+//      int,
+//      less<string>,
+//      std::vector<pair<string, int>, MyAllocator<pair<string, int>>
+//  > mymap
+//
+//
+//                  Configuration
+//
+// chobo::flat_map has two configurable settings:
+//
+// 1. Throw
+// Whether to throw exceptions: when `at` is called with a non-existent key.
+// By default, like std::map, it throws an std::out_of_range exception. If you define
+// CHOBO_FLAT_MAP_NO_THROW before including this header, the exception will
+// be substituted by an assertion.
+//
+// 2. const char* overloads
+// By default chobo::flat_map provides overloads for the access methods
+// (at, operator[], find, lower_bound, count) for const char* for cases when
+// std::string is the key, so that no allocations happen when accessing with
+// a C-string of a string literal.
+// However if const char* or any other class with implicit conversion from
+// const char* is the key, they won't compile.
+// If you plan on using flat_map with such keys, you'll need to define
+// CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS before including the header
+//
+//
+//                  TESTS
+//
+// The tests are included in the header file and use doctest (https://github.com/onqtam/doctest).
+// To run them, define CHOBO_FLAT_MAP_TEST_WITH_DOCTEST before including
+// the header in a file which has doctest.h already included.
+//
+// Additionally if chobo::static_vector is also available you may define
+// CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST to test flat_map with an
+// unrelying static_vector
+//
+// Additionally if chobo::vector_ptr is also available you may define
+// CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST to test flat_map with an
+// unrelying vector_ptr
+//
+#pragma once
+
+#include <vector>
+#include <algorithm>
+#include <type_traits>
+
+#if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
+#include <cstring>
+#endif
+
+#if !defined(CHOBO_FLAT_MAP_NO_THROW)
+#   include <stdexcept>
+#   define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() throw std::out_of_range("chobo::flat_map out of range")
+#else
+#   include <cassert>
+#   define _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE() assert(false && "chobo::flat_map out of range")
+#endif
+
+namespace chobo
+{
+
+template <typename Key, typename T, typename Compare = std::less<Key>, typename Container = std::vector<std::pair<Key, T>>>
+class flat_map
+{
+public:
+    typedef Key key_type;
+    typedef T mapped_type;
+    typedef std::pair<Key, T> value_type;
+    typedef Container container_type;
+    typedef Compare key_compare;
+    typedef value_type& reference;
+    typedef const value_type& const_reference;
+    typedef typename container_type::allocator_type allocator_type;
+    typedef typename std::allocator_traits<allocator_type>::pointer pointer;
+    typedef typename std::allocator_traits<allocator_type>::pointer const_pointer;
+    typedef typename container_type::iterator iterator;
+    typedef typename container_type::const_iterator const_iterator;
+    typedef typename container_type::reverse_iterator reverse_iterator;
+    typedef typename container_type::const_reverse_iterator const_reverse_iterator;
+    typedef typename container_type::difference_type difference_type;
+    typedef typename container_type::size_type size_type;
+
+    flat_map()
+    {}
+
+    explicit flat_map(const key_compare& comp, const allocator_type& alloc = allocator_type())
+        : m_cmp(comp)
+        , m_container(alloc)
+    {}
+
+    flat_map(const flat_map& x) = default;
+    flat_map(flat_map&& x) = default;
+
+    flat_map(std::initializer_list<value_type> ilist) : m_cmp(Compare())
+    {
+        m_container.reserve(ilist.size());
+        for (auto&& il : ilist)
+            emplace(il);
+    }
+
+    flat_map& operator=(const flat_map& x)
+    {
+        m_cmp = x.m_cmp;
+        m_container = x.m_container;
+        return *this;
+    }
+    flat_map& operator=(flat_map&& x)
+    {
+        m_cmp = std::move(x.m_cmp);
+        m_container = std::move(x.m_container);
+        return *this;
+    }
+
+    iterator begin() noexcept { return m_container.begin(); }
+    const_iterator begin() const noexcept { return m_container.begin(); }
+    iterator end() noexcept { return m_container.end(); }
+    const_iterator end() const noexcept { return m_container.end(); }
+    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
+    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
+    reverse_iterator rend() noexcept { return m_container.rend(); }
+    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
+    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
+    const_iterator cend() const noexcept { return m_container.cend(); }
+
+    bool empty() const noexcept { return m_container.empty(); }
+    size_type size() const noexcept { return m_container.size(); }
+    size_type max_size() const noexcept { return m_container.max_size(); }
+
+    void reserve(size_type count) { return m_container.reserve(count); }
+    size_type capacity() const noexcept { return m_container.capacity(); }
+
+    void clear() noexcept { m_container.clear(); }
+
+    iterator lower_bound(const key_type& k)
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp);
+    }
+
+    const_iterator lower_bound(const key_type& k) const
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp);
+    }
+
+    iterator find(const key_type& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+            return i;
+
+        return end();
+    }
+
+    const_iterator find(const key_type& k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+            return i;
+
+        return end();
+    }
+
+    size_t count(const key_type& k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+    template <typename P>
+    std::pair<iterator, bool> insert(P&& val)
+    {
+        auto i = lower_bound(val.first);
+        if (i != end() && !m_cmp(val.first, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, std::forward<P>(val)), true };
+    }
+
+    std::pair<iterator, bool> insert(const value_type& val)
+    {
+        auto i = lower_bound(val.first);
+        if (i != end() && !m_cmp(val.first, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, val), true };
+    }
+
+    template <class... Args>
+    std::pair<iterator, bool> emplace(Args&&... args)
+    {
+        value_type val(args...);
+        return insert(std::move(val));
+    }
+
+    iterator erase(const_iterator pos)
+    {
+        return m_container.erase(pos);
+    }
+
+    size_type erase(const key_type& k)
+    {
+        auto i = find(k);
+        if (i == end())
+        {
+            return 0;
+        }
+
+        erase(i);
+        return 1;
+    }
+
+    mapped_type& operator[](const key_type& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+        {
+            return i->second;
+        }
+
+        i = m_container.emplace(i, k, mapped_type());
+        return i->second;
+    }
+
+    mapped_type& operator[](key_type&& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+        {
+            return i->second;
+        }
+
+        i = m_container.emplace(i, std::forward<key_type>(k), mapped_type());
+        return i->second;
+    }
+
+    mapped_type& at(const key_type& k)
+    {
+        auto i = lower_bound(k);
+        if (i == end() || m_cmp(*i, k))
+        {
+            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    const mapped_type& at(const key_type& k) const
+    {
+        auto i = lower_bound(k);
+        if (i == end() || m_cmp(*i, k))
+        {
+            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    void swap(flat_map& x)
+    {
+        std::swap(m_cmp, x.m_cmp);
+        m_container.swap(x.m_container);
+    }
+
+    const container_type& container() const noexcept
+    {
+        return m_container;
+    }
+
+    // DANGER! If you're not careful with this function, you may irreversably break the map
+    container_type& modify_container() noexcept
+    {
+        return m_container;
+    }
+
+#if !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
+    ///////////////////////////////////////////////////////////////////////////////////
+    // const char* overloads for maps with an std::string key to avoid allocs
+    iterator lower_bound(const char* k)
+    {
+        static_assert(std::is_same<std::string, key_type>::value, "flat_map::lower_bound(const char*) works only for std::strings");
+        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
+        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
+        {
+            return strcmp(a.first.c_str(), b) < 0;
+        });
+    }
+
+    const_iterator lower_bound(const char* k) const
+    {
+        static_assert(std::is_same<std::string, key_type>::value, "flat_map::lower_bound(const char*) works only for std::strings");
+        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_map::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
+        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
+        {
+            return strcmp(a.first.c_str(), b) < 0;
+        });
+    }
+
+    mapped_type& operator[](const char* k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && i->first == k)
+        {
+            return i->second;
+        }
+
+        i = m_container.emplace(i, k, mapped_type());
+        return i->second;
+    }
+
+    mapped_type& at(const char* k)
+    {
+        auto i = lower_bound(k);
+        if (i == end() || i->first != k)
+        {
+            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    const mapped_type& at(const char* k) const
+    {
+        auto i = lower_bound(k);
+        if (i == end() || i->first != k)
+        {
+            _CHOBO_THROW_FLAT_MAP_OUT_OF_RANGE();
+        }
+
+        return i->second;
+    }
+
+    iterator find(const char* k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && i->first == k)
+            return i;
+
+        return end();
+    }
+
+    const_iterator find(const char* k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && i->first == k)
+            return i;
+
+        return end();
+    }
+
+    size_t count(const char* k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+#endif // !defined(CHOBO_FLAT_MAP_NO_CONST_CHAR_OVERLOADS)
+
+private:
+    struct pair_compare
+    {
+        pair_compare() = default;
+        pair_compare(const key_compare& kc) : kcmp(kc) {}
+        bool operator()(const value_type& a, const key_type& b) const
+        {
+            return kcmp(a.first, b);
+        }
+
+        bool operator()(const key_type& a, const value_type& b) const
+        {
+            return kcmp(a, b.first);
+        }
+
+        key_compare kcmp;
+    };
+    pair_compare m_cmp;
+    container_type m_container;
+};
+
+template <typename Key, typename T, typename Compare, typename Container>
+bool operator==(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
+{
+    return a.container() == b.container();
+}
+
+template <typename Key, typename T, typename Compare, typename Container>
+bool operator!=(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
+{
+    return a.container() != b.container();
+}
+template <typename Key, typename T, typename Compare, typename Container>
+bool operator<(const flat_map<Key, T, Compare, Container>& a, const flat_map<Key, T, Compare, Container>& b)
+{
+	return a.container() < b.container();
+}
+
+}
+
+#if defined(CHOBO_FLAT_MAP_TEST_WITH_DOCTEST)
+
+#include <string>
+
+namespace chobo_flat_map_test
+{
+
+// struct with no operator==
+struct int_wrap
+{
+    int_wrap() = default;
+    int_wrap(int i) : val(i) {}
+    int val;
+
+    struct compare
+    {
+        bool operator()(const int_wrap& a, const int_wrap& b) const
+        {
+            return a.val < b.val;
+        }
+    };
+};
+
+}
+
+TEST_CASE("[flat_map] test")
+{
+    using namespace chobo;
+    using namespace chobo_flat_map_test;
+
+    flat_map<int, float> ifmap;
+    CHECK(ifmap.empty());
+    CHECK(ifmap.size() == 0);
+    CHECK(ifmap.capacity() == 0);
+    CHECK(ifmap.begin() == ifmap.end());
+
+    ifmap[1] = 3.2f;
+    CHECK(ifmap.size() == 1);
+
+    auto ifit = ifmap.begin();
+    CHECK(ifit->first == 1);
+    CHECK(ifit->second == 3.2f);
+    CHECK(ifmap[1] == 3.2f);
+    CHECK(ifmap.at(1) == 3.2f);
+    CHECK(ifmap.count(1) == 1);
+    CHECK(ifmap.count(5) == 0);
+
+    ++ifit;
+    CHECK(ifit == ifmap.end());
+
+    auto res = ifmap.insert(std::make_pair(6, 3.14f));
+    CHECK(res.second);
+    CHECK(res.first == ifmap.begin() + 1);
+
+    res = ifmap.emplace(3, 5.5f);
+    CHECK(res.second);
+    CHECK(res.first == ifmap.begin() + 1);
+
+    res = ifmap.emplace(6, 8.f);
+    CHECK(!res.second);
+    CHECK(res.first == ifmap.begin() + 2);
+
+    ifmap[2] = 5;
+    ifmap[52] = 15;
+    ifmap[12] = 1;
+    CHECK(ifmap.size() == 6);
+
+    auto cmp = [](const flat_map<int, float>::value_type& a, const flat_map<int, float>::value_type& b) -> bool
+    {
+        return a.first < b.first;
+    };
+
+    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
+
+    ifmap.erase(12);
+    CHECK(ifmap.size() == 5);
+
+    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
+
+    ifit = ifmap.find(12);
+    CHECK(ifit == ifmap.end());
+
+    ifit = ifmap.find(6);
+    CHECK(ifit != ifmap.end());
+    ifmap.erase(ifit);
+
+    CHECK(ifmap.size() == 4);
+    CHECK(std::is_sorted(ifmap.begin(), ifmap.end(), cmp));
+    ifit = ifmap.find(6);
+    CHECK(ifit == ifmap.end());
+
+    //
+
+    flat_map<std::string, int> simap;
+
+    CHECK(simap["123"] == 0);
+
+    CHECK(simap.begin()->first.c_str() == "123");
+
+    ++simap["asd"];
+
+    auto siit = simap.find("asd");
+    CHECK(siit != simap.end());
+    CHECK(siit->second == 1);
+    CHECK(siit == simap.begin() + 1);
+
+    CHECK(simap.count("bababa") == 0);
+    CHECK(simap.count("asd") == 1);
+
+    std::string asd = "asd";
+    CHECK(simap.at(asd) == simap.at("asd"));
+
+    simap["0The quick brown fox jumps over the lazy dog"] = 555;
+    CHECK(simap.begin()->first[1] == 'T');
+    const void* cstr = simap.begin()->first.c_str();
+
+    auto simap2 = std::move(simap);
+    CHECK(simap.empty());
+    CHECK(simap2.begin()->first.c_str() == cstr);
+
+    simap = std::move(simap2);
+    CHECK(simap2.empty());
+    CHECK(simap.begin()->first.c_str() == cstr);
+
+    CHECK(simap2 != simap);
+    simap2 = simap;
+    CHECK(simap2 == simap);
+
+    // no == comparable tests
+    flat_map<int_wrap, int, int_wrap::compare> iwmap;
+    iwmap[5] = 1;
+    iwmap[20] = 15;
+    iwmap[10] = 5;
+
+    auto iwi = iwmap.emplace(3, 4);
+    CHECK(iwi.second == true);
+    CHECK(iwi.first == iwmap.begin());
+
+    CHECK(iwmap.begin()->first.val == 3);
+    CHECK(iwmap.begin()->second == 4);
+    CHECK(iwmap.rbegin()->first.val == 20);
+    CHECK(iwmap.rbegin()->second == 15);
+    CHECK(iwmap.at(10) == 5);
+
+    iwi = iwmap.insert(std::pair<int_wrap, int>(11, 6));
+    CHECK(iwi.second == true);
+    CHECK(iwi.first + 2 == iwmap.end());
+
+    CHECK(iwmap[11] == 6);
+
+    iwi = iwmap.emplace(10, 55);
+    CHECK(iwi.second == false);
+    CHECK(iwi.first->second == 5);
+
+    CHECK(iwmap.find(18) == iwmap.end());
+    CHECK(iwmap.find(11) != iwmap.end());
+
+    const auto ciwmap = iwmap;
+
+    CHECK(ciwmap.begin()->first.val == 3);
+    CHECK(ciwmap.begin()->second == 4);
+    CHECK(ciwmap.rbegin()->first.val == 20);
+    CHECK(ciwmap.rbegin()->second == 15);
+    CHECK(ciwmap.at(10) == 5);
+
+    CHECK(ciwmap.find(18) == ciwmap.end());
+    CHECK(ciwmap.find(11) != ciwmap.end());
+
+    // swap
+    flat_map<int, int> m1, m2;
+    m1.reserve(10);
+    m1[1] = 2;
+    m1[2] = 5;
+    auto m1c = m1.capacity();
+
+    CHECK(m2.capacity() == 0);
+    m1.swap(m2);
+
+    CHECK(m2.size() == 2);
+    CHECK(m2.capacity() == m1c);
+    CHECK(m1.capacity() == 0);
+
+    // self usurp
+    m2 = m2;
+    CHECK(m2.size() == 2);
+    CHECK(m2.capacity() == m1c);
+}
+
+#if defined(CHOBO_FLAT_MAP_TEST_STATIC_VECTOR_WITH_DOCTEST)
+
+TEST_CASE("[flat_map] static_vector test")
+{
+    using namespace chobo;
+
+    flat_map<int, char, std::less<int>, static_vector<std::pair<int, char>, 10>> smap;
+    CHECK(smap.empty());
+    CHECK(smap.size() == 0);
+    CHECK(smap.capacity() == 10);
+    CHECK(smap.begin() == smap.end());
+
+    smap[1] = 3;
+    CHECK(smap.size() == 1);
+
+    auto ifit = smap.begin();
+    CHECK(ifit->first == 1);
+    CHECK(ifit->second == 3);
+    CHECK(smap[1] == 3);
+    CHECK(smap.at(1) == 3);
+    CHECK(smap.count(1) == 1);
+    CHECK(smap.count(5) == 0);
+
+    ++ifit;
+    CHECK(ifit == smap.end());
+
+    auto res = smap.insert(std::make_pair(6, 3));
+    CHECK(res.second);
+    CHECK(res.first == smap.begin() + 1);
+
+    res = smap.emplace(3, 5);
+    CHECK(res.second);
+    CHECK(res.first == smap.begin() + 1);
+
+    res = smap.emplace(6, 8);
+    CHECK(!res.second);
+    CHECK(res.first == smap.begin() + 2);
+
+    smap[2] = 5;
+    smap[52] = 15;
+    smap[12] = 1;
+    CHECK(smap.size() == 6);
+
+    auto cmp = [](const flat_map<int, float>::value_type& a, const flat_map<int, float>::value_type& b) -> bool
+    {
+        return a.first < b.first;
+    };
+
+    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
+
+    smap.erase(12);
+    CHECK(smap.size() == 5);
+
+    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
+
+    ifit = smap.find(12);
+    CHECK(ifit == smap.end());
+
+    ifit = smap.find(6);
+    CHECK(ifit != smap.end());
+    smap.erase(ifit);
+
+    CHECK(smap.size() == 4);
+    CHECK(std::is_sorted(smap.begin(), smap.end(), cmp));
+    ifit = smap.find(6);
+    CHECK(ifit == smap.end());
+}
+
+#endif
+
+#if defined(CHOBO_FLAT_MAP_TEST_VECTOR_PTR_WITH_DOCTEST)
+
+TEST_CASE("[flat_map] vector_ptr test")
+{
+    using namespace chobo;
+    flat_map<int, char, std::less<int>, vector_ptr<std::pair<int, char>>> smap;
+
+    std::vector<std::pair<int, char>> vec;
+    smap.modify_container().reset(&vec);
+
+    smap[1] = '1';
+    smap[3] = '3';
+
+    CHECK(smap.at(3) == '3');
+
+    auto smap2 = smap;
+    CHECK(smap2.size() == 2);
+    CHECK(smap2[1] == '1');
+    CHECK(smap2.at(3) == '3');
+
+    smap2[0] = '0';
+
+    CHECK(smap.size() == 3);
+    CHECK(smap[0] == '0');
+
+    smap.clear();
+
+    CHECK(smap2.empty());
+}
+
+#endif
+
+
+#endif
+

+ 561 - 0
Include/RmlUi/Core/Containers/chobo/flat_set.hpp

@@ -0,0 +1,561 @@
+// chobo-flat-set v1.00
+//
+// std::set-like class with an underlying vector
+//
+// Unofficial, chobo-like container, largely based on chobo::flat_map
+//
+// MIT License:
+// Copyright(c) 2019 Michael R. P. Ragazzon 
+// Copyright(c) 2016 Chobolabs Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files(the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and / or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions :
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//
+//                  VERSION HISTORY
+//
+//  1.00 (2019-05-04) First public release
+//
+//
+//                  DOCUMENTATION
+//
+// Simply include this file wherever you need.
+// It defines the class chobo::flat_set, which is an almsot drop-in replacement
+// of std::set. Flat set has an optional underlying container which by default
+// is std::vector. Thus the items in the set are in a continuous block of
+// memory. Thus iterating over the set is cache friendly, at the cost of
+// O(n) for insert and erase.
+//
+// The elements inside (like in std::set) are kept in an order sorted by key.
+// Getting a value by key is O(log2 n)
+//
+// It generally performs much faster than std::set for smaller sets of elements
+//
+// The difference with std::set, which makes flat_set an not-exactly-drop-in
+// replacement is the last template argument:
+// * std::set has <value, compare, allocator>
+// * chobo::flat_set has <value, compare, container>
+// The container must be an std::vector compatible type (chobo::static_vector
+// and chobo::vector_ptr are, for example, viable). The container value type
+// must be 'value'.
+//
+//                  Changing the allocator.
+//
+// If you want to change the allocator of flat set, you'll have to provide a
+// container with the appriate one. Example:
+//
+// chobo::flat_set<
+//      string,
+//      less<string>,
+//      std::vector<<string>, MyAllocator<string>>
+//  > myset
+//
+//
+//                  Configuration
+//
+// chobo::flat_set has one configurable setting:
+//
+// 1. const char* overloads
+// By default chobo::flat_set provides overloads for the access methods
+// (at, operator[], find, lower_bound, count) for const char* for cases when
+// std::string is the key, so that no allocations happen when accessing with
+// a C-string of a string literal.
+// However if const char* or any other class with implicit conversion from
+// const char* is the key, they won't compile.
+// If you plan on using flat_set with such keys, you'll need to define
+// CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS before including the header
+//
+//
+//                  TESTS
+//
+// The tests are included in the header file and use doctest (https://github.com/onqtam/doctest).
+// To run them, define CHOBO_FLAT_SET_TEST_WITH_DOCTEST before including
+// the header in a file which has doctest.h already included.
+//
+// Additionally if chobo::static_vector is also available you may define
+// CHOBO_FLAT_SET_TEST_STATIC_VECTOR_WITH_DOCTEST to test flat_set with an
+// unrelying static_vector
+//
+// Additionally if chobo::vector_ptr is also available you may define
+// CHOBO_FLAT_SET_TEST_VECTOR_PTR_WITH_DOCTEST to test flat_set with an
+// unrelying vector_ptr
+//
+#pragma once
+
+#include <vector>
+#include <algorithm>
+#include <type_traits>
+
+#if !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
+#include <cstring>
+#endif
+
+namespace chobo
+{
+
+template <typename T, typename Compare = std::less<T>, typename Container = std::vector<T>>
+class flat_set
+{
+public:
+    typedef T key_type;
+    typedef T value_type;
+    typedef Container container_type;
+    typedef Compare key_compare;
+    typedef value_type& reference;
+    typedef const value_type& const_reference;
+    typedef typename container_type::allocator_type allocator_type;
+    typedef typename std::allocator_traits<allocator_type>::pointer pointer;
+    typedef typename std::allocator_traits<allocator_type>::pointer const_pointer;
+    typedef typename container_type::iterator iterator;
+    typedef typename container_type::const_iterator const_iterator;
+    typedef typename container_type::reverse_iterator reverse_iterator;
+    typedef typename container_type::const_reverse_iterator const_reverse_iterator;
+    typedef typename container_type::difference_type difference_type;
+    typedef typename container_type::size_type size_type;
+
+    flat_set()
+    {}
+
+    explicit flat_set(const key_compare& comp, const allocator_type& alloc = allocator_type())
+        : m_cmp(comp)
+        , m_container(alloc)
+    {}
+
+    flat_set(const flat_set& x) = default;
+    flat_set(flat_set&& x) = default;
+
+
+    flat_set(std::initializer_list<value_type> ilist)
+    {
+        m_container.reserve(ilist.size());
+        for (auto&& il : ilist)
+            emplace(il);
+    }
+
+    flat_set& operator=(const flat_set& x)
+    {
+        m_cmp = x.m_cmp;
+        m_container = x.m_container;
+        return *this;
+    }
+    flat_set& operator=(flat_set&& x) noexcept
+    {
+        m_cmp = std::move(x.m_cmp);
+        m_container = std::move(x.m_container);
+        return *this;
+    }
+
+    iterator begin() noexcept { return m_container.begin(); }
+    const_iterator begin() const noexcept { return m_container.begin(); }
+    iterator end() noexcept { return m_container.end(); }
+    const_iterator end() const noexcept { return m_container.end(); }
+    reverse_iterator rbegin() noexcept { return m_container.rbegin(); }
+    const_reverse_iterator rbegin() const noexcept { return m_container.rbegin(); }
+    reverse_iterator rend() noexcept { return m_container.rend(); }
+    const_reverse_iterator rend() const noexcept { return m_container.rend(); }
+    const_iterator cbegin() const noexcept { return m_container.cbegin(); }
+    const_iterator cend() const noexcept { return m_container.cend(); }
+
+    bool empty() const noexcept { return m_container.empty(); }
+    size_type size() const noexcept { return m_container.size(); }
+    size_type max_size() const noexcept { return m_container.max_size(); }
+
+    void reserve(size_type count) { return m_container.reserve(count); }
+    size_type capacity() const noexcept { return m_container.capacity(); }
+
+    void clear() noexcept { m_container.clear(); }
+
+    iterator lower_bound(const key_type& k)
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp);
+    }
+
+    const_iterator lower_bound(const key_type& k) const
+    {
+        return std::lower_bound(m_container.begin(), m_container.end(), k, m_cmp);
+    }
+
+    iterator find(const key_type& k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+            return i;
+
+        return end();
+    }
+
+    const_iterator find(const key_type& k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && !m_cmp(k, *i))
+            return i;
+
+        return end();
+    }
+
+    size_t count(const key_type& k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+    template <typename P>
+    std::pair<iterator, bool> insert(P&& val)
+    {
+        auto i = lower_bound(val);
+        if (i != end() && !m_cmp(val, *i))
+        {
+            return { i, false };
+        }
+
+        return{ m_container.emplace(i, std::forward<P>(val)), true };
+    }
+
+	template <typename InputIt >
+    void insert(InputIt first, InputIt last)
+    {
+		difference_type diff = std::distance(first, last);
+		if(diff > 0) reserve(size() + (size_t)diff);
+		for (auto it = first; it != last; ++it)
+			emplace(*it);
+    }
+
+    template <class... Args>
+    std::pair<iterator, bool> emplace(Args&&... args)
+    {
+        value_type val(args...);
+        return insert(std::move(val));
+    }
+
+    iterator erase(const_iterator pos)
+    {
+        return m_container.erase(pos);
+    }
+
+    size_type erase(const key_type& k)
+    {
+        auto i = find(k);
+        if (i == end())
+        {
+            return 0;
+        }
+
+        erase(i);
+        return 1;
+    }
+
+    void swap(flat_set& x)
+    {
+        std::swap(m_cmp, x.m_cmp);
+        m_container.swap(x.m_container);
+    }
+
+    const container_type& container() const noexcept
+    {
+        return m_container;
+    }
+
+    // DANGER! If you're not careful with this function, you may irreversably break the set
+    container_type& modify_container() noexcept
+    {
+        return m_container;
+    }
+
+#if !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
+    ///////////////////////////////////////////////////////////////////////////////////
+    // const char* overloads for sets with an std::string key to avoid allocs
+    iterator lower_bound(const char* k)
+    {
+        static_assert(std::is_same<std::string, key_type>::value, "flat_set::lower_bound(const char*) works only for std::strings");
+        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_set::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
+        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
+        {
+            return strcmp(a.c_str(), b) < 0;
+        });
+    }
+
+    const_iterator lower_bound(const char* k) const
+    {
+        static_assert(std::is_same<std::string, key_type>::value, "flat_set::lower_bound(const char*) works only for std::strings");
+        static_assert(std::is_same<std::less<std::string>, key_compare>::value, "flat_set::lower_bound(const char*) works only for std::string-s, compared with std::less<std::string>");
+        return std::lower_bound(m_container.begin(), m_container.end(), k, [](const value_type& a, const char* b) -> bool
+        {
+            return strcmp(a.c_str(), b) < 0;
+        });
+    }
+
+    iterator find(const char* k)
+    {
+        auto i = lower_bound(k);
+        if (i != end() && *i == k)
+            return i;
+
+        return end();
+    }
+
+    const_iterator find(const char* k) const
+    {
+        auto i = lower_bound(k);
+        if (i != end() && *i == k)
+            return i;
+
+        return end();
+    }
+
+    size_t count(const char* k) const
+    {
+        return find(k) == end() ? 0 : 1;
+    }
+
+#endif // !defined(CHOBO_FLAT_SET_NO_CONST_CHAR_OVERLOADS)
+
+private:
+	key_compare m_cmp;
+    container_type m_container;
+};
+
+template <typename T, typename Compare, typename Container>
+bool operator==(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
+{
+    return a.container() == b.container();
+}
+template <typename T, typename Compare, typename Container>
+bool operator!=(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
+{
+    return a.container() != b.container();
+}
+template <typename T, typename Compare, typename Container>
+bool operator<(const flat_set<T, Compare, Container>& a, const flat_set<T, Compare, Container>& b)
+{
+	return a.container() < b.container();
+}
+
+}
+
+#if defined(CHOBO_FLAT_SET_TEST_WITH_DOCTEST)
+
+#include <string>
+
+namespace chobo_flat_set_test
+{
+
+// struct with no operator==
+struct int_wrap
+{
+    int_wrap() = default;
+    int_wrap(int i) : val(i) {}
+    int val;
+
+    struct compare
+    {
+        bool operator()(const int_wrap& a, const int_wrap& b) const
+        {
+            return a.val < b.val;
+        }
+    };
+};
+
+}
+
+TEST_CASE("[flat_set] test")
+{
+    using namespace chobo;
+    using namespace chobo_flat_set_test;
+
+    flat_set<int> iset;
+    CHECK(iset.empty());
+    CHECK(iset.size() == 0);
+    CHECK(iset.capacity() == 0);
+    CHECK(iset.begin() == iset.end());
+
+    iset.insert(3);
+    CHECK(iset.size() == 1);
+
+    auto iit = iset.begin();
+    CHECK(*iit == 3);
+    CHECK(iset.count(3) == 1);
+    CHECK(iset.count(5) == 0);
+
+    ++iit;
+    CHECK(iit == iset.end());
+
+    auto res = iset.insert(6);
+    CHECK(res.second);
+    CHECK(res.first == iset.begin() + 1);
+
+    res = iset.emplace(4);
+    CHECK(res.second);
+    CHECK(res.first == iset.begin() + 1);
+
+    res = iset.emplace(6);
+    CHECK(!res.second);
+    CHECK(res.first == iset.begin() + 2);
+
+    iset.emplace(3);
+	CHECK(iset.size() == 3);
+    iset.emplace(9);
+    iset.insert(9);
+    iset.emplace(12);
+    CHECK(iset.size() == 5);
+
+    auto cmp = [](const flat_set<int>::value_type& a, const flat_set<int>::value_type& b) -> bool
+    {
+        return a < b;
+    };
+
+    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
+
+    iset.erase(12);
+    CHECK(iset.size() == 4);
+
+    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
+
+    iit = iset.find(11);
+    CHECK(iit == iset.end());
+
+    iit = iset.find(6);
+    CHECK(iit != iset.end());
+    iset.erase(iit);
+
+    CHECK(iset.size() == 3);
+    CHECK(std::is_sorted(iset.begin(), iset.end(), cmp));
+    iit = iset.find(6);
+    CHECK(iit == iset.end());
+
+    //
+
+    flat_set<std::string> sset;
+
+    CHECK(sset.find("123") == sset.end());
+	sset.emplace("123");
+    CHECK(*sset.begin() == "123");
+
+    sset.emplace("asd");
+
+    auto sit = sset.find("asd");
+    CHECK(sit != sset.end());
+    CHECK(sit == sset.begin() + 1);
+
+    CHECK(sset.count("bababa") == 0);
+    CHECK(sset.count("asd") == 1);
+
+    std::string asd = "asd";
+    CHECK(sset.find(asd) == sset.find("asd"));
+
+    sset.emplace("0The quick brown fox jumps over the lazy dog");
+    CHECK(sset.begin()->at(1) == 'T');
+    const void* cstr = sset.begin()->c_str();
+
+    auto sset2 = std::move(sset);
+    CHECK(sset.empty());
+    CHECK(sset2.begin()->c_str() == cstr);
+
+    sset = std::move(sset2);
+    CHECK(sset2.empty());
+    CHECK(sset.begin()->c_str() == cstr);
+
+    CHECK(sset2 != sset);
+    sset2 = sset;
+    CHECK(sset2 == sset);
+
+    // no == comparable tests
+    flat_set<int_wrap, int_wrap::compare> iwset;
+    iwset.emplace(5);
+	iwset.emplace(20);
+	iwset.emplace(10);
+
+    auto iwi = iwset.emplace(3);
+    CHECK(iwi.second == true);
+    CHECK(iwi.first == iwset.begin());
+
+    CHECK(iwset.begin()->val == 3);
+    CHECK(iwset.rbegin()->val == 20);
+
+    iwi = iwset.insert(int_wrap(11));
+    CHECK(iwi.second == true);
+    CHECK(iwi.first + 2 == iwset.end());
+
+    iwi = iwset.emplace(int_wrap(10));
+    CHECK(iwi.second == false);
+
+    CHECK(iwset.find(18) == iwset.end());
+    CHECK(iwset.find(11) != iwset.end());
+
+    const auto ciwset = iwset;
+
+    CHECK(ciwset.begin()->val == 3);
+    CHECK(ciwset.rbegin()->val == 20);
+
+    CHECK(ciwset.find(18) == ciwset.end());
+    CHECK(ciwset.find(11) != ciwset.end());
+
+    // swap
+    flat_set<int> m1, m2;
+    m1.reserve(10);
+    m1.emplace(1);
+	m1.emplace(2);
+    auto m1c = m1.capacity();
+
+    CHECK(m2.capacity() == 0);
+    m1.swap(m2);
+
+    CHECK(m2.size() == 2);
+    CHECK(m2.capacity() == m1c);
+    CHECK(m1.capacity() == 0);
+
+    // self usurp
+    m2 = m2;
+    CHECK(m2.size() == 2);
+    CHECK(m2.capacity() == m1c);
+
+
+	// initializer list
+	flat_set<std::string> ilset = { "hello", "great", "magnificent", "world", "hello", "again" };
+	CHECK(ilset.size() == 5);
+	CHECK(std::is_sorted(ilset.begin(), ilset.end()));
+
+	ilset = { "b", "a" };
+	CHECK(ilset.size() == 2);
+	CHECK(std::is_sorted(ilset.begin(), ilset.end()));
+}
+
+#if defined(CHOBO_FLAT_SET_TEST_STATIC_VECTOR_WITH_DOCTEST)
+
+TEST_CASE("[flat_set] static_vector test")
+{
+    using namespace chobo;
+	
+	// Not implemented
+}
+
+#endif
+
+#if defined(CHOBO_FLAT_SET_TEST_VECTOR_PTR_WITH_DOCTEST)
+
+TEST_CASE("[flat_set] vector_ptr test")
+{
+    using namespace chobo;
+
+	// Not implemented
+}
+
+#endif
+
+
+#endif
+

+ 2056 - 0
Include/RmlUi/Core/Containers/robin_hood.h

@@ -0,0 +1,2056 @@
+//                 ______  _____                 ______                _________
+//  ______________ ___  /_ ___(_)_______         ___  /_ ______ ______ ______  /
+//  __  ___/_  __ \__  __ \__  / __  __ \        __  __ \_  __ \_  __ \_  __  /
+//  _  /    / /_/ /_  /_/ /_  /  _  / / /        _  / / // /_/ // /_/ // /_/ /
+//  /_/     \____/ /_.___/ /_/   /_/ /_/ ________/_/ /_/ \____/ \____/ \__,_/
+//                                      _/_____/
+//
+// Fast & memory efficient hashtable based on robin hood hashing for C++11/14/17/20
+// version 3.4.1
+// https://github.com/martinus/robin-hood-hashing
+//
+// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2018-2019 Martin Ankerl <http://martin.ankerl.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+#ifndef ROBIN_HOOD_H_INCLUDED
+#define ROBIN_HOOD_H_INCLUDED
+
+// see https://semver.org/
+#define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes
+#define ROBIN_HOOD_VERSION_MINOR 4 // for adding functionality in a backwards-compatible manner
+#define ROBIN_HOOD_VERSION_PATCH 1 // for backwards-compatible bug fixes
+
+#include <algorithm>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <utility>
+
+// #define ROBIN_HOOD_LOG_ENABLED
+#ifdef ROBIN_HOOD_LOG_ENABLED
+#    include <iostream>
+#    define ROBIN_HOOD_LOG(x) std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl
+#else
+#    define ROBIN_HOOD_LOG(x)
+#endif
+
+// #define ROBIN_HOOD_TRACE_ENABLED
+#ifdef ROBIN_HOOD_TRACE_ENABLED
+#    include <iostream>
+#    define ROBIN_HOOD_TRACE(x) \
+        std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl
+#else
+#    define ROBIN_HOOD_TRACE(x)
+#endif
+
+// all non-argument macros should use this facility. See
+// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/
+#define ROBIN_HOOD(x) ROBIN_HOOD_PRIVATE_DEFINITION_##x()
+
+// mark unused members with this macro
+#define ROBIN_HOOD_UNUSED(identifier)
+
+// bitness
+#if SIZE_MAX == UINT32_MAX
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 32
+#elif SIZE_MAX == UINT64_MAX
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 64
+#else
+#    error Unsupported bitness
+#endif
+
+// endianess
+#ifdef _WIN32
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() 1
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() 0
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() \
+        (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#endif
+
+// inline
+#ifdef _WIN32
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __declspec(noinline)
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __attribute__((noinline))
+#endif
+
+// exceptions
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 0
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 1
+#endif
+
+// count leading/trailing bits
+#ifdef _WIN32
+#    if ROBIN_HOOD(BITNESS) == 32
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward
+#    else
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward64
+#    endif
+#    include <intrin.h>
+#    pragma intrinsic(ROBIN_HOOD(BITSCANFORWARD))
+#    define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x)                                       \
+        [](size_t mask) noexcept->int {                                               \
+            unsigned long index;                                                      \
+            return ROBIN_HOOD(BITSCANFORWARD)(&index, mask) ? static_cast<int>(index) \
+                                                            : ROBIN_HOOD(BITNESS);    \
+        }                                                                             \
+        (x)
+#else
+#    if ROBIN_HOOD(BITNESS) == 32
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzl
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzl
+#    else
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzll
+#        define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzll
+#    endif
+#    define ROBIN_HOOD_COUNT_LEADING_ZEROES(x) ((x) ? ROBIN_HOOD(CLZ)(x) : ROBIN_HOOD(BITNESS))
+#    define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x) ((x) ? ROBIN_HOOD(CTZ)(x) : ROBIN_HOOD(BITNESS))
+#endif
+
+// fallthrough
+#ifndef __has_cpp_attribute // For backwards compatibility
+#    define __has_cpp_attribute(x) 0
+#endif
+#if __has_cpp_attribute(clang::fallthrough)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[clang::fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[gnu::fallthrough]]
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH()
+#endif
+
+// likely/unlikely
+#if defined(_WIN32)
+#    define ROBIN_HOOD_LIKELY(condition) condition
+#    define ROBIN_HOOD_UNLIKELY(condition) condition
+#else
+#    define ROBIN_HOOD_LIKELY(condition) __builtin_expect(condition, 1)
+#    define ROBIN_HOOD_UNLIKELY(condition) __builtin_expect(condition, 0)
+#endif
+
+// workaround missing "is_trivially_copyable" in g++ < 5.0
+// See https://stackoverflow.com/a/31798726/48181
+#if defined(__GNUC__) && __GNUC__ < 5
+#    define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
+#else
+#    define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
+#endif
+
+// helpers for C++ versions, see https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX() __cplusplus
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX98() 199711L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX11() 201103L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX14() 201402L
+#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX17() 201703L
+
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX17)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD() [[nodiscard]]
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD()
+#endif
+
+namespace robin_hood {
+
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14)
+#    define ROBIN_HOOD_STD std
+#else
+
+// c++11 compatibility layer
+namespace ROBIN_HOOD_STD {
+template <class T>
+struct alignment_of
+    : std::integral_constant<std::size_t, alignof(typename std::remove_all_extents<T>::type)> {};
+
+template <class T, T... Ints>
+class integer_sequence {
+public:
+    using value_type = T;
+    static_assert(std::is_integral<value_type>::value, "not integral type");
+    static constexpr std::size_t size() noexcept {
+        return sizeof...(Ints);
+    }
+};
+template <std::size_t... Inds>
+using index_sequence = integer_sequence<std::size_t, Inds...>;
+
+namespace detail_ {
+template <class T, T Begin, T End, bool>
+struct IntSeqImpl {
+    using TValue = T;
+    static_assert(std::is_integral<TValue>::value, "not integral type");
+    static_assert(Begin >= 0 && Begin < End, "unexpected argument (Begin<0 || Begin<=End)");
+
+    template <class, class>
+    struct IntSeqCombiner;
+
+    template <TValue... Inds0, TValue... Inds1>
+    struct IntSeqCombiner<integer_sequence<TValue, Inds0...>, integer_sequence<TValue, Inds1...>> {
+        using TResult = integer_sequence<TValue, Inds0..., Inds1...>;
+    };
+
+    using TResult =
+        typename IntSeqCombiner<typename IntSeqImpl<TValue, Begin, Begin + (End - Begin) / 2,
+                                                    (End - Begin) / 2 == 1>::TResult,
+                                typename IntSeqImpl<TValue, Begin + (End - Begin) / 2, End,
+                                                    (End - Begin + 1) / 2 == 1>::TResult>::TResult;
+};
+
+template <class T, T Begin>
+struct IntSeqImpl<T, Begin, Begin, false> {
+    using TValue = T;
+    static_assert(std::is_integral<TValue>::value, "not integral type");
+    static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+    using TResult = integer_sequence<TValue>;
+};
+
+template <class T, T Begin, T End>
+struct IntSeqImpl<T, Begin, End, true> {
+    using TValue = T;
+    static_assert(std::is_integral<TValue>::value, "not integral type");
+    static_assert(Begin >= 0, "unexpected argument (Begin<0)");
+    using TResult = integer_sequence<TValue, Begin>;
+};
+} // namespace detail_
+
+template <class T, T N>
+using make_integer_sequence = typename detail_::IntSeqImpl<T, 0, N, (N - 0) == 1>::TResult;
+
+template <std::size_t N>
+using make_index_sequence = make_integer_sequence<std::size_t, N>;
+
+template <class... T>
+using index_sequence_for = make_index_sequence<sizeof...(T)>;
+
+} // namespace ROBIN_HOOD_STD
+
+#endif
+
+namespace detail {
+
+// umul
+#if defined(__SIZEOF_INT128__)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_UMUL128() 1
+#    if defined(__GNUC__) || defined(__clang__)
+#        pragma GCC diagnostic push
+#        pragma GCC diagnostic ignored "-Wpedantic"
+using uint128_t = unsigned __int128;
+#        pragma GCC diagnostic pop
+#    endif
+inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high) noexcept {
+    auto result = static_cast<uint128_t>(a) * static_cast<uint128_t>(b);
+    *high = static_cast<uint64_t>(result >> 64U);
+    return static_cast<uint64_t>(result);
+}
+#elif (defined(_WIN32) && ROBIN_HOOD(BITNESS) == 64)
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_UMUL128() 1
+#    include <intrin.h> // for __umulh
+#    pragma intrinsic(__umulh)
+#    ifndef _M_ARM64
+#        pragma intrinsic(_umul128)
+#    endif
+inline uint64_t umul128(uint64_t a, uint64_t b, uint64_t* high) noexcept {
+#    ifdef _M_ARM64
+    *high = __umulh(a, b);
+    return ((uint64_t)(a)) * (b);
+#    else
+    return _umul128(a, b, high);
+#    endif
+}
+#else
+#    define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_UMUL128() 0
+#endif
+
+// This cast gets rid of warnings like "cast from 'uint8_t*' {aka 'unsigned char*'} to
+// 'uint64_t*' {aka 'long unsigned int*'} increases required alignment of target type". Use with
+// care!
+template <typename T>
+inline T reinterpret_cast_no_cast_align_warning(void* ptr) noexcept {
+    return reinterpret_cast<T>(ptr);
+}
+
+template <typename T>
+inline T reinterpret_cast_no_cast_align_warning(void const* ptr) noexcept {
+    return reinterpret_cast<T>(ptr);
+}
+
+// make sure this is not inlined as it is slow and dramatically enlarges code, thus making other
+// inlinings more difficult. Throws are also generally the slow path.
+template <typename E, typename... Args>
+ROBIN_HOOD(NOINLINE)
+#if ROBIN_HOOD(HAS_EXCEPTIONS)
+void doThrow(Args&&... args) {
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
+    throw E(std::forward<Args>(args)...);
+}
+#else
+void doThrow(Args&&... ROBIN_HOOD_UNUSED(args) /*unused*/) {
+    abort();
+}
+#endif
+
+template <typename E, typename T, typename... Args>
+T* assertNotNull(T* t, Args&&... args) {
+    if (ROBIN_HOOD_UNLIKELY(nullptr == t)) {
+        doThrow<E>(std::forward<Args>(args)...);
+    }
+    return t;
+}
+
+template <typename T>
+inline T unaligned_load(void const* ptr) noexcept {
+    // using memcpy so we don't get into unaligned load problems.
+    // compiler should optimize this very well anyways.
+    T t;
+    std::memcpy(&t, ptr, sizeof(T));
+    return t;
+}
+
+// Allocates bulks of memory for objects of type T. This deallocates the memory in the destructor,
+// and keeps a linked list of the allocated memory around. Overhead per allocation is the size of a
+// pointer.
+template <typename T, size_t MinNumAllocs = 4, size_t MaxNumAllocs = 256>
+class BulkPoolAllocator {
+public:
+    BulkPoolAllocator() noexcept = default;
+
+    // does not copy anything, just creates a new allocator.
+    BulkPoolAllocator(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept
+        : mHead(nullptr)
+        , mListForFree(nullptr) {}
+
+    BulkPoolAllocator(BulkPoolAllocator&& o) noexcept
+        : mHead(o.mHead)
+        , mListForFree(o.mListForFree) {
+        o.mListForFree = nullptr;
+        o.mHead = nullptr;
+    }
+
+    BulkPoolAllocator& operator=(BulkPoolAllocator&& o) noexcept {
+        reset();
+        mHead = o.mHead;
+        mListForFree = o.mListForFree;
+        o.mListForFree = nullptr;
+        o.mHead = nullptr;
+        return *this;
+    }
+
+    BulkPoolAllocator&
+    operator=(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept {
+        // does not do anything
+        return *this;
+    }
+
+    ~BulkPoolAllocator() noexcept {
+        reset();
+    }
+
+    // Deallocates all allocated memory.
+    void reset() noexcept {
+        while (mListForFree) {
+            T* tmp = *mListForFree;
+            free(mListForFree);
+            mListForFree = reinterpret_cast_no_cast_align_warning<T**>(tmp);
+        }
+        mHead = nullptr;
+    }
+
+    // allocates, but does NOT initialize. Use in-place new constructor, e.g.
+    //   T* obj = pool.allocate();
+    //   ::new (static_cast<void*>(obj)) T();
+    T* allocate() {
+        T* tmp = mHead;
+        if (!tmp) {
+            tmp = performAllocation();
+        }
+
+        mHead = *reinterpret_cast_no_cast_align_warning<T**>(tmp);
+        return tmp;
+    }
+
+    // does not actually deallocate but puts it in store.
+    // make sure you have already called the destructor! e.g. with
+    //  obj->~T();
+    //  pool.deallocate(obj);
+    void deallocate(T* obj) noexcept {
+        *reinterpret_cast_no_cast_align_warning<T**>(obj) = mHead;
+        mHead = obj;
+    }
+
+    // Adds an already allocated block of memory to the allocator. This allocator is from now on
+    // responsible for freeing the data (with free()). If the provided data is not large enough to
+    // make use of, it is immediately freed. Otherwise it is reused and freed in the destructor.
+    void addOrFree(void* ptr, const size_t numBytes) noexcept {
+        // calculate number of available elements in ptr
+        if (numBytes < ALIGNMENT + ALIGNED_SIZE) {
+            // not enough data for at least one element. Free and return.
+            free(ptr);
+        } else {
+            add(ptr, numBytes);
+        }
+    }
+
+    void swap(BulkPoolAllocator<T, MinNumAllocs, MaxNumAllocs>& other) noexcept {
+        using std::swap;
+        swap(mHead, other.mHead);
+        swap(mListForFree, other.mListForFree);
+    }
+
+private:
+    // iterates the list of allocated memory to calculate how many to alloc next.
+    // Recalculating this each time saves us a size_t member.
+    // This ignores the fact that memory blocks might have been added manually with addOrFree. In
+    // practice, this should not matter much.
+    ROBIN_HOOD(NODISCARD) size_t calcNumElementsToAlloc() const noexcept {
+        auto tmp = mListForFree;
+        size_t numAllocs = MinNumAllocs;
+
+        while (numAllocs * 2 <= MaxNumAllocs && tmp) {
+            auto x = reinterpret_cast<T***>(tmp);
+            tmp = *x;
+            numAllocs *= 2;
+        }
+
+        return numAllocs;
+    }
+
+    // WARNING: Underflow if numBytes < ALIGNMENT! This is guarded in addOrFree().
+    void add(void* ptr, const size_t numBytes) noexcept {
+        const size_t numElements = (numBytes - ALIGNMENT) / ALIGNED_SIZE;
+
+        auto data = reinterpret_cast<T**>(ptr);
+
+        // link free list
+        auto x = reinterpret_cast<T***>(data);
+        *x = mListForFree;
+        mListForFree = data;
+
+        // create linked list for newly allocated data
+        auto const headT =
+            reinterpret_cast_no_cast_align_warning<T*>(reinterpret_cast<char*>(ptr) + ALIGNMENT);
+
+        auto const head = reinterpret_cast<char*>(headT);
+
+        // Visual Studio compiler automatically unrolls this loop, which is pretty cool
+        for (size_t i = 0; i < numElements; ++i) {
+            *reinterpret_cast_no_cast_align_warning<char**>(head + i * ALIGNED_SIZE) =
+                head + (i + 1) * ALIGNED_SIZE;
+        }
+
+        // last one points to 0
+        *reinterpret_cast_no_cast_align_warning<T**>(head + (numElements - 1) * ALIGNED_SIZE) =
+            mHead;
+        mHead = headT;
+    }
+
+    // Called when no memory is available (mHead == 0).
+    // Don't inline this slow path.
+    ROBIN_HOOD(NOINLINE) T* performAllocation() {
+        size_t const numElementsToAlloc = calcNumElementsToAlloc();
+
+        // alloc new memory: [prev |T, T, ... T]
+        // std::cout << (sizeof(T*) + ALIGNED_SIZE * numElementsToAlloc) << " bytes" << std::endl;
+        size_t const bytes = ALIGNMENT + ALIGNED_SIZE * numElementsToAlloc;
+        add(assertNotNull<std::bad_alloc>(malloc(bytes)), bytes);
+        return mHead;
+    }
+
+    // enforce byte alignment of the T's
+#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14)
+    static constexpr size_t ALIGNMENT =
+        (std::max)(std::alignment_of<T>::value, std::alignment_of<T*>::value);
+#else
+    static const size_t ALIGNMENT =
+        (ROBIN_HOOD_STD::alignment_of<T>::value > ROBIN_HOOD_STD::alignment_of<T*>::value)
+            ? ROBIN_HOOD_STD::alignment_of<T>::value
+            : +ROBIN_HOOD_STD::alignment_of<T*>::value; // the + is for walkarround
+#endif
+
+    static constexpr size_t ALIGNED_SIZE = ((sizeof(T) - 1) / ALIGNMENT + 1) * ALIGNMENT;
+
+    static_assert(MinNumAllocs >= 1, "MinNumAllocs");
+    static_assert(MaxNumAllocs >= MinNumAllocs, "MaxNumAllocs");
+    static_assert(ALIGNED_SIZE >= sizeof(T*), "ALIGNED_SIZE");
+    static_assert(0 == (ALIGNED_SIZE % sizeof(T*)), "ALIGNED_SIZE mod");
+    static_assert(ALIGNMENT >= sizeof(T*), "ALIGNMENT");
+
+    T* mHead{nullptr};
+    T** mListForFree{nullptr};
+};
+
+template <typename T, size_t MinSize, size_t MaxSize, bool IsFlatMap>
+struct NodeAllocator;
+
+// dummy allocator that does nothing
+template <typename T, size_t MinSize, size_t MaxSize>
+struct NodeAllocator<T, MinSize, MaxSize, true> {
+
+    // we are not using the data, so just free it.
+    void addOrFree(void* ptr, size_t ROBIN_HOOD_UNUSED(numBytes) /*unused*/) noexcept {
+        free(ptr);
+    }
+};
+
+template <typename T, size_t MinSize, size_t MaxSize>
+struct NodeAllocator<T, MinSize, MaxSize, false> : public BulkPoolAllocator<T, MinSize, MaxSize> {};
+
+// dummy hash, unsed as mixer when robin_hood::hash is already used
+template <typename T>
+struct identity_hash {
+    constexpr size_t operator()(T const& obj) const noexcept {
+        return static_cast<size_t>(obj);
+    }
+};
+
+// c++14 doesn't have is_nothrow_swappable, and clang++ 6.0.1 doesn't like it either, so I'm making
+// my own here.
+namespace swappable {
+using std::swap;
+template <typename T>
+struct nothrow {
+    static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>()));
+};
+
+} // namespace swappable
+
+} // namespace detail
+
+struct is_transparent_tag {};
+
+// A custom pair implementation is used in the map because std::pair is not is_trivially_copyable,
+// which means it would  not be allowed to be used in std::memcpy. This struct is copyable, which is
+// also tested.
+template <typename T1, typename T2>
+struct pair {
+    using first_type = T1;
+    using second_type = T2;
+
+    template <typename U1 = T1, typename U2 = T2,
+              typename = typename std::enable_if<std::is_default_constructible<U1>::value &&
+                                                 std::is_default_constructible<U2>::value>::type>
+    constexpr pair() noexcept(noexcept(U1()) && noexcept(U2()))
+        : first()
+        , second() {}
+
+    // pair constructors are explicit so we don't accidentally call this ctor when we don't have to.
+    explicit constexpr pair(std::pair<T1, T2> const& o) noexcept(
+        noexcept(T1(std::declval<T1 const&>())) && noexcept(T2(std::declval<T2 const&>())))
+        : first(o.first)
+        , second(o.second) {}
+
+    // pair constructors are explicit so we don't accidentally call this ctor when we don't have to.
+    explicit constexpr pair(std::pair<T1, T2>&& o) noexcept(
+        noexcept(T1(std::move(std::declval<T1&&>()))) &&
+        noexcept(T2(std::move(std::declval<T2&&>()))))
+        : first(std::move(o.first))
+        , second(std::move(o.second)) {}
+
+    constexpr pair(T1&& a, T2&& b) noexcept(noexcept(T1(std::move(std::declval<T1&&>()))) &&
+                                            noexcept(T2(std::move(std::declval<T2&&>()))))
+        : first(std::move(a))
+        , second(std::move(b)) {}
+
+    template <typename U1, typename U2>
+    constexpr pair(U1&& a, U2&& b) noexcept(noexcept(T1(std::forward<U1>(std::declval<U1&&>()))) &&
+                                            noexcept(T2(std::forward<U2>(std::declval<U2&&>()))))
+        : first(std::forward<U1>(a))
+        , second(std::forward<U2>(b)) {}
+
+    template <typename... U1, typename... U2>
+    constexpr pair(
+        std::piecewise_construct_t /*unused*/, std::tuple<U1...> a,
+        std::tuple<U2...> b) noexcept(noexcept(pair(std::declval<std::tuple<U1...>&>(),
+                                                    std::declval<std::tuple<U2...>&>(),
+                                                    ROBIN_HOOD_STD::index_sequence_for<U1...>(),
+                                                    ROBIN_HOOD_STD::index_sequence_for<U2...>())))
+        : pair(a, b, ROBIN_HOOD_STD::index_sequence_for<U1...>(),
+               ROBIN_HOOD_STD::index_sequence_for<U2...>()) {}
+
+    // constructor called from the std::piecewise_construct_t ctor
+    template <typename... U1, size_t... I1, typename... U2, size_t... I2>
+    pair(std::tuple<U1...>& a, std::tuple<U2...>& b,
+         ROBIN_HOOD_STD::index_sequence<I1...> /*unused*/,
+         ROBIN_HOOD_STD::index_sequence<
+             I2...> /*unused*/) noexcept(noexcept(T1(std::
+                                                         forward<U1>(std::get<I1>(
+                                                             std::declval<
+                                                                 std::tuple<U1...>&>()))...)) &&
+                                         noexcept(T2(std::forward<U2>(
+                                             std::get<I2>(std::declval<std::tuple<U2...>&>()))...)))
+        : first(std::forward<U1>(std::get<I1>(a))...)
+        , second(std::forward<U2>(std::get<I2>(b))...) {
+        // make visual studio compiler happy about warning about unused a & b.
+        // Visual studio's pair implementation disables warning 4100.
+        (void)a;
+        (void)b;
+    }
+
+    ROBIN_HOOD(NODISCARD) first_type& getFirst() noexcept {
+        return first;
+    }
+    ROBIN_HOOD(NODISCARD) first_type const& getFirst() const noexcept {
+        return first;
+    }
+    ROBIN_HOOD(NODISCARD) second_type& getSecond() noexcept {
+        return second;
+    }
+    ROBIN_HOOD(NODISCARD) second_type const& getSecond() const noexcept {
+        return second;
+    }
+
+    void swap(pair<T1, T2>& o) noexcept((detail::swappable::nothrow<T1>::value) &&
+                                        (detail::swappable::nothrow<T2>::value)) {
+        using std::swap;
+        swap(first, o.first);
+        swap(second, o.second);
+    }
+
+    T1 first;  // NOLINT(misc-non-private-member-variables-in-classes)
+    T2 second; // NOLINT(misc-non-private-member-variables-in-classes)
+};
+
+template <typename A, typename B>
+void swap(pair<A, B>& a, pair<A, B>& b) noexcept(
+    noexcept(std::declval<pair<A, B>&>().swap(std::declval<pair<A, B>&>()))) {
+    a.swap(b);
+}
+
+// Hash an arbitrary amount of bytes. This is basically Murmur2 hash without caring about big
+// endianness. TODO(martinus) add a fallback for very large strings?
+static size_t hash_bytes(void const* ptr, size_t const len) noexcept {
+    static constexpr uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
+    static constexpr uint64_t seed = UINT64_C(0xe17a1465);
+    static constexpr unsigned int r = 47;
+
+    auto const data64 = static_cast<uint64_t const*>(ptr);
+    uint64_t h = seed ^ (len * m);
+
+    size_t const n_blocks = len / 8;
+    for (size_t i = 0; i < n_blocks; ++i) {
+        auto k = detail::unaligned_load<uint64_t>(data64 + i);
+
+        k *= m;
+        k ^= k >> r;
+        k *= m;
+
+        h ^= k;
+        h *= m;
+    }
+
+    auto const data8 = reinterpret_cast<uint8_t const*>(data64 + n_blocks);
+    switch (len & 7U) {
+    case 7:
+        h ^= static_cast<uint64_t>(data8[6]) << 48U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 6:
+        h ^= static_cast<uint64_t>(data8[5]) << 40U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 5:
+        h ^= static_cast<uint64_t>(data8[4]) << 32U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 4:
+        h ^= static_cast<uint64_t>(data8[3]) << 24U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 3:
+        h ^= static_cast<uint64_t>(data8[2]) << 16U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 2:
+        h ^= static_cast<uint64_t>(data8[1]) << 8U;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    case 1:
+        h ^= static_cast<uint64_t>(data8[0]);
+        h *= m;
+        ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH
+    default:
+        break;
+    }
+
+    h ^= h >> r;
+    h *= m;
+    h ^= h >> r;
+    return static_cast<size_t>(h);
+}
+
+inline size_t hash_int(uint64_t obj) noexcept {
+#if ROBIN_HOOD(HAS_UMUL128)
+    // 167079903232 masksum, 120428523 ops best: 0xde5fb9d2630458e9
+    static constexpr uint64_t k = UINT64_C(0xde5fb9d2630458e9);
+    uint64_t h;
+    uint64_t l = detail::umul128(obj, k, &h);
+    return h + l;
+#elif ROBIN_HOOD(BITNESS) == 32
+    uint64_t const r = obj * UINT64_C(0xca4bcaa75ec3f625);
+    auto h = static_cast<uint32_t>(r >> 32U);
+    auto l = static_cast<uint32_t>(r);
+    return h + l;
+#else
+    // murmurhash 3 finalizer
+    uint64_t h = obj;
+    h ^= h >> 33;
+    h *= 0xff51afd7ed558ccd;
+    h ^= h >> 33;
+    h *= 0xc4ceb9fe1a85ec53;
+    h ^= h >> 33;
+    return static_cast<size_t>(h);
+#endif
+}
+
+// A thin wrapper around std::hash, performing an additional simple mixing step of the result.
+template <typename T>
+struct hash : public std::hash<T> {
+    size_t operator()(T const& obj) const
+        noexcept(noexcept(std::declval<std::hash<T>>().operator()(std::declval<T const&>()))) {
+        // call base hash
+        auto result = std::hash<T>::operator()(obj);
+        // return mixed of that, to be save against identity has
+        return hash_int(static_cast<uint64_t>(result));
+    }
+};
+
+template <>
+struct hash<std::string> {
+    size_t operator()(std::string const& str) const noexcept {
+        return hash_bytes(str.data(), str.size());
+    }
+};
+
+template <class T>
+struct hash<T*> {
+    size_t operator()(T* ptr) const noexcept {
+        return hash_int(reinterpret_cast<size_t>(ptr));
+    }
+};
+
+#define ROBIN_HOOD_HASH_INT(T)                           \
+    template <>                                          \
+    struct hash<T> {                                     \
+        size_t operator()(T obj) const noexcept {        \
+            return hash_int(static_cast<uint64_t>(obj)); \
+        }                                                \
+    }
+
+#if defined(__GNUC__) && !defined(__clang__)
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wuseless-cast"
+#endif
+// see https://en.cppreference.com/w/cpp/utility/hash
+ROBIN_HOOD_HASH_INT(bool);
+ROBIN_HOOD_HASH_INT(char);
+ROBIN_HOOD_HASH_INT(signed char);
+ROBIN_HOOD_HASH_INT(unsigned char);
+ROBIN_HOOD_HASH_INT(char16_t);
+ROBIN_HOOD_HASH_INT(char32_t);
+ROBIN_HOOD_HASH_INT(wchar_t);
+ROBIN_HOOD_HASH_INT(short);
+ROBIN_HOOD_HASH_INT(unsigned short);
+ROBIN_HOOD_HASH_INT(int);
+ROBIN_HOOD_HASH_INT(unsigned int);
+ROBIN_HOOD_HASH_INT(long);
+ROBIN_HOOD_HASH_INT(long long);
+ROBIN_HOOD_HASH_INT(unsigned long);
+ROBIN_HOOD_HASH_INT(unsigned long long);
+#if defined(__GNUC__) && !defined(__clang__)
+#    pragma GCC diagnostic pop
+#endif
+namespace detail {
+
+// using wrapper classes for hash and key_equal prevents the diamond problem when the same type is
+// used. see https://stackoverflow.com/a/28771920/48181
+template <typename T>
+struct WrapHash : public T {
+    WrapHash() = default;
+    explicit WrapHash(T const& o) noexcept(noexcept(T(std::declval<T const&>())))
+        : T(o) {}
+};
+
+template <typename T>
+struct WrapKeyEqual : public T {
+    WrapKeyEqual() = default;
+    explicit WrapKeyEqual(T const& o) noexcept(noexcept(T(std::declval<T const&>())))
+        : T(o) {}
+};
+
+// A highly optimized hashmap implementation, using the Robin Hood algorithm.
+//
+// In most cases, this map should be usable as a drop-in replacement for std::unordered_map, but be
+// about 2x faster in most cases and require much less allocations.
+//
+// This implementation uses the following memory layout:
+//
+// [Node, Node, ... Node | info, info, ... infoSentinel ]
+//
+// * Node: either a DataNode that directly has the std::pair<key, val> as member,
+//   or a DataNode with a pointer to std::pair<key,val>. Which DataNode representation to use
+//   depends on how fast the swap() operation is. Heuristically, this is automatically choosen based
+//   on sizeof(). there are always 2^n Nodes.
+//
+// * info: Each Node in the map has a corresponding info byte, so there are 2^n info bytes.
+//   Each byte is initialized to 0, meaning the corresponding Node is empty. Set to 1 means the
+//   corresponding node contains data. Set to 2 means the corresponding Node is filled, but it
+//   actually belongs to the previous position and was pushed out because that place is already
+//   taken.
+//
+// * infoSentinel: Sentinel byte set to 1, so that iterator's ++ can stop at end() without the need
+// for a idx
+//   variable.
+//
+// According to STL, order of templates has effect on throughput. That's why I've moved the boolean
+// to the front.
+// https://www.reddit.com/r/cpp/comments/ahp6iu/compile_time_binary_size_reductions_and_cs_future/eeguck4/
+template <bool IsFlatMap, size_t MaxLoadFactor100, typename Key, typename T, typename Hash,
+          typename KeyEqual>
+class unordered_map
+    : public WrapHash<Hash>,
+      public WrapKeyEqual<KeyEqual>,
+      detail::NodeAllocator<
+          robin_hood::pair<typename std::conditional<IsFlatMap, Key, Key const>::type, T>, 4, 16384,
+          IsFlatMap> {
+public:
+    using key_type = Key;
+    using mapped_type = T;
+    using value_type =
+        robin_hood::pair<typename std::conditional<IsFlatMap, Key, Key const>::type, T>;
+    using size_type = size_t;
+    using hasher = Hash;
+    using key_equal = KeyEqual;
+    using Self =
+        unordered_map<IsFlatMap, MaxLoadFactor100, key_type, mapped_type, hasher, key_equal>;
+    static constexpr bool is_flat_map = IsFlatMap;
+
+private:
+    static_assert(MaxLoadFactor100 > 10 && MaxLoadFactor100 < 100,
+                  "MaxLoadFactor100 needs to be >10 && < 100");
+
+    using WHash = WrapHash<Hash>;
+    using WKeyEqual = WrapKeyEqual<KeyEqual>;
+
+    // configuration defaults
+
+    // make sure we have 8 elements, needed to quickly rehash mInfo
+    static constexpr size_t InitialNumElements = sizeof(uint64_t);
+    static constexpr uint32_t InitialInfoNumBits = 5;
+    static constexpr uint8_t InitialInfoInc = 1U << InitialInfoNumBits;
+    static constexpr uint8_t InitialInfoHashShift = sizeof(size_t) * 8 - InitialInfoNumBits;
+    using DataPool = detail::NodeAllocator<value_type, 4, 16384, IsFlatMap>;
+
+    // type needs to be wider than uint8_t.
+    using InfoType = uint32_t;
+
+    // DataNode ////////////////////////////////////////////////////////
+
+    // Primary template for the data node. We have special implementations for small and big
+    // objects. For large objects it is assumed that swap() is fairly slow, so we allocate these on
+    // the heap so swap merely swaps a pointer.
+    template <typename M, bool>
+    class DataNode {};
+
+    // Small: just allocate on the stack.
+    template <typename M>
+    class DataNode<M, true> final {
+    public:
+        template <typename... Args>
+        explicit DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, Args&&... args) noexcept(
+            noexcept(value_type(std::forward<Args>(args)...)))
+            : mData(std::forward<Args>(args)...) {}
+
+        DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, true>&& n) noexcept(
+            std::is_nothrow_move_constructible<value_type>::value)
+            : mData(std::move(n.mData)) {}
+
+        // doesn't do anything
+        void destroy(M& ROBIN_HOOD_UNUSED(map) /*unused*/) noexcept {}
+        void destroyDoNotDeallocate() noexcept {}
+
+        value_type const* operator->() const noexcept {
+            return &mData;
+        }
+        value_type* operator->() noexcept {
+            return &mData;
+        }
+
+        const value_type& operator*() const noexcept {
+            return mData;
+        }
+
+        value_type& operator*() noexcept {
+            return mData;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::first_type& getFirst() noexcept {
+            return mData.first;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::first_type const& getFirst() const noexcept {
+            return mData.first;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::second_type& getSecond() noexcept {
+            return mData.second;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::second_type const& getSecond() const noexcept {
+            return mData.second;
+        }
+
+        void swap(DataNode<M, true>& o) noexcept(
+            noexcept(std::declval<value_type>().swap(std::declval<value_type>()))) {
+            mData.swap(o.mData);
+        }
+
+    private:
+        value_type mData;
+    };
+
+    // big object: allocate on heap.
+    template <typename M>
+    class DataNode<M, false> {
+    public:
+        template <typename... Args>
+        explicit DataNode(M& map, Args&&... args)
+            : mData(map.allocate()) {
+            ::new (static_cast<void*>(mData)) value_type(std::forward<Args>(args)...);
+        }
+
+        DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, false>&& n) noexcept
+            : mData(std::move(n.mData)) {}
+
+        void destroy(M& map) noexcept {
+            // don't deallocate, just put it into list of datapool.
+            mData->~value_type();
+            map.deallocate(mData);
+        }
+
+        void destroyDoNotDeallocate() noexcept {
+            mData->~value_type();
+        }
+
+        value_type const* operator->() const noexcept {
+            return mData;
+        }
+
+        value_type* operator->() noexcept {
+            return mData;
+        }
+
+        const value_type& operator*() const {
+            return *mData;
+        }
+
+        value_type& operator*() {
+            return *mData;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::first_type& getFirst() {
+            return mData->first;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::first_type const& getFirst() const {
+            return mData->first;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::second_type& getSecond() {
+            return mData->second;
+        }
+
+        ROBIN_HOOD(NODISCARD) typename value_type::second_type const& getSecond() const {
+            return mData->second;
+        }
+
+        void swap(DataNode<M, false>& o) noexcept {
+            using std::swap;
+            swap(mData, o.mData);
+        }
+
+    private:
+        value_type* mData;
+    };
+
+    using Node = DataNode<Self, IsFlatMap>;
+
+    // Cloner //////////////////////////////////////////////////////////
+
+    template <typename M, bool UseMemcpy>
+    struct Cloner;
+
+    // fast path: Just copy data, without allocating anything.
+    template <typename M>
+    struct Cloner<M, true> {
+        void operator()(M const& source, M& target) const {
+            // std::memcpy(target.mKeyVals, source.mKeyVals,
+            //             target.calcNumBytesTotal(target.mMask + 1));
+            auto src = reinterpret_cast<char const*>(source.mKeyVals);
+            auto tgt = reinterpret_cast<char*>(target.mKeyVals);
+            std::copy(src, src + target.calcNumBytesTotal(target.mMask + 1), tgt);
+        }
+    };
+
+    template <typename M>
+    struct Cloner<M, false> {
+        void operator()(M const& s, M& t) const {
+            std::copy(s.mInfo, s.mInfo + t.calcNumBytesInfo(t.mMask + 1), t.mInfo);
+
+            for (size_t i = 0; i < t.mMask + 1; ++i) {
+                if (t.mInfo[i]) {
+                    ::new (static_cast<void*>(t.mKeyVals + i)) Node(t, *s.mKeyVals[i]);
+                }
+            }
+        }
+    };
+
+    // Destroyer ///////////////////////////////////////////////////////
+
+    template <typename M, bool IsFlatMapAndTrivial>
+    struct Destroyer {};
+
+    template <typename M>
+    struct Destroyer<M, true> {
+        void nodes(M& m) const noexcept {
+            m.mNumElements = 0;
+        }
+
+        void nodesDoNotDeallocate(M& m) const noexcept {
+            m.mNumElements = 0;
+        }
+    };
+
+    template <typename M>
+    struct Destroyer<M, false> {
+        void nodes(M& m) const noexcept {
+            m.mNumElements = 0;
+            // clear also resets mInfo to 0, that's sometimes not necessary.
+            for (size_t idx = 0; idx <= m.mMask; ++idx) {
+                if (0 != m.mInfo[idx]) {
+                    Node& n = m.mKeyVals[idx];
+                    n.destroy(m);
+                    n.~Node();
+                }
+            }
+        }
+
+        void nodesDoNotDeallocate(M& m) const noexcept {
+            m.mNumElements = 0;
+            // clear also resets mInfo to 0, that's sometimes not necessary.
+            for (size_t idx = 0; idx <= m.mMask; ++idx) {
+                if (0 != m.mInfo[idx]) {
+                    Node& n = m.mKeyVals[idx];
+                    n.destroyDoNotDeallocate();
+                    n.~Node();
+                }
+            }
+        }
+    };
+
+    // Iter ////////////////////////////////////////////////////////////
+
+    struct fast_forward_tag {};
+
+    // generic iterator for both const_iterator and iterator.
+    template <bool IsConst>
+    // NOLINTNEXTLINE(hicpp-special-member-functions,cppcoreguidelines-special-member-functions)
+    class Iter {
+    private:
+        using NodePtr = typename std::conditional<IsConst, Node const*, Node*>::type;
+
+    public:
+        using difference_type = std::ptrdiff_t;
+        using value_type = typename Self::value_type;
+        using reference = typename std::conditional<IsConst, value_type const&, value_type&>::type;
+        using pointer = typename std::conditional<IsConst, value_type const*, value_type*>::type;
+        using iterator_category = std::forward_iterator_tag;
+
+        // default constructed iterator can be compared to itself, but WON'T return true when
+        // compared to end().
+        Iter() = default;
+
+        // Rule of zero: nothing specified. The conversion constructor is only enabled for iterator
+        // to const_iterator, so it doesn't accidentally work as a copy ctor.
+
+        // Conversion constructor from iterator to const_iterator.
+        template <bool OtherIsConst,
+                  typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
+        // NOLINTNEXTLINE(hicpp-explicit-conversions)
+        Iter(Iter<OtherIsConst> const& other) noexcept
+            : mKeyVals(other.mKeyVals)
+            , mInfo(other.mInfo) {}
+
+        Iter(NodePtr valPtr, uint8_t const* infoPtr) noexcept
+            : mKeyVals(valPtr)
+            , mInfo(infoPtr) {}
+
+        Iter(NodePtr valPtr, uint8_t const* infoPtr,
+             fast_forward_tag ROBIN_HOOD_UNUSED(tag) /*unused*/) noexcept
+            : mKeyVals(valPtr)
+            , mInfo(infoPtr) {
+            fastForward();
+        }
+
+        template <bool OtherIsConst,
+                  typename = typename std::enable_if<IsConst && !OtherIsConst>::type>
+        Iter& operator=(Iter<OtherIsConst> const& other) noexcept {
+            mKeyVals = other.mKeyVals;
+            mInfo = other.mInfo;
+            return *this;
+        }
+
+        // prefix increment. Undefined behavior if we are at end()!
+        Iter& operator++() noexcept {
+            mInfo++;
+            mKeyVals++;
+            fastForward();
+            return *this;
+        }
+
+        reference operator*() const {
+            return **mKeyVals;
+        }
+
+        pointer operator->() const {
+            return &**mKeyVals;
+        }
+
+        template <bool O>
+        bool operator==(Iter<O> const& o) const noexcept {
+            return mKeyVals == o.mKeyVals;
+        }
+
+        template <bool O>
+        bool operator!=(Iter<O> const& o) const noexcept {
+            return mKeyVals != o.mKeyVals;
+        }
+
+    private:
+        // fast forward to the next non-free info byte
+        void fastForward() noexcept {
+            int inc;
+            do {
+                auto const n = detail::unaligned_load<size_t>(mInfo);
+#if ROBIN_HOOD(LITTLE_ENDIAN)
+                inc = ROBIN_HOOD_COUNT_TRAILING_ZEROES(n) / 8;
+#else
+                inc = ROBIN_HOOD_COUNT_LEADING_ZEROES(n) / 8;
+#endif
+                mInfo += inc;
+                mKeyVals += inc;
+            } while (inc == static_cast<int>(sizeof(size_t)));
+        }
+
+        friend class unordered_map<IsFlatMap, MaxLoadFactor100, key_type, mapped_type, hasher,
+                                   key_equal>;
+        NodePtr mKeyVals{nullptr};
+        uint8_t const* mInfo{nullptr};
+    };
+
+    ////////////////////////////////////////////////////////////////////
+
+    // highly performance relevant code.
+    // Lower bits are used for indexing into the array (2^n size)
+    // The upper 1-5 bits need to be a reasonable good hash, to save comparisons.
+    template <typename HashKey>
+    void keyToIdx(HashKey&& key, size_t* idx, InfoType* info) const {
+        // for a user-specified hash that is *not* robin_hood::hash, apply robin_hood::hash as an
+        // additional mixing step. This serves as a bad hash prevention, if the given data is badly
+        // mixed.
+        using Mix =
+            typename std::conditional<std::is_same<::robin_hood::hash<key_type>, hasher>::value,
+                                      ::robin_hood::detail::identity_hash<size_t>,
+                                      ::robin_hood::hash<size_t>>::type;
+        *idx = Mix{}(WHash::operator()(key));
+
+        *info = mInfoInc + static_cast<InfoType>(*idx >> mInfoHashShift);
+        *idx &= mMask;
+    }
+
+    // forwards the index by one, wrapping around at the end
+    void next(InfoType* info, size_t* idx) const noexcept {
+        *idx = (*idx + 1) & mMask;
+        *info += mInfoInc;
+    }
+
+    void nextWhileLess(InfoType* info, size_t* idx) const noexcept {
+        // unrolling this by hand did not bring any speedups.
+        while (*info < mInfo[*idx]) {
+            next(info, idx);
+        }
+    }
+
+    // Shift everything up by one element. Tries to move stuff around.
+    // True if some shifting has occured (entry under idx is a constructed object)
+    // Fals if no shift has occured (entry under idx is unconstructed memory)
+    void
+    shiftUp(size_t idx,
+            size_t const insertion_idx) noexcept(std::is_nothrow_move_assignable<Node>::value) {
+        while (idx != insertion_idx) {
+            size_t prev_idx = (idx - 1) & mMask;
+            if (mInfo[idx]) {
+                mKeyVals[idx] = std::move(mKeyVals[prev_idx]);
+            } else {
+                ::new (static_cast<void*>(mKeyVals + idx)) Node(std::move(mKeyVals[prev_idx]));
+            }
+            mInfo[idx] = static_cast<uint8_t>(mInfo[prev_idx] + mInfoInc);
+            if (ROBIN_HOOD_UNLIKELY(mInfo[idx] + mInfoInc > 0xFF)) {
+                mMaxNumElementsAllowed = 0;
+            }
+            idx = prev_idx;
+        }
+    }
+
+    void shiftDown(size_t idx) noexcept(std::is_nothrow_move_assignable<Node>::value) {
+        // until we find one that is either empty or has zero offset.
+        // TODO(martinus) we don't need to move everything, just the last one for the same bucket.
+        mKeyVals[idx].destroy(*this);
+
+        // until we find one that is either empty or has zero offset.
+        size_t nextIdx = (idx + 1) & mMask;
+        while (mInfo[nextIdx] >= 2 * mInfoInc) {
+            mInfo[idx] = static_cast<uint8_t>(mInfo[nextIdx] - mInfoInc);
+            mKeyVals[idx] = std::move(mKeyVals[nextIdx]);
+            idx = nextIdx;
+            nextIdx = (idx + 1) & mMask;
+        }
+
+        mInfo[idx] = 0;
+        // don't destroy, we've moved it
+        // mKeyVals[idx].destroy(*this);
+        mKeyVals[idx].~Node();
+    }
+
+    // copy of find(), except that it returns iterator instead of const_iterator.
+    template <typename Other>
+    ROBIN_HOOD(NODISCARD)
+    size_t findIdx(Other const& key) const {
+        size_t idx;
+        InfoType info;
+        keyToIdx(key, &idx, &info);
+
+        do {
+            // unrolling this twice gives a bit of a speedup. More unrolling did not help.
+            if (info == mInfo[idx] && WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+                return idx;
+            }
+            next(&info, &idx);
+            if (info == mInfo[idx] && WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+                return idx;
+            }
+            next(&info, &idx);
+        } while (info <= mInfo[idx]);
+
+        // nothing found!
+        return mMask == 0 ? 0 : mMask + 1;
+    }
+
+    void cloneData(const unordered_map& o) {
+        Cloner<unordered_map, IsFlatMap && ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(Node)>()(o, *this);
+    }
+
+    // inserts a keyval that is guaranteed to be new, e.g. when the hashmap is resized.
+    // @return index where the element was created
+    size_t insert_move(Node&& keyval) {
+        // we don't retry, fail if overflowing
+        // don't need to check max num elements
+        if (0 == mMaxNumElementsAllowed && !try_increase_info()) {
+            throwOverflowError();
+        }
+
+        size_t idx;
+        InfoType info;
+        keyToIdx(keyval.getFirst(), &idx, &info);
+
+        // skip forward. Use <= because we are certain that the element is not there.
+        while (info <= mInfo[idx]) {
+            idx = (idx + 1) & mMask;
+            info += mInfoInc;
+        }
+
+        // key not found, so we are now exactly where we want to insert it.
+        auto const insertion_idx = idx;
+        auto const insertion_info = static_cast<uint8_t>(info);
+        if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) {
+            mMaxNumElementsAllowed = 0;
+        }
+
+        // find an empty spot
+        while (0 != mInfo[idx]) {
+            next(&info, &idx);
+        }
+
+        auto& l = mKeyVals[insertion_idx];
+        if (idx == insertion_idx) {
+            ::new (static_cast<void*>(&l)) Node(std::move(keyval));
+        } else {
+            shiftUp(idx, insertion_idx);
+            l = std::move(keyval);
+        }
+
+        // put at empty spot
+        mInfo[insertion_idx] = insertion_info;
+
+        ++mNumElements;
+        return insertion_idx;
+    }
+
+public:
+    using iterator = Iter<false>;
+    using const_iterator = Iter<true>;
+
+    // Creates an empty hash map. Nothing is allocated yet, this happens at the first insert. This
+    // tremendously speeds up ctor & dtor of a map that never receives an element. The penalty is
+    // payed at the first insert, and not before. Lookup of this empty map works because everybody
+    // points to DummyInfoByte::b. parameter bucket_count is dictated by the standard, but we can
+    // ignore it.
+    explicit unordered_map(size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0,
+                           const Hash& h = Hash{},
+                           const KeyEqual& equal = KeyEqual{}) noexcept(noexcept(Hash(h)) &&
+                                                                        noexcept(KeyEqual(equal)))
+        : WHash(h)
+        , WKeyEqual(equal) {
+        ROBIN_HOOD_TRACE(this);
+    }
+
+    template <typename Iter>
+    unordered_map(Iter first, Iter last, size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0,
+                  const Hash& h = Hash{}, const KeyEqual& equal = KeyEqual{})
+        : WHash(h)
+        , WKeyEqual(equal) {
+        ROBIN_HOOD_TRACE(this);
+        insert(first, last);
+    }
+
+    unordered_map(std::initializer_list<value_type> initlist,
+                  size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0, const Hash& h = Hash{},
+                  const KeyEqual& equal = KeyEqual{})
+        : WHash(h)
+        , WKeyEqual(equal) {
+        ROBIN_HOOD_TRACE(this);
+        insert(initlist.begin(), initlist.end());
+    }
+
+    unordered_map(unordered_map&& o) noexcept
+        : WHash(std::move(static_cast<WHash&>(o)))
+        , WKeyEqual(std::move(static_cast<WKeyEqual&>(o)))
+        , DataPool(std::move(static_cast<DataPool&>(o))) {
+        ROBIN_HOOD_TRACE(this);
+        if (o.mMask) {
+            mKeyVals = std::move(o.mKeyVals);
+            mInfo = std::move(o.mInfo);
+            mNumElements = std::move(o.mNumElements);
+            mMask = std::move(o.mMask);
+            mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed);
+            mInfoInc = std::move(o.mInfoInc);
+            mInfoHashShift = std::move(o.mInfoHashShift);
+            // set other's mask to 0 so its destructor won't do anything
+            o.init();
+        }
+    }
+
+    unordered_map& operator=(unordered_map&& o) noexcept {
+        ROBIN_HOOD_TRACE(this);
+        if (&o != this) {
+            if (o.mMask) {
+                // only move stuff if the other map actually has some data
+                destroy();
+                mKeyVals = std::move(o.mKeyVals);
+                mInfo = std::move(o.mInfo);
+                mNumElements = std::move(o.mNumElements);
+                mMask = std::move(o.mMask);
+                mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed);
+                mInfoInc = std::move(o.mInfoInc);
+                mInfoHashShift = std::move(o.mInfoHashShift);
+                WHash::operator=(std::move(static_cast<WHash&>(o)));
+                WKeyEqual::operator=(std::move(static_cast<WKeyEqual&>(o)));
+                DataPool::operator=(std::move(static_cast<DataPool&>(o)));
+
+                o.init();
+
+            } else {
+                // nothing in the other map => just clear us.
+                clear();
+            }
+        }
+        return *this;
+    }
+
+    unordered_map(const unordered_map& o)
+        : WHash(static_cast<const WHash&>(o))
+        , WKeyEqual(static_cast<const WKeyEqual&>(o))
+        , DataPool(static_cast<const DataPool&>(o)) {
+        ROBIN_HOOD_TRACE(this);
+        if (!o.empty()) {
+            // not empty: create an exact copy. it is also possible to just iterate through all
+            // elements and insert them, but copying is probably faster.
+
+            mKeyVals = static_cast<Node*>(
+                detail::assertNotNull<std::bad_alloc>(malloc(calcNumBytesTotal(o.mMask + 1))));
+            // no need for calloc because clonData does memcpy
+            mInfo = reinterpret_cast<uint8_t*>(mKeyVals + o.mMask + 1);
+            mNumElements = o.mNumElements;
+            mMask = o.mMask;
+            mMaxNumElementsAllowed = o.mMaxNumElementsAllowed;
+            mInfoInc = o.mInfoInc;
+            mInfoHashShift = o.mInfoHashShift;
+            cloneData(o);
+        }
+    }
+
+    // Creates a copy of the given map. Copy constructor of each entry is used.
+    unordered_map& operator=(unordered_map const& o) {
+        ROBIN_HOOD_TRACE(this);
+        if (&o == this) {
+            // prevent assigning of itself
+            return *this;
+        }
+
+        // we keep using the old allocator and not assign the new one, because we want to keep the
+        // memory available. when it is the same size.
+        if (o.empty()) {
+            if (0 == mMask) {
+                // nothing to do, we are empty too
+                return *this;
+            }
+
+            // not empty: destroy what we have there
+            // clear also resets mInfo to 0, that's sometimes not necessary.
+            destroy();
+            init();
+            WHash::operator=(static_cast<const WHash&>(o));
+            WKeyEqual::operator=(static_cast<const WKeyEqual&>(o));
+            DataPool::operator=(static_cast<DataPool const&>(o));
+
+            return *this;
+        }
+
+        // clean up old stuff
+        Destroyer<Self, IsFlatMap && std::is_trivially_destructible<Node>::value>{}.nodes(*this);
+
+        if (mMask != o.mMask) {
+            // no luck: we don't have the same array size allocated, so we need to realloc.
+            if (0 != mMask) {
+                // only deallocate if we actually have data!
+                free(mKeyVals);
+            }
+
+            mKeyVals = static_cast<Node*>(
+                detail::assertNotNull<std::bad_alloc>(malloc(calcNumBytesTotal(o.mMask + 1))));
+
+            // no need for calloc here because cloneData performs a memcpy.
+            mInfo = reinterpret_cast<uint8_t*>(mKeyVals + o.mMask + 1);
+            // sentinel is set in cloneData
+        }
+        WHash::operator=(static_cast<const WHash&>(o));
+        WKeyEqual::operator=(static_cast<const WKeyEqual&>(o));
+        DataPool::operator=(static_cast<DataPool const&>(o));
+        mNumElements = o.mNumElements;
+        mMask = o.mMask;
+        mMaxNumElementsAllowed = o.mMaxNumElementsAllowed;
+        mInfoInc = o.mInfoInc;
+        mInfoHashShift = o.mInfoHashShift;
+        cloneData(o);
+
+        return *this;
+    }
+
+    // Swaps everything between the two maps.
+    void swap(unordered_map& o) {
+        ROBIN_HOOD_TRACE(this);
+        using std::swap;
+        swap(o, *this);
+    }
+
+    // Clears all data, without resizing.
+    void clear() {
+        ROBIN_HOOD_TRACE(this);
+        if (empty()) {
+            // don't do anything! also important because we don't want to write to DummyInfoByte::b,
+            // even though we would just write 0 to it.
+            return;
+        }
+
+        Destroyer<Self, IsFlatMap && std::is_trivially_destructible<Node>::value>{}.nodes(*this);
+
+        // clear everything except the sentinel
+        // std::memset(mInfo, 0, sizeof(uint8_t) * (mMask + 1));
+        uint8_t const z = 0;
+        std::fill(mInfo, mInfo + (sizeof(uint8_t) * (mMask + 1)), z);
+
+        mInfoInc = InitialInfoInc;
+        mInfoHashShift = InitialInfoHashShift;
+    }
+
+    // Destroys the map and all it's contents.
+    ~unordered_map() {
+        ROBIN_HOOD_TRACE(this);
+        destroy();
+    }
+
+    // Checks if both maps contain the same entries. Order is irrelevant.
+    bool operator==(const unordered_map& other) const {
+        ROBIN_HOOD_TRACE(this);
+        if (other.size() != size()) {
+            return false;
+        }
+        for (auto const& otherEntry : other) {
+            auto const myIt = find(otherEntry.first);
+            if (myIt == end() || !(myIt->second == otherEntry.second)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    bool operator!=(const unordered_map& other) const {
+        ROBIN_HOOD_TRACE(this);
+        return !operator==(other);
+    }
+
+    mapped_type& operator[](const key_type& key) {
+        ROBIN_HOOD_TRACE(this);
+        return doCreateByKey(key);
+    }
+
+    mapped_type& operator[](key_type&& key) {
+        ROBIN_HOOD_TRACE(this);
+        return doCreateByKey(std::move(key));
+    }
+
+    template <typename Iter>
+    void insert(Iter first, Iter last) {
+        for (; first != last; ++first) {
+            // value_type ctor needed because this might be called with std::pair's
+            insert(value_type(*first));
+        }
+    }
+
+    template <typename... Args>
+    std::pair<iterator, bool> emplace(Args&&... args) {
+        ROBIN_HOOD_TRACE(this);
+        Node n{*this, std::forward<Args>(args)...};
+        auto r = doInsert(std::move(n));
+        if (!r.second) {
+            // insertion not possible: destroy node
+            // NOLINTNEXTLINE(bugprone-use-after-move)
+            n.destroy(*this);
+        }
+        return r;
+    }
+
+    std::pair<iterator, bool> insert(const value_type& keyval) {
+        ROBIN_HOOD_TRACE(this);
+        return doInsert(keyval);
+    }
+
+    std::pair<iterator, bool> insert(value_type&& keyval) {
+        return doInsert(std::move(keyval));
+    }
+
+    // Returns 1 if key is found, 0 otherwise.
+    size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        auto kv = mKeyVals + findIdx(key);
+        if (kv != reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+            return 1;
+        }
+        return 0;
+    }
+
+    // Returns a reference to the value found for key.
+    // Throws std::out_of_range if element cannot be found
+    mapped_type& at(key_type const& key) {
+        ROBIN_HOOD_TRACE(this);
+        auto kv = mKeyVals + findIdx(key);
+        if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+            doThrow<std::out_of_range>("key not found");
+        }
+        return kv->getSecond();
+    }
+
+    // Returns a reference to the value found for key.
+    // Throws std::out_of_range if element cannot be found
+    mapped_type const& at(key_type const& key) const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        auto kv = mKeyVals + findIdx(key);
+        if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) {
+            doThrow<std::out_of_range>("key not found");
+        }
+        return kv->getSecond();
+    }
+
+    const_iterator find(const key_type& key) const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        const size_t idx = findIdx(key);
+        return const_iterator{mKeyVals + idx, mInfo + idx};
+    }
+
+    template <typename OtherKey>
+    const_iterator find(const OtherKey& key, is_transparent_tag /*unused*/) const {
+        ROBIN_HOOD_TRACE(this);
+        const size_t idx = findIdx(key);
+        return const_iterator{mKeyVals + idx, mInfo + idx};
+    }
+
+    iterator find(const key_type& key) {
+        ROBIN_HOOD_TRACE(this);
+        const size_t idx = findIdx(key);
+        return iterator{mKeyVals + idx, mInfo + idx};
+    }
+
+    template <typename OtherKey>
+    iterator find(const OtherKey& key, is_transparent_tag /*unused*/) {
+        ROBIN_HOOD_TRACE(this);
+        const size_t idx = findIdx(key);
+        return iterator{mKeyVals + idx, mInfo + idx};
+    }
+
+    iterator begin() {
+        ROBIN_HOOD_TRACE(this);
+        if (empty()) {
+            return end();
+        }
+        return iterator(mKeyVals, mInfo, fast_forward_tag{});
+    }
+    const_iterator begin() const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return cbegin();
+    }
+    const_iterator cbegin() const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        if (empty()) {
+            return cend();
+        }
+        return const_iterator(mKeyVals, mInfo, fast_forward_tag{});
+    }
+
+    iterator end() {
+        ROBIN_HOOD_TRACE(this);
+        // no need to supply valid info pointer: end() must not be dereferenced, and only node
+        // pointer is compared.
+        return iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr};
+    }
+    const_iterator end() const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return cend();
+    }
+    const_iterator cend() const { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return const_iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr};
+    }
+
+    iterator erase(const_iterator pos) {
+        ROBIN_HOOD_TRACE(this);
+        // its safe to perform const cast here
+        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+        return erase(iterator{const_cast<Node*>(pos.mKeyVals), const_cast<uint8_t*>(pos.mInfo)});
+    }
+
+    // Erases element at pos, returns iterator to the next element.
+    iterator erase(iterator pos) {
+        ROBIN_HOOD_TRACE(this);
+        // we assume that pos always points to a valid entry, and not end().
+        auto const idx = static_cast<size_t>(pos.mKeyVals - mKeyVals);
+
+        shiftDown(idx);
+        --mNumElements;
+
+        if (*pos.mInfo) {
+            // we've backward shifted, return this again
+            return pos;
+        }
+
+        // no backward shift, return next element
+        return ++pos;
+    }
+
+    size_t erase(const key_type& key) {
+        ROBIN_HOOD_TRACE(this);
+        size_t idx;
+        InfoType info;
+        keyToIdx(key, &idx, &info);
+
+        // check while info matches with the source idx
+        do {
+            if (info == mInfo[idx] && WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+                shiftDown(idx);
+                --mNumElements;
+                return 1;
+            }
+            next(&info, &idx);
+        } while (info <= mInfo[idx]);
+
+        // nothing found to delete
+        return 0;
+    }
+
+    // reserves space for the specified number of elements. Makes sure the old data fits.
+    // exactly the same as reserve(c).
+    void rehash(size_t c) {
+        reserve(c);
+    }
+
+    // reserves space for the specified number of elements. Makes sure the old data fits.
+    // Exactly the same as resize(c). Use resize(0) to shrink to fit.
+    void reserve(size_t c) {
+        ROBIN_HOOD_TRACE(this);
+        auto const minElementsAllowed = (std::max)(c, mNumElements);
+        auto newSize = InitialNumElements;
+        while (calcMaxNumElementsAllowed(newSize) < minElementsAllowed && newSize != 0) {
+            newSize *= 2;
+        }
+        if (ROBIN_HOOD_UNLIKELY(newSize == 0)) {
+            throwOverflowError();
+        }
+
+        rehashPowerOfTwo(newSize);
+    }
+
+    size_type size() const noexcept { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return mNumElements;
+    }
+
+    size_type max_size() const noexcept { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return static_cast<size_type>(-1);
+    }
+
+    ROBIN_HOOD(NODISCARD) bool empty() const noexcept {
+        ROBIN_HOOD_TRACE(this);
+        return 0 == mNumElements;
+    }
+
+    float max_load_factor() const noexcept { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return MaxLoadFactor100 / 100.0F;
+    }
+
+    // Average number of elements per bucket. Since we allow only 1 per bucket
+    float load_factor() const noexcept { // NOLINT(modernize-use-nodiscard)
+        ROBIN_HOOD_TRACE(this);
+        return static_cast<float>(size()) / static_cast<float>(mMask + 1);
+    }
+
+    ROBIN_HOOD(NODISCARD) size_t mask() const noexcept {
+        ROBIN_HOOD_TRACE(this);
+        return mMask;
+    }
+
+    ROBIN_HOOD(NODISCARD) size_t calcMaxNumElementsAllowed(size_t maxElements) const noexcept {
+        if (ROBIN_HOOD_LIKELY(maxElements <= (std::numeric_limits<size_t>::max)() / 100)) {
+            return maxElements * MaxLoadFactor100 / 100;
+        }
+
+        // we might be a bit inprecise, but since maxElements is quite large that doesn't matter
+        return (maxElements / 100) * MaxLoadFactor100;
+    }
+
+    ROBIN_HOOD(NODISCARD) size_t calcNumBytesInfo(size_t numElements) const {
+        return numElements + sizeof(uint64_t);
+    }
+
+    // calculation ony allowed for 2^n values
+    ROBIN_HOOD(NODISCARD) size_t calcNumBytesTotal(size_t numElements) const {
+#if ROBIN_HOOD(BITNESS) == 64
+        return numElements * sizeof(Node) + calcNumBytesInfo(numElements);
+#else
+        // make sure we're doing 64bit operations, so we are at least safe against 32bit overflows.
+        auto const ne = static_cast<uint64_t>(numElements);
+        auto const s = static_cast<uint64_t>(sizeof(Node));
+        auto const infos = static_cast<uint64_t>(calcNumBytesInfo(numElements));
+
+        auto const total64 = ne * s + infos;
+        auto const total = static_cast<size_t>(total64);
+
+        if (ROBIN_HOOD_UNLIKELY(static_cast<uint64_t>(total) != total64)) {
+            throwOverflowError();
+        }
+        return total;
+#endif
+    }
+
+private:
+    // reserves space for at least the specified number of elements.
+    // only works if numBuckets if power of two
+    void rehashPowerOfTwo(size_t numBuckets) {
+        ROBIN_HOOD_TRACE(this);
+
+        Node* const oldKeyVals = mKeyVals;
+        uint8_t const* const oldInfo = mInfo;
+
+        const size_t oldMaxElements = mMask + 1;
+
+        // resize operation: move stuff
+        init_data(numBuckets);
+        if (oldMaxElements > 1) {
+            for (size_t i = 0; i < oldMaxElements; ++i) {
+                if (oldInfo[i] != 0) {
+                    insert_move(std::move(oldKeyVals[i]));
+                    // destroy the node but DON'T destroy the data.
+                    oldKeyVals[i].~Node();
+                }
+            }
+
+            // don't destroy old data: put it into the pool instead
+            DataPool::addOrFree(oldKeyVals, calcNumBytesTotal(oldMaxElements));
+        }
+    }
+
+    ROBIN_HOOD(NOINLINE) void throwOverflowError() const {
+#if ROBIN_HOOD(HAS_EXCEPTIONS)
+        throw std::overflow_error("robin_hood::map overflow");
+#else
+        abort();
+#endif
+    }
+
+    void init_data(size_t max_elements) {
+        mNumElements = 0;
+        mMask = max_elements - 1;
+        mMaxNumElementsAllowed = calcMaxNumElementsAllowed(max_elements);
+
+        // calloc also zeroes everything
+        mKeyVals = reinterpret_cast<Node*>(
+            detail::assertNotNull<std::bad_alloc>(calloc(1, calcNumBytesTotal(max_elements))));
+        mInfo = reinterpret_cast<uint8_t*>(mKeyVals + max_elements);
+
+        // set sentinel
+        mInfo[max_elements] = 1;
+
+        mInfoInc = InitialInfoInc;
+        mInfoHashShift = InitialInfoHashShift;
+    }
+
+    template <typename Arg>
+    mapped_type& doCreateByKey(Arg&& key) {
+        while (true) {
+            size_t idx;
+            InfoType info;
+            keyToIdx(key, &idx, &info);
+            nextWhileLess(&info, &idx);
+
+            // while we potentially have a match. Can't do a do-while here because when mInfo is 0
+            // we don't want to skip forward
+            while (info == mInfo[idx]) {
+                if (WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) {
+                    // key already exists, do not insert.
+                    return mKeyVals[idx].getSecond();
+                }
+                next(&info, &idx);
+            }
+
+            // unlikely that this evaluates to true
+            if (ROBIN_HOOD_UNLIKELY(mNumElements >= mMaxNumElementsAllowed)) {
+                increase_size();
+                continue;
+            }
+
+            // key not found, so we are now exactly where we want to insert it.
+            auto const insertion_idx = idx;
+            auto const insertion_info = info;
+            if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) {
+                mMaxNumElementsAllowed = 0;
+            }
+
+            // find an empty spot
+            while (0 != mInfo[idx]) {
+                next(&info, &idx);
+            }
+
+            auto& l = mKeyVals[insertion_idx];
+            if (idx == insertion_idx) {
+                // put at empty spot. This forwards all arguments into the node where the object is
+                // constructed exactly where it is needed.
+                ::new (static_cast<void*>(&l))
+                    Node(*this, std::piecewise_construct,
+                         std::forward_as_tuple(std::forward<Arg>(key)), std::forward_as_tuple());
+            } else {
+                shiftUp(idx, insertion_idx);
+                l = Node(*this, std::piecewise_construct,
+                         std::forward_as_tuple(std::forward<Arg>(key)), std::forward_as_tuple());
+            }
+
+            // mKeyVals[idx].getFirst() = std::move(key);
+            mInfo[insertion_idx] = static_cast<uint8_t>(insertion_info);
+
+            ++mNumElements;
+            return mKeyVals[insertion_idx].getSecond();
+        }
+    }
+
+    // This is exactly the same code as operator[], except for the return values
+    template <typename Arg>
+    std::pair<iterator, bool> doInsert(Arg&& keyval) {
+        while (true) {
+            size_t idx;
+            InfoType info;
+            keyToIdx(keyval.getFirst(), &idx, &info);
+            nextWhileLess(&info, &idx);
+
+            // while we potentially have a match
+            while (info == mInfo[idx]) {
+                if (WKeyEqual::operator()(keyval.getFirst(), mKeyVals[idx].getFirst())) {
+                    // key already exists, do NOT insert.
+                    // see http://en.cppreference.com/w/cpp/container/unordered_map/insert
+                    return std::make_pair<iterator, bool>(iterator(mKeyVals + idx, mInfo + idx),
+                                                          false);
+                }
+                next(&info, &idx);
+            }
+
+            // unlikely that this evaluates to true
+            if (ROBIN_HOOD_UNLIKELY(mNumElements >= mMaxNumElementsAllowed)) {
+                increase_size();
+                continue;
+            }
+
+            // key not found, so we are now exactly where we want to insert it.
+            auto const insertion_idx = idx;
+            auto const insertion_info = info;
+            if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) {
+                mMaxNumElementsAllowed = 0;
+            }
+
+            // find an empty spot
+            while (0 != mInfo[idx]) {
+                next(&info, &idx);
+            }
+
+            auto& l = mKeyVals[insertion_idx];
+            if (idx == insertion_idx) {
+                ::new (static_cast<void*>(&l)) Node(*this, std::forward<Arg>(keyval));
+            } else {
+                shiftUp(idx, insertion_idx);
+                l = Node(*this, std::forward<Arg>(keyval));
+            }
+
+            // put at empty spot
+            mInfo[insertion_idx] = static_cast<uint8_t>(insertion_info);
+
+            ++mNumElements;
+            return std::make_pair(iterator(mKeyVals + insertion_idx, mInfo + insertion_idx), true);
+        }
+    }
+
+    bool try_increase_info() {
+        ROBIN_HOOD_LOG("mInfoInc=" << mInfoInc << ", numElements=" << mNumElements
+                                   << ", maxNumElementsAllowed="
+                                   << calcMaxNumElementsAllowed(mMask + 1));
+        if (mInfoInc <= 2) {
+            // need to be > 2 so that shift works (otherwise undefined behavior!)
+            return false;
+        }
+        // we got space left, try to make info smaller
+        mInfoInc = static_cast<uint8_t>(mInfoInc >> 1U);
+
+        // remove one bit of the hash, leaving more space for the distance info.
+        // This is extremely fast because we can operate on 8 bytes at once.
+        ++mInfoHashShift;
+        auto const data = reinterpret_cast_no_cast_align_warning<uint64_t*>(mInfo);
+        auto const numEntries = (mMask + 1) / 8;
+
+        for (size_t i = 0; i < numEntries; ++i) {
+            data[i] = (data[i] >> 1U) & UINT64_C(0x7f7f7f7f7f7f7f7f);
+        }
+        mMaxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1);
+        return true;
+    }
+
+    void increase_size() {
+        // nothing allocated yet? just allocate InitialNumElements
+        if (0 == mMask) {
+            init_data(InitialNumElements);
+            return;
+        }
+
+        auto const maxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1);
+        if (mNumElements < maxNumElementsAllowed && try_increase_info()) {
+            return;
+        }
+
+        ROBIN_HOOD_LOG("mNumElements=" << mNumElements << ", maxNumElementsAllowed="
+                                       << maxNumElementsAllowed << ", load="
+                                       << (static_cast<double>(mNumElements) * 100.0 /
+                                           (static_cast<double>(mMask) + 1)));
+        // it seems we have a really bad hash function! don't try to resize again
+        if (mNumElements * 2 < calcMaxNumElementsAllowed(mMask + 1)) {
+            throwOverflowError();
+        }
+
+        rehashPowerOfTwo((mMask + 1) * 2);
+    }
+
+    void destroy() {
+        if (0 == mMask) {
+            // don't deallocate!
+            return;
+        }
+
+        Destroyer<Self, IsFlatMap && std::is_trivially_destructible<Node>::value>{}
+            .nodesDoNotDeallocate(*this);
+        free(mKeyVals);
+    }
+
+    void init() noexcept {
+        mKeyVals = reinterpret_cast<Node*>(&mMask);
+        mInfo = reinterpret_cast<uint8_t*>(&mMask);
+        mNumElements = 0;
+        mMask = 0;
+        mMaxNumElementsAllowed = 0;
+        mInfoInc = InitialInfoInc;
+        mInfoHashShift = InitialInfoHashShift;
+    }
+
+    // members are sorted so no padding occurs
+    Node* mKeyVals = reinterpret_cast<Node*>(&mMask);    // 8 byte  8
+    uint8_t* mInfo = reinterpret_cast<uint8_t*>(&mMask); // 8 byte 16
+    size_t mNumElements = 0;                             // 8 byte 24
+    size_t mMask = 0;                                    // 8 byte 32
+    size_t mMaxNumElementsAllowed = 0;                   // 8 byte 40
+    InfoType mInfoInc = InitialInfoInc;                  // 4 byte 44
+    InfoType mInfoHashShift = InitialInfoHashShift;      // 4 byte 48
+                                                         // 16 byte 56 if NodeAllocator
+};
+
+} // namespace detail
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+          typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_flat_map = detail::unordered_map<true, MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+          typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_node_map = detail::unordered_map<false, MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+template <typename Key, typename T, typename Hash = hash<Key>,
+          typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80>
+using unordered_map =
+    detail::unordered_map<sizeof(robin_hood::pair<Key, T>) <= sizeof(size_t) * 6 &&
+                              std::is_nothrow_move_constructible<robin_hood::pair<Key, T>>::value &&
+                              std::is_nothrow_move_assignable<robin_hood::pair<Key, T>>::value,
+                          MaxLoadFactor100, Key, T, Hash, KeyEqual>;
+
+} // namespace robin_hood
+
+#endif

+ 44 - 65
Include/RmlUi/Core/Context.h

@@ -31,29 +31,19 @@
 
 
 #include "Header.h"
 #include "Header.h"
 #include "Types.h"
 #include "Types.h"
-#include "ReferenceCountable.h"
-#include "ElementReference.h"
+#include "Traits.h"
 #include "Input.h"
 #include "Input.h"
-#include "String.h"
 #include "ScriptInterface.h"
 #include "ScriptInterface.h"
-#include "ViewState.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
 class Stream;
 class Stream;
-class Dictionary;
-
-}
-}
-
-namespace Rml {
-namespace Core {
-
 class ContextInstancer;
 class ContextInstancer;
 class ElementDocument;
 class ElementDocument;
 class EventListener;
 class EventListener;
 class RenderInterface;
 class RenderInterface;
+enum class EventId : uint16_t;
 
 
 /**
 /**
 	A context for storing, rendering and processing RML documents. Multiple contexts can exist simultaneously.
 	A context for storing, rendering and processing RML documents. Multiple contexts can exist simultaneously.
@@ -82,10 +72,6 @@ public:
 	/// @return The current dimensions of the context.
 	/// @return The current dimensions of the context.
 	const Vector2i& GetDimensions() const;
 	const Vector2i& GetDimensions() const;
 
 
-
-	/// Returns the current state of the view.
-	const ViewState& GetViewState() const noexcept;
-
 	/// Changes the size ratio of 'dp' unit to 'px' unit
 	/// Changes the size ratio of 'dp' unit to 'px' unit
 	/// @param[in] dp_ratio The new density-independent pixel ratio of the context.
 	/// @param[in] dp_ratio The new density-independent pixel ratio of the context.
 	void SetDensityIndependentPixelRatio(float density_independent_pixel_ratio);
 	void SetDensityIndependentPixelRatio(float density_independent_pixel_ratio);
@@ -93,27 +79,27 @@ public:
 	/// @return The current density-independent pixel ratio of the context.
 	/// @return The current density-independent pixel ratio of the context.
 	float GetDensityIndependentPixelRatio() const;
 	float GetDensityIndependentPixelRatio() const;
 
 
-
-	/// Updates all elements in the context's documents.
+	/// Updates all elements in the context's documents. 
+	/// This must be called before Context::Render, but after any elements have been changed, added or removed.
 	bool Update();
 	bool Update();
 	/// Renders all visible elements in the context's documents.
 	/// Renders all visible elements in the context's documents.
 	bool Render();
 	bool Render();
 
 
 	/// Creates a new, empty document and places it into this context.
 	/// Creates a new, empty document and places it into this context.
 	/// @param[in] tag The document type to create.
 	/// @param[in] tag The document type to create.
-	/// @return The new document, or NULL if no document could be created. The document is returned with a reference owned by the caller.
+	/// @return The new document, or nullptr if no document could be created.
 	ElementDocument* CreateDocument(const String& tag = "body");
 	ElementDocument* CreateDocument(const String& tag = "body");
 	/// Load a document into the context.
 	/// Load a document into the context.
 	/// @param[in] document_path The path to the document to load.
 	/// @param[in] document_path The path to the document to load.
-	/// @return The loaded document, or NULL if no document was loaded. The document is returned with a reference owned by the caller.
+	/// @return The loaded document, or nullptr if no document was loaded.
 	ElementDocument* LoadDocument(const String& document_path);
 	ElementDocument* LoadDocument(const String& document_path);
 	/// Load a document into the context.
 	/// Load a document into the context.
 	/// @param[in] document_stream The opened stream, ready to read.
 	/// @param[in] document_stream The opened stream, ready to read.
-	/// @return The loaded document, or NULL if no document was loaded. The document is returned with a reference owned by the caller.
+	/// @return The loaded document, or nullptr if no document was loaded.
 	ElementDocument* LoadDocument(Stream* document_stream);
 	ElementDocument* LoadDocument(Stream* document_stream);
 	/// Load a document into the context.
 	/// Load a document into the context.
 	/// @param[in] string The string containing the document RML.
 	/// @param[in] string The string containing the document RML.
-	/// @return The loaded document, or NULL if no document was loaded. The document is returned with a reference owned by the caller.
+	/// @return The loaded document, or nullptr if no document was loaded.
 	ElementDocument* LoadDocumentFromMemory(const String& string);
 	ElementDocument* LoadDocumentFromMemory(const String& string);
 	/// Unload the given document.
 	/// Unload the given document.
 	/// @param[in] document The document to unload.
 	/// @param[in] document The document to unload.
@@ -128,34 +114,41 @@ public:
 
 
 	/// Returns the first document in the context with the given id.
 	/// Returns the first document in the context with the given id.
 	/// @param[in] id The id of the desired document.
 	/// @param[in] id The id of the desired document.
-	/// @return The document (if it was found), or NULL if no document exists with the ID. The document is returned with a borrowed reference.
+	/// @return The document (if it was found), or nullptr if no document exists with the ID.
 	ElementDocument* GetDocument(const String& id);
 	ElementDocument* GetDocument(const String& id);
 	/// Returns a document in the context by index.
 	/// Returns a document in the context by index.
 	/// @param[in] index The index of the desired document.
 	/// @param[in] index The index of the desired document.
-	/// @return The document (if one exists with this index), or NULL if the index was invalid. The document is returned with a borrowed reference.
+	/// @return The document (if one exists with this index), or nullptr if the index was invalid.
 	ElementDocument* GetDocument(int index);
 	ElementDocument* GetDocument(int index);
 	/// Returns the number of documents in the context.
 	/// Returns the number of documents in the context.
-	/// @return The number of documents in the context.
 	int GetNumDocuments() const;
 	int GetNumDocuments() const;
 
 
 	/// Returns the hover element.
 	/// Returns the hover element.
-	/// @return The element the mouse cursor is hovering over. The element is returned with a borrowed reference.
+	/// @return The element the mouse cursor is hovering over.
 	Element* GetHoverElement();
 	Element* GetHoverElement();
-
 	/// Returns the focus element.
 	/// Returns the focus element.
-	/// @return The element with input focus. The element is returned with a borrowed reference.
+	/// @return The element with input focus.
 	Element* GetFocusElement();
 	Element* GetFocusElement();
-
 	/// Returns the root element that holds all the documents
 	/// Returns the root element that holds all the documents
-	/// @return The root element. The element is returned with a borrowed reference.
+	/// @return The root element.
 	Element* GetRootElement();
 	Element* GetRootElement();
 
 
+	// Returns the youngest descendent of the given element which is under the given point in screen coordinates.
+	// @param[in] point The point to test.
+	// @param[in] ignore_element If set, this element and its descendents will be ignored.
+	// @param[in] element Used internally.
+	// @return The element under the point, or nullptr if nothing is.
+	Element* GetElementAtPoint(Vector2f point, const Element* ignore_element = nullptr, Element* element = nullptr) const;
+
 	/// Brings the document to the front of the document stack.
 	/// Brings the document to the front of the document stack.
 	/// @param[in] document The document to pull to the front of the stack.
 	/// @param[in] document The document to pull to the front of the stack.
 	void PullDocumentToFront(ElementDocument* document);
 	void PullDocumentToFront(ElementDocument* document);
 	/// Sends the document to the back of the document stack.
 	/// Sends the document to the back of the document stack.
 	/// @param[in] document The document to push to the bottom of the stack.
 	/// @param[in] document The document to push to the bottom of the stack.
 	void PushDocumentToBack(ElementDocument* document);
 	void PushDocumentToBack(ElementDocument* document);
+	/// Remove the document from the focus history and focus the previous document.
+	/// @param[in] document The document to unfocus.
+	void UnfocusDocument(ElementDocument* document);
 
 
 	/// Adds an event listener to the context's root element.
 	/// Adds an event listener to the context's root element.
 	/// @param[in] event The name of the event to attach to.
 	/// @param[in] event The name of the event to attach to.
@@ -179,12 +172,14 @@ public:
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	bool ProcessKeyUp(Input::KeyIdentifier key_identifier, int key_modifier_state);
 	bool ProcessKeyUp(Input::KeyIdentifier key_identifier, int key_modifier_state);
 
 
-	/// Sends a single character of text as text input into this context.
-	/// @param[in] character The UCS-2 character to send into this context.
+	/// Sends a single unicode character as text input into this context.
+	/// @param[in] character The unicode code point to send into this context.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
-	bool ProcessTextInput(word character);
+	bool ProcessTextInput(Character character);
+	/// Sends a single ascii character as text input into this context.
+	bool ProcessTextInput(char character);
 	/// Sends a string of text as text input into this context.
 	/// Sends a string of text as text input into this context.
-	/// @param[in] string The UCS-2 string to send into this context.
+	/// @param[in] string The UTF8 string to send into this context.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	bool ProcessTextInput(const String& string);
 	bool ProcessTextInput(const String& string);
 
 
@@ -205,14 +200,7 @@ public:
 	/// @param[in] wheel_delta The mouse-wheel movement this frame. RmlUi treats a negative delta as up movement (away from the user), positive as down.
 	/// @param[in] wheel_delta The mouse-wheel movement this frame. RmlUi treats a negative delta as up movement (away from the user), positive as down.
 	/// @param[in] key_modifier_state The state of key modifiers (shift, control, caps-lock, etc) keys; this should be generated by ORing together members of the Input::KeyModifier enumeration.
 	/// @param[in] key_modifier_state The state of key modifiers (shift, control, caps-lock, etc) keys; this should be generated by ORing together members of the Input::KeyModifier enumeration.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
-	bool ProcessMouseWheel(int wheel_delta, int key_modifier_state);
-
-	/// Notifies RmlUi of a change in the projection matrix.
-	/// @param[in] projection The new projection matrix.
-	void ProcessProjectionChange(const Matrix4f &projection);
-	/// Notifies RmlUi of a change in the view matrix.
-	/// @param[in] projection The new view matrix.
-	void ProcessViewChange(const Matrix4f &view);
+	bool ProcessMouseWheel(float wheel_delta, int key_modifier_state);
 
 
 	/// Gets the context's render interface.
 	/// Gets the context's render interface.
 	/// @return The render interface the context renders through.
 	/// @return The render interface the context renders through.
@@ -231,7 +219,7 @@ public:
 	void SetInstancer(ContextInstancer* instancer);
 	void SetInstancer(ContextInstancer* instancer);
 
 
 protected:
 protected:
-	virtual void OnReferenceDeactivate();
+	void Release() override;
 
 
 private:
 private:
 	String name;
 	String name;
@@ -240,8 +228,8 @@ private:
 
 
 	ContextInstancer* instancer;
 	ContextInstancer* instancer;
 
 
-	typedef std::set< ElementReference > ElementSet;
-	typedef std::vector< ElementReference > ElementList;
+	using ElementSet = SmallOrderedSet< Element* > ;
+	using ElementList = std::vector< Element* >;
 	// Set of elements that are currently in hover state.
 	// Set of elements that are currently in hover state.
 	ElementSet hover_chain;
 	ElementSet hover_chain;
 	// List of elements that are currently in active state.
 	// List of elements that are currently in active state.
@@ -250,16 +238,16 @@ private:
 	ElementList document_focus_history;
 	ElementList document_focus_history;
 
 
 	// Documents that have been unloaded from the context but not yet released.
 	// Documents that have been unloaded from the context but not yet released.
-	ElementList unloaded_documents;
+	OwnedElementList unloaded_documents;
 
 
 	// Root of the element tree.
 	// Root of the element tree.
-	Element* root;
+	ElementPtr root;
 	// The element that current has input focus.
 	// The element that current has input focus.
-	ElementReference focus;
+	Element* focus;
 	// The top-most element being hovered over.
 	// The top-most element being hovered over.
-	ElementReference hover;
+	Element* hover;
 	// The element that was being hovered over when the primary mouse button was pressed most recently.
 	// The element that was being hovered over when the primary mouse button was pressed most recently.
-	ElementReference active;
+	Element* active;
 
 
 	// The element that was clicked on last.
 	// The element that was clicked on last.
 	Element* last_click_element;
 	Element* last_click_element;
@@ -270,10 +258,10 @@ private:
 	bool enable_cursor;
 	bool enable_cursor;
 	String cursor_name;
 	String cursor_name;
 	// Document attached to cursor (e.g. while dragging).
 	// Document attached to cursor (e.g. while dragging).
-	ElementDocument* cursor_proxy;
+	ElementPtr cursor_proxy;
 
 
 	// The element that is currently being dragged (or about to be dragged).
 	// The element that is currently being dragged (or about to be dragged).
-	ElementReference drag;
+	Element* drag;
 	// True if a drag has begun (ie, the ondragstart event has been fired for the drag element), false otherwise.
 	// True if a drag has begun (ie, the ondragstart event has been fired for the drag element), false otherwise.
 	bool drag_started;
 	bool drag_started;
 	// True if the current drag is a verbose drag (ie, sends ondragover, ondragout, ondragdrop, etc, events).
 	// True if the current drag is a verbose drag (ie, sends ondragover, ondragout, ondragdrop, etc, events).
@@ -283,7 +271,7 @@ private:
 
 
 	// The element currently being dragged over; this is equivalent to hover, but only set while an element is being
 	// The element currently being dragged over; this is equivalent to hover, but only set while an element is being
 	// dragged, and excludes the dragged element.
 	// dragged, and excludes the dragged element.
-	ElementReference drag_hover;
+	Element* drag_hover;
 	// Set of elements that are currently being dragged over; this differs from the hover state as the dragged element
 	// Set of elements that are currently being dragged over; this differs from the hover state as the dragged element
 	// itself can't be part of it.
 	// itself can't be part of it.
 	ElementSet drag_hover_chain;
 	ElementSet drag_hover_chain;
@@ -296,11 +284,8 @@ private:
 	Vector2i clip_origin;
 	Vector2i clip_origin;
 	Vector2i clip_dimensions;
 	Vector2i clip_dimensions;
 
 
-	// The current view state
-	ViewState view_state;
-
-	// Internal callback for when an element is removed from the hierarchy.
-	void OnElementRemove(Element* element);
+	// Internal callback for when an element is detached or removed from the hierarchy.
+	void OnElementDetach(Element* element);
 	// Internal callback for when a new element gains focus.
 	// Internal callback for when a new element gains focus.
 	bool OnFocusChange(Element* element);
 	bool OnFocusChange(Element* element);
 
 
@@ -309,12 +294,6 @@ private:
 
 
 	// Updates the current hover elements, sending required events.
 	// Updates the current hover elements, sending required events.
 	void UpdateHoverChain(const Dictionary& parameters, const Dictionary& drag_parameters, const Vector2i& old_mouse_position);
 	void UpdateHoverChain(const Dictionary& parameters, const Dictionary& drag_parameters, const Vector2i& old_mouse_position);
-	// Returns the youngest descendent of the given element which is under the given point in screen coordinates.
-	// @param[in] point The point to test.
-	// @param[in] ignore_element If set, this element and its descendents will be ignored.
-	// @param[in] element Used internally.
-	// @return The element under the point, or NULL if nothing is.
-	Element* GetElementAtPoint(const Vector2f& point, const Element* ignore_element = NULL, Element* element = NULL);
 
 
 	// Creates the drag clone from the given element. The old drag clone will be released if
 	// Creates the drag clone from the given element. The old drag clone will be released if
 	// necessary.
 	// necessary.
@@ -336,7 +315,7 @@ private:
 	void ReleaseUnloadedDocuments();
 	void ReleaseUnloadedDocuments();
 
 
 	// Sends the specified event to all elements in new_items that don't appear in old_items.
 	// Sends the specified event to all elements in new_items that don't appear in old_items.
-	static void SendEvents(const ElementSet& old_items, const ElementSet& new_items, const String& event, const Dictionary& parameters, bool interruptible);
+	static void SendEvents(const ElementSet& old_items, const ElementSet& new_items, EventId id, const Dictionary& parameters);
 
 
 	friend class Element;
 	friend class Element;
 	friend RMLUICORE_API Context* CreateContext(const String&, const Vector2i&, RenderInterface*);
 	friend RMLUICORE_API Context* CreateContext(const String&, const Vector2i&, RenderInterface*);

+ 4 - 6
Include/RmlUi/Core/ContextInstancer.h

@@ -29,8 +29,8 @@
 #ifndef RMLUICORECONTEXTINSTANCER_H
 #ifndef RMLUICORECONTEXTINSTANCER_H
 #define RMLUICORECONTEXTINSTANCER_H
 #define RMLUICORECONTEXTINSTANCER_H
 
 
-#include "ReferenceCountable.h"
 #include "Header.h"
 #include "Header.h"
+#include "Traits.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -44,7 +44,7 @@ class Event;
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICORE_API ContextInstancer : public ReferenceCountable
+class RMLUICORE_API ContextInstancer : public Releasable
 {
 {
 public:
 public:
 	virtual ~ContextInstancer();
 	virtual ~ContextInstancer();
@@ -52,17 +52,15 @@ public:
 	/// Instances a context.
 	/// Instances a context.
 	/// @param[in] name Name of this context.
 	/// @param[in] name Name of this context.
 	/// @return The instanced context.
 	/// @return The instanced context.
-	virtual Context* InstanceContext(const String& name) = 0;
+	virtual ContextPtr InstanceContext(const String& name) = 0;
 
 
 	/// Releases a context previously created by this context.
 	/// Releases a context previously created by this context.
 	/// @param[in] context The context to release.
 	/// @param[in] context The context to release.
 	virtual void ReleaseContext(Context* context) = 0;
 	virtual void ReleaseContext(Context* context) = 0;
 
 
+protected:
 	/// Releases this context instancer
 	/// Releases this context instancer
 	virtual void Release() = 0;
 	virtual void Release() = 0;
-
-private:
-	virtual void OnReferenceDeactivate();
 };
 };
 
 
 }
 }

+ 54 - 20
Include/RmlUi/Core/Core.h

@@ -34,6 +34,7 @@
 #include "Header.h"
 #include "Header.h"
 #include "Animation.h"
 #include "Animation.h"
 #include "Box.h"
 #include "Box.h"
+#include "ComputedValues.h"
 #include "Context.h"
 #include "Context.h"
 #include "ContextInstancer.h"
 #include "ContextInstancer.h"
 #include "Decorator.h"
 #include "Decorator.h"
@@ -41,8 +42,6 @@
 #include "Element.h"
 #include "Element.h"
 #include "ElementDocument.h"
 #include "ElementDocument.h"
 #include "ElementInstancer.h"
 #include "ElementInstancer.h"
-#include "ElementInstancerGeneric.h"
-#include "ElementReference.h"
 #include "ElementScroll.h"
 #include "ElementScroll.h"
 #include "ElementText.h"
 #include "ElementText.h"
 #include "ElementUtilities.h"
 #include "ElementUtilities.h"
@@ -52,28 +51,29 @@
 #include "EventListenerInstancer.h"
 #include "EventListenerInstancer.h"
 #include "Factory.h"
 #include "Factory.h"
 #include "FileInterface.h"
 #include "FileInterface.h"
-#include "Font.h"
-#include "FontDatabase.h"
 #include "FontEffect.h"
 #include "FontEffect.h"
 #include "FontGlyph.h"
 #include "FontGlyph.h"
+#include "FontEngineInterface.h"
 #include "Geometry.h"
 #include "Geometry.h"
 #include "GeometryUtilities.h"
 #include "GeometryUtilities.h"
+#include "ID.h"
 #include "Input.h"
 #include "Input.h"
 #include "Log.h"
 #include "Log.h"
 #include "Plugin.h"
 #include "Plugin.h"
+#include "PropertiesIteratorView.h"
 #include "Property.h"
 #include "Property.h"
 #include "PropertyDefinition.h"
 #include "PropertyDefinition.h"
 #include "PropertyDictionary.h"
 #include "PropertyDictionary.h"
+#include "PropertyIdSet.h"
 #include "PropertyParser.h"
 #include "PropertyParser.h"
 #include "PropertySpecification.h"
 #include "PropertySpecification.h"
 #include "RenderInterface.h"
 #include "RenderInterface.h"
-#include "String.h"
+#include "Spritesheet.h"
+#include "StringUtilities.h"
 #include "StyleSheet.h"
 #include "StyleSheet.h"
-#include "StyleSheetKeywords.h"
 #include "StyleSheetSpecification.h"
 #include "StyleSheetSpecification.h"
 #include "SystemInterface.h"
 #include "SystemInterface.h"
 #include "Texture.h"
 #include "Texture.h"
-#include "Types.h"
 #include "Tween.h"
 #include "Tween.h"
 #include "Vertex.h"
 #include "Vertex.h"
 #include "XMLNodeHandler.h"
 #include "XMLNodeHandler.h"
@@ -100,52 +100,86 @@ RMLUICORE_API void Shutdown();
 RMLUICORE_API String GetVersion();
 RMLUICORE_API String GetVersion();
 
 
 /// Sets the interface through which all system requests are made. This must be called before Initialise().
 /// Sets the interface through which all system requests are made. This must be called before Initialise().
-/// @param[in] system_interface The application-specified logging interface.
+/// @param[in] system_interface A non-owning pointer to the application-specified logging interface.
+/// @lifetime The interface must be kept alive until after the call to Core::Shutdown.
 RMLUICORE_API void SetSystemInterface(SystemInterface* system_interface);
 RMLUICORE_API void SetSystemInterface(SystemInterface* system_interface);
 /// Returns RmlUi's system interface.
 /// Returns RmlUi's system interface.
-/// @return RmlUi's system interface.
 RMLUICORE_API SystemInterface* GetSystemInterface();
 RMLUICORE_API SystemInterface* GetSystemInterface();
 
 
 /// Sets the interface through which all rendering requests are made. This is not required to be called, but if it is
 /// Sets the interface through which all rendering requests are made. This is not required to be called, but if it is
 /// it must be called before Initialise(). If no render interface is specified, then all contexts must have a custom
 /// it must be called before Initialise(). If no render interface is specified, then all contexts must have a custom
 /// render interface.
 /// render interface.
-/// @param[in] render_interface Render interface implementation.
+/// @param[in] render_interface A non-owning pointer to the render interface implementation.
+/// @lifetime The interface must be kept alive until after the call to Core::Shutdown.
 RMLUICORE_API void SetRenderInterface(RenderInterface* render_interface);
 RMLUICORE_API void SetRenderInterface(RenderInterface* render_interface);
 /// Returns RmlUi's default's render interface.
 /// Returns RmlUi's default's render interface.
-/// @return RmlUi's render interface.
 RMLUICORE_API RenderInterface* GetRenderInterface();
 RMLUICORE_API RenderInterface* GetRenderInterface();
 
 
 /// Sets the interface through which all file I/O requests are made. This is not required to be called, but if it is it
 /// Sets the interface through which all file I/O requests are made. This is not required to be called, but if it is it
 /// must be called before Initialise().
 /// must be called before Initialise().
-/// @param[in] file_interface The application-specified file interface
+/// @param[in] file_interface A non-owning pointer to the application-specified file interface.
+/// @lifetime The interface must be kept alive until after the call to Core::Shutdown.
 RMLUICORE_API void SetFileInterface(FileInterface* file_interface);
 RMLUICORE_API void SetFileInterface(FileInterface* file_interface);
 /// Returns RmlUi's file interface.
 /// Returns RmlUi's file interface.
-/// @return RmlUi's file interface.
 RMLUICORE_API FileInterface* GetFileInterface();
 RMLUICORE_API FileInterface* GetFileInterface();
 
 
+/// Sets the interface through which all font requests are made. This is not required to be called, but if it is
+/// it must be called before Initialise().
+/// @param[in] font_interface A non-owning pointer to the application-specified font engine interface.
+/// @lifetime The interface must be kept alive until after the call to Core::Shutdown.
+RMLUICORE_API void SetFontEngineInterface(FontEngineInterface* font_interface);
+/// Returns RmlUi's font interface.
+RMLUICORE_API FontEngineInterface* GetFontEngineInterface();
+	
 /// Creates a new element context.
 /// Creates a new element context.
 /// @param[in] name The new name of the context. This must be unique.
 /// @param[in] name The new name of the context. This must be unique.
 /// @param[in] dimensions The initial dimensions of the new context.
 /// @param[in] dimensions The initial dimensions of the new context.
-/// @param[in] render_interface The custom render interface to use, or NULL to use the default.
-/// @return The new context, or NULL if the context could not be created.
-RMLUICORE_API Context* CreateContext(const String& name, const Vector2i& dimensions, RenderInterface* render_interface = NULL);
+/// @param[in] render_interface The custom render interface to use, or nullptr to use the default.
+/// @lifetime If specified, the render interface must be kept alive until after the context is destroyed or the call to Core::Shutdown.
+/// @return A non-owning pointer to the new context, or nullptr if the context could not be created.
+RMLUICORE_API Context* CreateContext(const String& name, const Vector2i& dimensions, RenderInterface* render_interface = nullptr);
+/// Removes and destroys a context.
+/// @param[in] name The name of the context to remove.
+/// @return True if name is a valid context, false otherwise.
+RMLUICORE_API bool RemoveContext(const String& name);
 /// Fetches a previously constructed context by name.
 /// Fetches a previously constructed context by name.
 /// @param[in] name The name of the desired context.
 /// @param[in] name The name of the desired context.
-/// @return The desired context, or NULL if no context exists with the given name.
+/// @return The desired context, or nullptr if no context exists with the given name.
 RMLUICORE_API Context* GetContext(const String& name);
 RMLUICORE_API Context* GetContext(const String& name);
 /// Fetches a context by index.
 /// Fetches a context by index.
 /// @param[in] index The index of the desired context. If this is outside of the valid range of contexts, it will be clamped.
 /// @param[in] index The index of the desired context. If this is outside of the valid range of contexts, it will be clamped.
-/// @return The requested context, or NULL if no contexts exist.
+/// @return The requested context, or nullptr if no contexts exist.
 RMLUICORE_API Context* GetContext(int index);
 RMLUICORE_API Context* GetContext(int index);
 /// Returns the number of active contexts.
 /// Returns the number of active contexts.
 /// @return The total number of active RmlUi contexts.
 /// @return The total number of active RmlUi contexts.
 RMLUICORE_API int GetNumContexts();
 RMLUICORE_API int GetNumContexts();
 
 
+/// Adds a new font face to the font engine. The face's family, style and weight will be determined from the face itself.
+/// @param[in] file_name The file to load the face from.
+/// @param[in] fallback_face True to use this font face for unknown characters in other font faces.
+/// @return True if the face was loaded successfully, false otherwise.
+RMLUICORE_API bool LoadFontFace(const String& file_name, bool fallback_face = false);
+/// Adds a new font face from memory to the font engine. The face's family, style and weight is given by the parameters.
+/// @param[in] data A pointer to the data.
+/// @param[in] data_size Size of the data in bytes.
+/// @param[in] family The family to register the font as.
+/// @param[in] style The style to register the font as.
+/// @param[in] weight The weight to register the font as.
+/// @param[in] fallback_face True to use this font face for unknown characters in other font faces.
+/// @return True if the face was loaded successfully, false otherwise.
+RMLUICORE_API bool LoadFontFace(const byte* data, int data_size, const String& font_family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face = false);
+
 /// Registers a generic RmlUi plugin.
 /// Registers a generic RmlUi plugin.
 RMLUICORE_API void RegisterPlugin(Plugin* plugin);
 RMLUICORE_API void RegisterPlugin(Plugin* plugin);
 
 
-/// Forces all compiled geometry handles generated by RmlUi to be released.
-RMLUICORE_API void ReleaseCompiledGeometries();
+/// Registers a new event type. If the type already exists, it will replace custom event types, but not internal types.
+/// @param[in] type The new event type.
+/// @param[in] interruptible Whether the event can be interrupted during dispatch.
+/// @param[in] bubbles Whether the event executes the bubble phase. If false, only capture and target phase is executed.
+/// @param[in] default_action_phase Defines during which phase(s) the 'Element::ProcessDefaultAction' method is called.
+/// @return The EventId of the newly created type, or existing type if 'type' is an internal type.
+RMLUICORE_API EventId RegisterEventType(const String& type, bool interruptible, bool bubbles, DefaultActionPhase default_action_phase = DefaultActionPhase::None);
+
 /// Forces all texture handles loaded and generated by RmlUi to be released.
 /// Forces all texture handles loaded and generated by RmlUi to be released.
 RMLUICORE_API void ReleaseTextures();
 RMLUICORE_API void ReleaseTextures();
 
 

+ 16 - 9
Include/RmlUi/Core/Debug.h

@@ -59,6 +59,7 @@
 #define RMLUI_ERROR
 #define RMLUI_ERROR
 #define RMLUI_ERRORMSG(m)
 #define RMLUI_ERRORMSG(m)
 #define RMLUI_VERIFY(x) x
 #define RMLUI_VERIFY(x) x
+#define RMLUI_ASSERT_NONRECURSIVE
 #else
 #else
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -92,18 +93,24 @@ if (!Rml::Core::Assert(m, __FILE__, __LINE__)) \
 }
 }
 #define RMLUI_VERIFY(x) RMLUI_ASSERT(x)
 #define RMLUI_VERIFY(x) RMLUI_ASSERT(x)
 
 
-}
-}
-#endif
+struct RmlUiAssertNonrecursive {
+	bool& entered;
+	RmlUiAssertNonrecursive(bool& entered) : entered(entered) {
+		RMLUI_ASSERTMSG(!entered, "A method defined as non-recursive was entered twice!");
+		entered = true;
+	}
+	~RmlUiAssertNonrecursive() {
+		entered = false;
+	}
+};
 
 
-namespace Rml {
-namespace Core {
+#define RMLUI_ASSERT_NONRECURSIVE \
+static bool rmlui_nonrecursive_entered = false; \
+RmlUiAssertNonrecursive rmlui_nonrecursive(rmlui_nonrecursive_entered)
 
 
-template <bool> struct STATIC_ASSERTION_FAILURE;
-template <> struct STATIC_ASSERTION_FAILURE<true>{};	
-	
 }
 }
 }
 }
-#define RMLUI_STATIC_ASSERT(cond, msg) Rml::Core::STATIC_ASSERTION_FAILURE<cond> msg; (void)&msg;
+#endif
+
 
 
 #endif
 #endif

+ 15 - 38
Include/RmlUi/Core/Decorator.h

@@ -29,7 +29,6 @@
 #ifndef RMLUICOREDECORATOR_H
 #ifndef RMLUICOREDECORATOR_H
 #define RMLUICOREDECORATOR_H
 #define RMLUICOREDECORATOR_H
 
 
-#include "ReferenceCountable.h"
 #include <vector>
 #include <vector>
 #include "Header.h"
 #include "Header.h"
 #include "Texture.h"
 #include "Texture.h"
@@ -43,7 +42,6 @@ class Element;
 class PropertyDictionary;
 class PropertyDictionary;
 class Property;
 class Property;
 struct Texture;
 struct Texture;
-class TextureResource;
 
 
 /**
 /**
 	The abstract base class for any visual object that can be attached to any element.
 	The abstract base class for any visual object that can be attached to any element.
@@ -51,7 +49,7 @@ class TextureResource;
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API Decorator : public ReferenceCountable
+class RMLUICORE_API Decorator
 {
 {
 public:
 public:
 	Decorator();
 	Decorator();
@@ -59,63 +57,42 @@ public:
 
 
 	/// Called on a decorator to generate any required per-element data for a newly decorated element.
 	/// Called on a decorator to generate any required per-element data for a newly decorated element.
 	/// @param[in] element The newly decorated element.
 	/// @param[in] element The newly decorated element.
-	/// @return A handle to a decorator-defined data handle, or NULL if none is needed for the element.
-	virtual DecoratorDataHandle GenerateElementData(Element* element) = 0;
+	/// @return A handle to a decorator-defined data handle, or nullptr if none is needed for the element.
+	virtual DecoratorDataHandle GenerateElementData(Element* element) const = 0;
 	/// Called to release element data generated by this decorator.
 	/// Called to release element data generated by this decorator.
 	/// @param[in] element_data The element data handle to release.
 	/// @param[in] element_data The element data handle to release.
-	virtual void ReleaseElementData(DecoratorDataHandle element_data) = 0;
-
-	/// Sets the z-index of the decorator. A decorator with a higher z-index will be rendered after a decorator
-	/// with a lower z-index. By default, all decorators have a z-index of 0.
-	/// @param[in] z-index The new z-index of the decorator.
-	void SetZIndex(float z_index);
-	/// Returns the decorator's z-index.
-	/// @return The z-index of the decorator.
-	float GetZIndex() const;
-
-	/// Sets the specificity of the decorator.
-	/// @param[in] specificity The specificity of the decorator.
-	void SetSpecificity(int specificity);
-	/// Returns the specificity of the decorator. This is used when multiple pseudo-classes are active on an
-	/// element, each with similarly-named decorators.
-	/// @return The specificity of the decorator.
-	int GetSpecificity() const;
+	virtual void ReleaseElementData(DecoratorDataHandle element_data) const = 0;
 
 
 	/// Called to render the decorator on an element.
 	/// Called to render the decorator on an element.
 	/// @param[in] element The element to render the decorator on.
 	/// @param[in] element The element to render the decorator on.
 	/// @param[in] element_data The handle to the data generated by the decorator for the element.
 	/// @param[in] element_data The handle to the data generated by the decorator for the element.
-	virtual void RenderElement(Element* element, DecoratorDataHandle element_data) = 0;
+	virtual void RenderElement(Element* element, DecoratorDataHandle element_data) const = 0;
 
 
 	/// Value specifying an invalid or non-existent Decorator data handle.
 	/// Value specifying an invalid or non-existent Decorator data handle.
 	static const DecoratorDataHandle INVALID_DECORATORDATAHANDLE = 0;
 	static const DecoratorDataHandle INVALID_DECORATORDATAHANDLE = 0;
 
 
 protected:
 protected:
-	/// Releases the decorator through its instancer.
-	virtual void OnReferenceDeactivate();
-
 	/// Attempts to load a texture into the list of textures in use by the decorator.
 	/// Attempts to load a texture into the list of textures in use by the decorator.
 	/// @param[in] texture_name The name of the texture to load.
 	/// @param[in] texture_name The name of the texture to load.
 	/// @param[in] rcss_path The RCSS file the decorator definition was loaded from; this is used to resolve relative paths.
 	/// @param[in] rcss_path The RCSS file the decorator definition was loaded from; this is used to resolve relative paths.
 	/// @return The index of the texture if the load was successful, or -1 if the load failed.
 	/// @return The index of the texture if the load was successful, or -1 if the load failed.
 	int LoadTexture(const String& texture_name, const String& rcss_path);
 	int LoadTexture(const String& texture_name, const String& rcss_path);
+	/// Adds a texture if it is valid into the list of textures in use by the decorator.
+	/// @param[in] texture The texture to add.
+	/// @return The index of the texture if it is successful, or -1 if it is invalid.
+	int AddTexture(const Texture& texture);
+	/// Get number of textures in use by the decorator.
+	int GetNumTextures() const;
 	/// Returns one of the decorator's previously loaded textures.
 	/// Returns one of the decorator's previously loaded textures.
 	/// @param[in] index The index of the desired texture.
 	/// @param[in] index The index of the desired texture.
-	/// @return The texture at the appropriate index, or NULL if the index was invalid.
+	/// @return The texture at the appropriate index, or nullptr if the index was invalid.
 	const Texture* GetTexture(int index = 0) const;
 	const Texture* GetTexture(int index = 0) const;
 
 
 private:
 private:
-	DecoratorInstancer* instancer;
-
-	// The z-index of this decorator, used to resolve render order when multiple decorators are rendered
-	// simultaneously on the same element.
-	float z_index;
-	// The maximum specificity of the properties used to define the decorator.
-	int specificity;
-
 	// Stores a list of textures in use by this decorator.
 	// Stores a list of textures in use by this decorator.
-	std::vector< Texture > textures;
-
-	friend class Factory;
+	// Optimized for the common case of a single texture.
+	Texture first_texture;
+	std::vector< Texture > additional_textures;
 };
 };
 
 
 }
 }

+ 23 - 16
Include/RmlUi/Core/DecoratorInstancer.h

@@ -29,7 +29,6 @@
 #ifndef RMLUICOREDECORATORINSTANCER_H
 #ifndef RMLUICOREDECORATORINSTANCER_H
 #define RMLUICOREDECORATORINSTANCER_H
 #define RMLUICOREDECORATORINSTANCER_H
 
 
-#include "ReferenceCountable.h"
 #include "Header.h"
 #include "Header.h"
 #include "PropertyDictionary.h"
 #include "PropertyDictionary.h"
 #include "PropertySpecification.h"
 #include "PropertySpecification.h"
@@ -37,7 +36,11 @@
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
+struct Sprite;
+class StyleSheet;
 class Decorator;
 class Decorator;
+class DecoratorInstancerInterface;
+
 
 
 /**
 /**
 	An element instancer provides a method for allocating and deallocating decorators.
 	An element instancer provides a method for allocating and deallocating decorators.
@@ -48,23 +51,18 @@ class Decorator;
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API DecoratorInstancer : public ReferenceCountable
+class RMLUICORE_API DecoratorInstancer
 {
 {
 public:
 public:
 	DecoratorInstancer();
 	DecoratorInstancer();
 	virtual ~DecoratorInstancer();
 	virtual ~DecoratorInstancer();
 
 
 	/// Instances a decorator given the property tag and attributes from the RCSS file.
 	/// Instances a decorator given the property tag and attributes from the RCSS file.
-	/// @param[in] name The type of decorator desired. For example, "background-decorator: simple;" is declared as type "simple".
+	/// @param[in] name The type of decorator desired. For example, "decorator: simple(...);" is declared as type "simple".
 	/// @param[in] properties All RCSS properties associated with the decorator.
 	/// @param[in] properties All RCSS properties associated with the decorator.
-	/// @return The decorator if it was instanced successfully, NULL if an error occured.
-	virtual Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties) = 0;
-	/// Releases the given decorator.
-	/// @param[in] decorator Decorator to release. This is guaranteed to have been constructed by this instancer.
-	virtual void ReleaseDecorator(Decorator* decorator) = 0;
-
-	/// Releases the instancer.
-	virtual void Release() = 0;
+	/// @param[in] interface An interface for querying the active style sheet.
+	/// @return A shared_ptr to the decorator if it was instanced successfully.
+	virtual SharedPtr<Decorator> InstanceDecorator(const String& name, const PropertyDictionary& properties, const DecoratorInstancerInterface& interface) = 0;
 
 
 	/// Returns the property specification associated with the instancer.
 	/// Returns the property specification associated with the instancer.
 	const PropertySpecification& GetPropertySpecification() const;
 	const PropertySpecification& GetPropertySpecification() const;
@@ -75,20 +73,29 @@ protected:
 	/// @param[in] default_value The default value to be used.
 	/// @param[in] default_value The default value to be used.
 	/// @return The new property definition, ready to have parsers attached.
 	/// @return The new property definition, ready to have parsers attached.
 	PropertyDefinition& RegisterProperty(const String& property_name, const String& default_value);
 	PropertyDefinition& RegisterProperty(const String& property_name, const String& default_value);
-	/// Registers a shorthand property definition.
+	/// Registers a shorthand property definition. Specify a shorthand name of 'decorator' to parse anonymous decorators.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] type The type of shorthand to declare.
 	/// @param[in] type The type of shorthand to declare.
 	/// @param True if all the property names exist, false otherwise.
 	/// @param True if all the property names exist, false otherwise.
-	bool RegisterShorthand(const String& shorthand_name, const String& property_names, PropertySpecification::ShorthandType type = PropertySpecification::AUTO);
-
-	// Releases the instancer.
-	virtual void OnReferenceDeactivate();
+	ShorthandId RegisterShorthand(const String& shorthand_name, const String& property_names, ShorthandType type);
 
 
 private:
 private:
 	PropertySpecification properties;
 	PropertySpecification properties;
 };
 };
 
 
+
+class RMLUICORE_API DecoratorInstancerInterface {
+public:
+	DecoratorInstancerInterface(const StyleSheet& style_sheet) : style_sheet(style_sheet) {}
+
+	/// Get a sprite from any @spritesheet in the style sheet the decorator is being instanced on.
+	const Sprite* GetSprite(const String& name) const;
+
+private:
+	const StyleSheet& style_sheet;
+};
+
 }
 }
 }
 }
 
 

+ 22 - 109
Include/RmlUi/Core/Dictionary.h

@@ -35,118 +35,31 @@
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
-/**
-	A dictionay is a container of variants.
-	It uses a hash table to maintain a string key to variant mapping.
-
-	@author Lloyd Weehuizen
- */
-
-class RMLUICORE_API Dictionary
+inline Variant* GetIf(Dictionary& dictionary, const String& key)
 {
 {
-public:
-	Dictionary();
-	Dictionary(const Dictionary &dict);
-	~Dictionary();
-
-	/// Store an item in the dictionary
-	void Set(const String& key, const Variant &value);
-  
-	/// Templated set eases setting of values
-	template <typename T>
-	inline void Set(const String& key, const T& value);
-	
-	/// Get an item from the dictionary
-	Variant* Get(const String& key) const;
-	Variant* operator[](const String& key) const;
-	
-	/// Get a value from the dictionary, if it doesn't exist
-	/// use the supplied default value
-	template <typename T>
-	inline T Get(const String& key, const T& default_val) const;
-	
-	/// Get a value from the dictionary, returns if the
-	/// value was found or not.
-	template <typename T>
-	inline bool GetInto(const String& key, T& value) const;
-
-	/// Remove an item from the dictionary
-	bool Remove(const String& key);
-
-	/// Iterate through a dictionary
-	bool Iterate(int &pos, String& key, Variant* &value) const;
-	template <typename T>
-	bool Iterate(int &pos, String& key, T& value) const;
-
-	/// Reserve the specified number of entries in the dictionary
-	bool Reserve(int size);
-
-	/// Empty the dictionary
-	void Clear();
-
-	/// Is the dictionary empty?
-	bool IsEmpty() const;
-
-	/// Items in the dict
-	int Size() const;
-
-	/// Merges another dictionary into this one. Any existing values stored against similar keys will be updated.
-	void Merge(const Dictionary& dict);
-
-	// Copy
-	void operator=(const Dictionary &dict);
-
-private:
-	unsigned int num_full;  // Active + # Dummy
-	unsigned int num_used;  // Active
-
-	/* DICTIONARY_MINSIZE is the minimum size of a dictionary.  This many slots are
-	 * allocated directly in the dict object (in the small_table member).
-	 * It must be a power of 2, and at least 4.  8 allows dicts with no more
-	 * than 5 active entries to live in small_table (and so avoid an
-	 * additional malloc); instrumentation suggested this suffices for the
-	 * majority of dicts (consisting mostly of usually-small instance dicts and
-	 * usually-small dicts created to pass keyword arguments).
-	 */
-	static const int DICTIONARY_MINSIZE = 8;
-
-	// Individual entry in a dictionary
-	struct DictionaryEntry
-	{
-		DictionaryEntry() : hash(0) {}
-		Hash hash;		// Cached hash of key
-		String key;		// key in plain text
-		Variant value;	// Value for this entry
-	};
-  
-  /* The table contains mask + 1 slots, and that's a power of 2.
-	 * We store the mask instead of the size because the mask is more
-	 * frequently needed.
-	 */
-	unsigned int mask;
-
-	// Small dictionaries just use this, saves mallocs for small tables
-	DictionaryEntry small_table[DICTIONARY_MINSIZE];
-
-	/// Pointer to table in use, may be malloc'd or may point to smallTable
-	DictionaryEntry* table;
-
-	/// Insert an item
-	void Insert(const String& key, Hash hash, const Variant& value);
-
-	/// Retrieve an item
-	DictionaryEntry* Retrieve(const String& key, Hash hash) const;
-
-	/// Reset to small dictionary
-	void ResetToMinimumSize();  
-
-	// Copy another dict
-	void Copy(const Dictionary &dict);
-};
-
+	auto it = dictionary.find(key);
+	if (it != dictionary.end())
+		return &(it->second);
+	return nullptr;
+}
+inline const Variant* GetIf(const Dictionary& dictionary, const String& key)
+{
+	auto it = dictionary.find(key);
+	if (it != dictionary.end())
+		return &(it->second);
+	return nullptr;
 }
 }
+template<typename T>
+inline T Get(const Dictionary& dictionary, const String& key, const T& default_value)
+{
+	T result = default_value;
+	auto it = dictionary.find(key);
+	if (it != dictionary.end())
+		it->second.GetInto(result);
+	return result;
 }
 }
 
 
-#include "Dictionary.inl"
+}
+}
 
 
 #endif
 #endif

+ 0 - 70
Include/RmlUi/Core/Dictionary.inl

@@ -1,70 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-namespace Rml {
-namespace Core {
-
-template< typename T >
-inline void Dictionary::Set(const String& key, const T& value)
-{
-	Set(key, Variant(value));
-}
-
-template< typename T >
-inline T Dictionary::Get(const String& key, const T& default_value) const
-{
-	T value;
-	if (!GetInto(key, value))
-		return default_value;
-
-	return value;
-}
-
-
-template <typename T>
-inline bool Dictionary::GetInto(const String& key, T& value) const
-{
-	Variant* variant = Get(key);
-	if (!variant)
-		return false;
-
-	return variant->GetInto<T>(value);
-}
-
-template <typename T>
-inline bool Dictionary::Iterate(int& pos, String& key, T& value) const
-{
-	Variant* variant;
-	bool iterate = Iterate(pos, key, variant);
-	if (iterate)
-		variant->GetInto(value);
-	return iterate;
-}
-
-}
-}

+ 138 - 185
Include/RmlUi/Core/Element.h

@@ -29,15 +29,14 @@
 #ifndef RMLUICOREELEMENT_H
 #ifndef RMLUICOREELEMENT_H
 #define RMLUICOREELEMENT_H
 #define RMLUICOREELEMENT_H
 
 
-#include "ReferenceCountable.h"
 #include "ScriptInterface.h"
 #include "ScriptInterface.h"
 #include "Header.h"
 #include "Header.h"
 #include "Box.h"
 #include "Box.h"
+#include "ComputedValues.h"
 #include "Event.h"
 #include "Event.h"
 #include "Property.h"
 #include "Property.h"
 #include "Types.h"
 #include "Types.h"
 #include "Transform.h"
 #include "Transform.h"
-#include "TransformState.h"
 #include "Tween.h"
 #include "Tween.h"
 
 
 #include <memory>
 #include <memory>
@@ -47,7 +46,6 @@ namespace Core {
 
 
 class Context;
 class Context;
 class Decorator;
 class Decorator;
-class Dictionary;
 class ElementInstancer;
 class ElementInstancer;
 class EventDispatcher;
 class EventDispatcher;
 class EventListener;
 class EventListener;
@@ -58,10 +56,13 @@ class ElementDefinition;
 class ElementDocument;
 class ElementDocument;
 class ElementScroll;
 class ElementScroll;
 class ElementStyle;
 class ElementStyle;
-class FontFaceHandle;
+class PropertiesIteratorView;
+class FontFaceHandleDefault;
 class PropertyDictionary;
 class PropertyDictionary;
 class RenderInterface;
 class RenderInterface;
+class TransformState;
 class StyleSheet;
 class StyleSheet;
+struct ElementMeta;
 
 
 /**
 /**
 	A generic element in the DOM tree.
 	A generic element in the DOM tree.
@@ -78,11 +79,8 @@ public:
 	Element(const String& tag);
 	Element(const String& tag);
 	virtual ~Element();
 	virtual ~Element();
 
 
-	void Update();
-	void Render();
-
 	/// Clones this element, returning a new, unparented element.
 	/// Clones this element, returning a new, unparented element.
-	Element* Clone() const;
+	ElementPtr Clone() const;
 
 
 	/** @name Classes
 	/** @name Classes
 	 */
 	 */
@@ -103,18 +101,18 @@ public:
 	String GetClassNames() const;
 	String GetClassNames() const;
 	//@}
 	//@}
 
 
-	/// Returns the active style sheet for this element. This may be NULL.
+	/// Returns the active style sheet for this element. This may be nullptr.
 	/// @return The element's style sheet.
 	/// @return The element's style sheet.
-	virtual StyleSheet* GetStyleSheet() const;
+	virtual const SharedPtr<StyleSheet>& GetStyleSheet() const;
 
 
-	/// Returns the element's definition, updating if necessary.
+	/// Returns the element's definition.
 	/// @return The element's definition.
 	/// @return The element's definition.
 	const ElementDefinition* GetDefinition();
 	const ElementDefinition* GetDefinition();
 
 
 	/// Fills a string with the full address of this element.
 	/// Fills a string with the full address of this element.
 	/// @param[in] include_pseudo_classes True if the address is to include the pseudo-classes of the leaf element.
 	/// @param[in] include_pseudo_classes True if the address is to include the pseudo-classes of the leaf element.
 	/// @return The address of the element, including its full parentage.
 	/// @return The address of the element, including its full parentage.
-	String GetAddress(bool include_pseudo_classes = false) const;
+	String GetAddress(bool include_pseudo_classes = false, bool include_parents = true) const;
 
 
 	/// Sets the position of this element, as a two-dimensional offset from another element.
 	/// Sets the position of this element, as a two-dimensional offset from another element.
 	/// @param[in] offset The offset (in pixels) of our primary box's top-left border corner from our offset parent's top-left border corner.
 	/// @param[in] offset The offset (in pixels) of our primary box's top-left border corner from our offset parent's top-left border corner.
@@ -150,10 +148,13 @@ public:
 	/// Adds a box to the end of the list describing this element's geometry.
 	/// Adds a box to the end of the list describing this element's geometry.
 	/// @param[in] box The auxiliary box for the element.
 	/// @param[in] box The auxiliary box for the element.
 	void AddBox(const Box& box);
 	void AddBox(const Box& box);
+	/// Returns the main box describing the size of the element.
+	/// @return The box.
+	const Box& GetBox();
 	/// Returns one of the boxes describing the size of the element.
 	/// Returns one of the boxes describing the size of the element.
-	/// @param[in] index The index of the desired box. If this is outside of the bounds of the element's list of boxes, it will be clamped.
+	/// @param[in] index The index of the desired box, with 0 being the main box. If outside of bounds, the main box will be returned.
 	/// @return The requested box.
 	/// @return The requested box.
-	const Box& GetBox(int index = 0);
+	const Box& GetBox(int index);
 	/// Returns the number of boxes making up this element's geometry.
 	/// Returns the number of boxes making up this element's geometry.
 	/// @return the number of boxes making up this element's geometry.
 	/// @return the number of boxes making up this element's geometry.
 	int GetNumBoxes();
 	int GetNumBoxes();
@@ -181,7 +182,7 @@ public:
 
 
 	/// Returns the element's font face handle.
 	/// Returns the element's font face handle.
 	/// @return The element's font face handle.
 	/// @return The element's font face handle.
-	FontFaceHandle* GetFontFaceHandle() const;
+	FontFaceHandle GetFontFaceHandle() const;
 
 
 	/** @name Properties
 	/** @name Properties
 	 */
 	 */
@@ -195,105 +196,61 @@ public:
 	/// @param[in] name The name of the new property.
 	/// @param[in] name The name of the new property.
 	/// @param[in] property The parsed property to set.
 	/// @param[in] property The parsed property to set.
 	/// @return True if the property was set successfully, false otherwise.
 	/// @return True if the property was set successfully, false otherwise.
-	bool SetProperty(const String& name, const Property& property);
-	/// Removes a local property override on the element; its value will revert to that defined in
-	/// the style sheet.
+	bool SetProperty(PropertyId id, const Property& property);
+	/// Removes a local property override on the element; its value will revert to that defined in the style sheet.
 	/// @param[in] name The name of the local property definition to remove.
 	/// @param[in] name The name of the local property definition to remove.
 	void RemoveProperty(const String& name);
 	void RemoveProperty(const String& name);
-	/// Returns one of this element's properties. If this element is not defined this property, or a parent cannot
-	/// be found that we can inherit the property from, the default value will be returned.
+	void RemoveProperty(PropertyId id);
+	/// Returns one of this element's properties. If the property is not defined for this element and not inherited 
+	/// from an ancestor, the default value will be returned.
 	/// @param[in] name The name of the property to fetch the value for.
 	/// @param[in] name The name of the property to fetch the value for.
-	/// @return The value of this property for this element, or NULL if no property exists with the given name.
+	/// @return The value of this property for this element, or nullptr if no property exists with the given name.
 	const Property* GetProperty(const String& name);		
 	const Property* GetProperty(const String& name);		
+	const Property* GetProperty(PropertyId id);		
 	/// Returns the values of one of this element's properties.		
 	/// Returns the values of one of this element's properties.		
 	/// @param[in] name The name of the property to get.
 	/// @param[in] name The name of the property to get.
 	/// @return The value of this property.
 	/// @return The value of this property.
 	template < typename T >
 	template < typename T >
 	T GetProperty(const String& name);
 	T GetProperty(const String& name);
-	/// Returns one of this element's properties. If this element is not defined this property, NULL will be
+	/// Returns one of this element's properties. If this element is not defined this property, nullptr will be
 	/// returned.
 	/// returned.
 	/// @param[in] name The name of the property to fetch the value for.
 	/// @param[in] name The name of the property to fetch the value for.
-	/// @return The value of this property for this element, or NULL if this property has not been explicitly defined for this element.
+	/// @return The value of this property for this element, or nullptr if this property has not been explicitly defined for this element.
 	const Property* GetLocalProperty(const String& name);
 	const Property* GetLocalProperty(const String& name);
-	/// Returns the local properties, excluding any properties from local class.
-	/// @return The local properties for this element, or NULL if no properties defined
-	const PropertyMap* GetLocalProperties();
-	/// Resolves one of this element's properties. If the value is a number or px, this is returned. Angles are returned as radians.
-	/// Precentages are resolved based on the second argument (the base value).
-	/// @param[in] name The name of the property to resolve the value for.
-	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
-	/// @return The value of this property for this element.
-	float ResolveProperty(const String& name, float base_value);
-	/// Resolves one of this element's non-inherited properties. If the value is a number or px, this is returned. Angles are returned as radians.
-	/// Precentages are resolved based on the second argument (the base value).
+	const Property* GetLocalProperty(PropertyId id);
+	/// Returns the local style properties, excluding any properties from local class.
+	/// @return The local properties for this element, or nullptr if no properties defined
+	const PropertyMap& GetLocalStyleProperties();
+
+	/// Resolves a property with units of number, percentage, length, or angle to their canonical unit (unit-less, 'px', or 'rad').
+	/// Numbers and percentages are scaled by the base value and returned.
+	/// @param[in] property The property to resolve the value for.
+	/// @param[in] base_value The value that is scaled by the number or percentage value, if applicable.
+	/// @return The resolved value in their canonical unit, or zero if it could not be resolved.
+	float ResolveNumericProperty(const Property *property, float base_value);
+	/// Resolves a property with units of number, percentage, length, or angle to their canonical unit (unit-less, 'px', or 'rad').
+	/// Numbers and percentages are scaled according to the relative target of the property definition.
 	/// @param[in] name The property to resolve the value for.
 	/// @param[in] name The property to resolve the value for.
-	/// @param[in] base_value The value that is scaled by the percentage value, if it is a percentage.
-	/// @return The value of this property for this element.
-	float ResolveProperty(const Property *property, float base_value);
-
-	/// Returns 'top', 'bottom', 'left' and 'right' properties from element's style or local cache.
-	void GetOffsetProperties(const Property **top, const Property **bottom, const Property **left, const Property **right );
-	/// Returns 'border-width' properties from element's style or local cache.
-	void GetBorderWidthProperties(const Property **border_top_width, const Property **border_bottom_width, const Property **border_left_width, const Property **border_right_width);
-	/// Returns 'margin' properties from element's style or local cache.
-	void GetMarginProperties(const Property **margin_top, const Property **margin_bottom, const Property **margin_left, const Property **margin_right);
-	/// Returns 'padding' properties from element's style or local cache.
-	void GetPaddingProperties(const Property **padding_top, const Property **padding_bottom, const Property **padding_left, const Property **padding_right);
-	/// Returns 'width' and 'height' properties from element's style or local cache.
-	void GetDimensionProperties(const Property **width, const Property **height);
-	/// Returns local 'width' and 'height' properties from element's style or local cache,
-	/// ignoring default values.
-	void GetLocalDimensionProperties(const Property **width, const Property **height);
+	/// @return The resolved value in their canonical unit, or zero if it could not be resolved.
+	float ResolveNumericProperty(const String& property_name);
+
 	/// Returns the size of the containing block. Often percentages are scaled relative to this.
 	/// Returns the size of the containing block. Often percentages are scaled relative to this.
 	Vector2f GetContainingBlock();
 	Vector2f GetContainingBlock();
-	/// Returns 'overflow' properties' values from element's style or local cache.
-	void GetOverflow(int *overflow_x, int *overflow_y);
 	/// Returns 'position' property value from element's style or local cache.
 	/// Returns 'position' property value from element's style or local cache.
-	int GetPosition();
+	Style::Position GetPosition();
 	/// Returns 'float' property value from element's style or local cache.
 	/// Returns 'float' property value from element's style or local cache.
-	int GetFloat();
+	Style::Float GetFloat();
 	/// Returns 'display' property value from element's style or local cache.
 	/// Returns 'display' property value from element's style or local cache.
-	int GetDisplay();
-	/// Returns 'white-space' property value from element's style or local cache.
-	int GetWhitespace();
-	/// Returns 'pointer-events' property value from element's style or local cache.
-	int GetPointerEvents();
-
+	Style::Display GetDisplay();
 	/// Returns 'line-height' property value from element's style or local cache.
 	/// Returns 'line-height' property value from element's style or local cache.
-	const Property *GetLineHeightProperty();
-	/// Returns 'text-align' property value from element's style or local cache.
-	int GetTextAlign();
-	/// Returns 'text-transform' property value from element's style or local cache.
-	int GetTextTransform();
-	/// Returns 'vertical-align' property value from element's style or local cache.
-	const Property *GetVerticalAlignProperty();
-
-	/// Returns 'perspective' property value from element's style or local cache.
-	const Property *GetPerspective();
-	/// Returns 'perspective-origin-x' property value from element's style or local cache.
-	const Property *GetPerspectiveOriginX();
-	/// Returns 'perspective-origin-y' property value from element's style or local cache.
-	const Property *GetPerspectiveOriginY();
-	/// Returns 'transform' property value from element's style or local cache.
-	const Property *GetTransform();
-	/// Returns 'transform-origin-x' property value from element's style or local cache.
-	const Property *GetTransformOriginX();
-	/// Returns 'transform-origin-y' property value from element's style or local cache.
-	const Property *GetTransformOriginY();
-	/// Returns 'transform-origin-z' property value from element's style or local cache.
-	const Property *GetTransformOriginZ();
+	float GetLineHeight();
+
 	/// Returns this element's TransformState
 	/// Returns this element's TransformState
 	const TransformState *GetTransformState() const noexcept;
 	const TransformState *GetTransformState() const noexcept;
-	/// Returns the TransformStates that are effective for this element.
-	void GetEffectiveTransformState(
-		const TransformState **local_perspective,
-		const TransformState **perspective,
-		const TransformState **transform
-	) noexcept;
 	/// Project a 2D point in pixel coordinates onto the element's plane.
 	/// Project a 2D point in pixel coordinates onto the element's plane.
-	/// @param[in] point The point to project.
-	/// @return The projected coordinates.
-	const Vector2f Project(const Vector2f& point) noexcept;
+	/// @param[in-out] point The point to project in, and the resulting projected point out.
+	/// @return True on success, false if transformation matrix is singular.
+	bool Project(Vector2f& point) const noexcept;
 
 
 	/// Start an animation of the given property on this element.
 	/// Start an animation of the given property on this element.
 	/// If an animation of the same property name exists, it will be replaced.
 	/// If an animation of the same property name exists, it will be replaced.
@@ -306,13 +263,10 @@ public:
 	/// @return True if a new animation key was added.
 	/// @return True if a new animation key was added.
 	bool AddAnimationKey(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{});
 	bool AddAnimationKey(const String& property_name, const Property& target_value, float duration, Tween tween = Tween{});
 	
 	
-	/// Iterates over the properties defined on this element.
-	/// @param[inout] index Index of the property to fetch. This is incremented to the next valid index after the fetch. Indices are not necessarily incremental.
-	/// @param[out] pseudo_classes The pseudo-classes the property is defined by.
-	/// @param[out] name The name of the property at the specified index.
-	/// @param[out] property The property at the specified index.
-	/// @return True if a property was successfully fetched.
-	bool IterateProperties(int& index, PseudoClassList& pseudo_classes, String& name, const Property*& property) const;
+	/// Iterator for the local (non-inherited) properties defined on this element.
+	/// @warning Modifying the element's properties or classes invalidates the iterator.
+	/// @return Iterator to the first property defined on this element.
+	PropertiesIteratorView IterateLocalProperties() const;
 	///@}
 	///@}
 
 
 	/** @name Pseudo-classes
 	/** @name Pseudo-classes
@@ -345,8 +299,8 @@ public:
 	void SetAttribute(const String& name, const T& value);
 	void SetAttribute(const String& name, const T& value);
 	/// Gets the specified attribute.
 	/// Gets the specified attribute.
 	/// @param[in] name Name of the attribute to retrieve.
 	/// @param[in] name Name of the attribute to retrieve.
-	/// @return A variant representing the attribute, or NULL if the attribute doesn't exist.
-	Variant* GetAttribute(const String& name) const;
+	/// @return A variant representing the attribute, or nullptr if the attribute doesn't exist.
+	Variant* GetAttribute(const String& name);
 	/// Gets the specified attribute, with default value.
 	/// Gets the specified attribute, with default value.
 	/// @param[in] name Name of the attribute to retrieve.
 	/// @param[in] name Name of the attribute to retrieve.
 	/// @param[in] default_value Value to return if the attribute doesn't exist.
 	/// @param[in] default_value Value to return if the attribute doesn't exist.
@@ -355,46 +309,28 @@ public:
 	/// Checks if the element has a certain attribute.
 	/// Checks if the element has a certain attribute.
 	/// @param[in] name The name of the attribute to check for.
 	/// @param[in] name The name of the attribute to check for.
 	/// @return True if the element has the given attribute, false if not.
 	/// @return True if the element has the given attribute, false if not.
-	bool HasAttribute(const String& name);
+	bool HasAttribute(const String& name) const;
 	/// Removes the attribute from the element.
 	/// Removes the attribute from the element.
 	/// @param[in] name Name of the attribute.
 	/// @param[in] name Name of the attribute.
 	void RemoveAttribute(const String& name);
 	void RemoveAttribute(const String& name);
 	/// Set a group of attributes.
 	/// Set a group of attributes.
 	/// @param[in] attributes Attributes to set.
 	/// @param[in] attributes Attributes to set.
-	void SetAttributes(const ElementAttributes* attributes);
-	/// Iterates over the attributes.
-	/// @param[inout] index Index to fetch. This is incremented after the fetch.
-	/// @param[out] name Name of the attribute at the specified index
-	/// @param[out] value Value of the attribute at the specified index.
-	/// @return True if an attribute was successfully fetched.
-	template< typename T >
-	bool IterateAttributes(int& index, String& name, T& value) const;
+	void SetAttributes(const ElementAttributes& attributes);
+	/// Get the attributes of the element.
+	/// @return The attributes
+	const ElementAttributes& GetAttributes() const { return attributes; }
 	/// Returns the number of attributes on the element.
 	/// Returns the number of attributes on the element.
 	/// @return The number of attributes on the element.
 	/// @return The number of attributes on the element.
 	int GetNumAttributes() const;
 	int GetNumAttributes() const;
 	//@}
 	//@}
 
 
-	/** @name Decorators
-	 */
-	//@{
-	/// Iterates over all decorators attached to the element. Note that all decorators are iterated
-	/// over, not just active ones.
-	/// @param[inout] index Index to fetch. This is incremented after the fetch.
-	/// @param[out] pseudo_classes The pseudo-classes the decorator required to be active before it renders.
-	/// @param[out] name The name of the decorator at the specified index.
-	/// @param[out] decorator The decorator at the specified index.
-	/// @param[out] decorator_data This element's handle to any data is has stored against the decorator.
-	/// @return True if a decorator was successfully fetched, false if not.
-	bool IterateDecorators(int& index, PseudoClassList& pseudo_classes, String& name, Decorator*& decorator, DecoratorDataHandle& decorator_data);
-	//@}
-
 	/// Gets the outer-most focus element down the tree from this node.
 	/// Gets the outer-most focus element down the tree from this node.
 	/// @return Outer-most focus element.
 	/// @return Outer-most focus element.
 	Element* GetFocusLeafNode();
 	Element* GetFocusLeafNode();
 
 
 	/// Returns the element's context.
 	/// Returns the element's context.
 	/// @return The context this element's document exists within.
 	/// @return The context this element's document exists within.
-	Context* GetContext();
+	Context* GetContext() const;
 
 
 	/** @name DOM Properties
 	/** @name DOM Properties
 	 */
 	 */
@@ -472,28 +408,28 @@ public:
 
 
 	/// Gets the object representing the declarations of an element's style attributes.
 	/// Gets the object representing the declarations of an element's style attributes.
 	/// @return The element's style.
 	/// @return The element's style.
-	ElementStyle* GetStyle();
+	ElementStyle* GetStyle() const;
 
 
 	/// Gets the document this element belongs to.
 	/// Gets the document this element belongs to.
 	/// @return This element's document.
 	/// @return This element's document.
-	virtual ElementDocument* GetOwnerDocument();
+	ElementDocument* GetOwnerDocument() const;
 
 
 	/// Gets this element's parent node.
 	/// Gets this element's parent node.
 	/// @return This element's parent.
 	/// @return This element's parent.
 	Element* GetParentNode() const;
 	Element* GetParentNode() const;
 
 
 	/// Gets the element immediately following this one in the tree.
 	/// Gets the element immediately following this one in the tree.
-	/// @return This element's next sibling element, or NULL if there is no sibling element.
+	/// @return This element's next sibling element, or nullptr if there is no sibling element.
 	Element* GetNextSibling() const;
 	Element* GetNextSibling() const;
 	/// Gets the element immediately preceding this one in the tree.
 	/// Gets the element immediately preceding this one in the tree.
-	/// @return This element's previous sibling element, or NULL if there is no sibling element.
+	/// @return This element's previous sibling element, or nullptr if there is no sibling element.
 	Element* GetPreviousSibling() const;
 	Element* GetPreviousSibling() const;
 
 
 	/// Returns the first child of this element.
 	/// Returns the first child of this element.
-	/// @return This element's first child, or NULL if it contains no children.
+	/// @return This element's first child, or nullptr if it contains no children.
 	Element* GetFirstChild() const;
 	Element* GetFirstChild() const;
 	/// Gets the last child of this element.
 	/// Gets the last child of this element.
-	/// @return This element's last child, or NULL if it contains no children.
+	/// @return This element's last child, or nullptr if it contains no children.
 	Element* GetLastChild() const;
 	Element* GetLastChild() const;
 	/// Get the child element at the given index.
 	/// Get the child element at the given index.
 	/// @param[in] index Index of child to get.
 	/// @param[in] index Index of child to get.
@@ -533,17 +469,24 @@ public:
 	/// @param[in] listener The listener object to be attached.
 	/// @param[in] listener The listener object to be attached.
 	/// @param[in] in_capture_phase True to attach in the capture phase, false in bubble phase.
 	/// @param[in] in_capture_phase True to attach in the capture phase, false in bubble phase.
 	void AddEventListener(const String& event, EventListener* listener, bool in_capture_phase = false);
 	void AddEventListener(const String& event, EventListener* listener, bool in_capture_phase = false);
+	/// Adds an event listener to this element by id.
+	void AddEventListener(EventId id, EventListener* listener, bool in_capture_phase = false);
 	/// Removes an event listener from this element.
 	/// Removes an event listener from this element.
 	/// @param[in] event Event to detach from.
 	/// @param[in] event Event to detach from.
 	/// @param[in] listener The listener object to be detached.
 	/// @param[in] listener The listener object to be detached.
 	/// @param[in] in_capture_phase True to detach from the capture phase, false from the bubble phase.
 	/// @param[in] in_capture_phase True to detach from the capture phase, false from the bubble phase.
 	void RemoveEventListener(const String& event, EventListener* listener, bool in_capture_phase = false);
 	void RemoveEventListener(const String& event, EventListener* listener, bool in_capture_phase = false);
+	/// Removes an event listener from this element by id.
+	void RemoveEventListener(EventId id, EventListener* listener, bool in_capture_phase = false);
 	/// Sends an event to this element.
 	/// Sends an event to this element.
-	/// @param[in] event Name of the event in string form.
+	/// @param[in] type Event type in string form.
 	/// @param[in] parameters The event parameters.
 	/// @param[in] parameters The event parameters.
-	/// @param[in] interruptible True if the propagation of the event be stopped.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
 	/// @return True if the event was not consumed (ie, was prevented from propagating by an element), false if it was.
-	bool DispatchEvent(const String& event, const Dictionary& parameters, bool interruptible = false);
+	bool DispatchEvent(const String& type, const Dictionary& parameters);
+	/// Sends an event to this element, overriding the default behavior for the given event type.
+	bool DispatchEvent(const String& type, const Dictionary& parameters, bool interruptible, bool bubbles = true);
+	/// Sends an event to this element by event id.
+	bool DispatchEvent(EventId id, const Dictionary& parameters);
 
 
 	/// Scrolls the parent element's contents so that this element is visible.
 	/// Scrolls the parent element's contents so that this element is visible.
 	/// @param[in] align_with_top If true, the element will align itself to the top of the parent element's window. If false, the element will be aligned to the bottom of the parent element's window.
 	/// @param[in] align_with_top If true, the element will align itself to the top of the parent element's window. If false, the element will be aligned to the bottom of the parent element's window.
@@ -552,28 +495,28 @@ public:
 	/// Append a child to this element.
 	/// Append a child to this element.
 	/// @param[in] element The element to append as a child.
 	/// @param[in] element The element to append as a child.
 	/// @param[in] dom_element True if the element is to be part of the DOM, false otherwise. Only set this to false if you know what you're doing!
 	/// @param[in] dom_element True if the element is to be part of the DOM, false otherwise. Only set this to false if you know what you're doing!
-	void AppendChild(Element* element, bool dom_element = true);
+	Element* AppendChild(ElementPtr element, bool dom_element = true);
 	/// Adds a child to this element, directly after the adjacent element. The new element inherits the DOM/non-DOM
 	/// Adds a child to this element, directly after the adjacent element. The new element inherits the DOM/non-DOM
 	/// status from the adjacent element.
 	/// status from the adjacent element.
 	/// @param[in] element Element to insert into the this element.
 	/// @param[in] element Element to insert into the this element.
 	/// @param[in] adjacent_element The element to insert directly before.
 	/// @param[in] adjacent_element The element to insert directly before.
-	void InsertBefore(Element* element, Element* adjacent_element);
+	Element* InsertBefore(ElementPtr element, Element* adjacent_element);
 	/// Replaces the second node with the first node.
 	/// Replaces the second node with the first node.
 	/// @param[in] inserted_element The element that will be inserted and replace the other element.
 	/// @param[in] inserted_element The element that will be inserted and replace the other element.
 	/// @param[in] replaced_element The existing element that will be replaced. If this doesn't exist, inserted_element will be appended.
 	/// @param[in] replaced_element The existing element that will be replaced. If this doesn't exist, inserted_element will be appended.
-	/// @return True if the replaced_element was found, false otherwise.
-	bool ReplaceChild(Element* inserted_element, Element* replaced_element);
+	/// @return A unique pointer to the replaced element if found, discard the result to immediately destroy.
+	ElementPtr ReplaceChild(ElementPtr inserted_element, Element* replaced_element);
 	/// Remove a child element from this element.
 	/// Remove a child element from this element.
 	/// @param[in] The element to remove.
 	/// @param[in] The element to remove.
-	/// @returns True if the element was found and removed.
-	bool RemoveChild(Element* element);
+	/// @returns A unique pointer to the element if found, discard the result to immediately destroy.
+	ElementPtr RemoveChild(Element* element);
 	/// Returns whether or not this element has any DOM children.
 	/// Returns whether or not this element has any DOM children.
 	/// @return True if the element has at least one DOM child, false otherwise.
 	/// @return True if the element has at least one DOM child, false otherwise.
 	bool HasChildNodes() const;
 	bool HasChildNodes() const;
 
 
 	/// Get a child element by its ID.
 	/// Get a child element by its ID.
 	/// @param[in] id Id of the the child element
 	/// @param[in] id Id of the the child element
-	/// @return The child of this element with the given ID, or NULL if no such child exists.
+	/// @return The child of this element with the given ID, or nullptr if no such child exists.
 	Element* GetElementById(const String& id);
 	Element* GetElementById(const String& id);
 	/// Get all descendant elements with the given tag.
 	/// Get all descendant elements with the given tag.
 	/// @param[out] elements Resulting elements.
 	/// @param[out] elements Resulting elements.
@@ -622,38 +565,45 @@ public:
 	/// @param[in] instancer Instancer to set on this element.
 	/// @param[in] instancer Instancer to set on this element.
 	void SetInstancer(ElementInstancer* instancer);
 	void SetInstancer(ElementInstancer* instancer);
 
 
-	/// Called for every event sent to this element or one of its descendants.
+	/// Called when an emitted event propagates to this element, for event types with default actions.
+	/// Note: See 'EventSpecification' for the events that call this function and during which phase.
 	/// @param[in] event The event to process.
 	/// @param[in] event The event to process.
-	virtual void ProcessEvent(Event& event);
-	
-	/// Update the element's layout if required.
-	void UpdateLayout();
+	virtual void ProcessDefaultAction(Event& event);
+
+	/// Return the computed values of the element's properties. These values are updated as appropriate on every Context::Update.
+	const ComputedValues& GetComputedValues() const;
 
 
 protected:
 protected:
-	/// Forces the element to generate a local stacking context, regardless of the value of its z-index
-	/// property.
+	void Update(float dp_ratio);
+	void Render();
+
+	/// Updates definition, computed values, and runs OnPropertyChange on this element.
+	void UpdateProperties();
+
+	/// Forces the element to generate a local stacking context, regardless of the value of its z-index property.
 	void ForceLocalStackingContext();
 	void ForceLocalStackingContext();
 
 
 	/// Called during the update loop after children are updated.
 	/// Called during the update loop after children are updated.
 	virtual void OnUpdate();
 	virtual void OnUpdate();
 	/// Called during render after backgrounds, borders, decorators, but before children, are rendered.
 	/// Called during render after backgrounds, borders, decorators, but before children, are rendered.
 	virtual void OnRender();
 	virtual void OnRender();
-
+	/// Called during update if the element size has been changed.
+	virtual void OnResize();
 	/// Called during a layout operation, when the element is being positioned and sized.
 	/// Called during a layout operation, when the element is being positioned and sized.
 	virtual void OnLayout();
 	virtual void OnLayout();
 
 
 	/// Called when attributes on the element are changed.
 	/// Called when attributes on the element are changed.
-	/// @param[in] changed_attributes The attributes changed on the element.
-	virtual void OnAttributeChange(const AttributeNameList& changed_attributes);
+	/// @param[in] changed_attributes Dictionary of attributes changed on the element. Attribute value will be empty if it was unset.
+	virtual void OnAttributeChange(const ElementAttributes& changed_attributes);
 	/// Called when properties on the element are changed.
 	/// Called when properties on the element are changed.
 	/// @param[in] changed_properties The properties changed on the element.
 	/// @param[in] changed_properties The properties changed on the element.
-	virtual void OnPropertyChange(const PropertyNameList& changed_properties);
+	virtual void OnPropertyChange(const PropertyIdSet& changed_properties);
 
 
-	/// Called when a child node has been added somewhere in the hierarchy.
-	// @param[in] child The element that has been added. This may be this element.
+	/// Called when a child node has been added up to two levels below us in the hierarchy.
+	/// @param[in] child The element that has been added. This may be this element.
 	virtual void OnChildAdd(Element* child);
 	virtual void OnChildAdd(Element* child);
-	/// Called when a child node has been removed somewhere in the hierarchy.
-	// @param[in] child The element that has been removed. This may be this element.
+	/// Called when a child node has been removed up to two levels below us in the hierarchy.
+	/// @param[in] child The element that has been removed. This may be this element.
 	virtual void OnChildRemove(Element* child);
 	virtual void OnChildRemove(Element* child);
 
 
 	/// Forces a re-layout of this element, and any other elements required.
 	/// Forces a re-layout of this element, and any other elements required.
@@ -662,24 +612,17 @@ protected:
 	/// Returns true if the element has been marked as needing a re-layout.
 	/// Returns true if the element has been marked as needing a re-layout.
 	virtual bool IsLayoutDirty();
 	virtual bool IsLayoutDirty();
 
 
-	/// Increment/Decrement the layout lock
-	virtual void LockLayout(bool lock);
-
-	/// Forces a reevaluation of applicable font effects.
-	virtual void DirtyFont();
-
 	/// Returns the RML of this element and all children.
 	/// Returns the RML of this element and all children.
 	/// @param[out] content The content of this element and those under it, in XML form.
 	/// @param[out] content The content of this element and those under it, in XML form.
 	virtual void GetRML(String& content);
 	virtual void GetRML(String& content);
 
 
-	virtual void OnReferenceDeactivate();
+	void SetOwnerDocument(ElementDocument* document);
+
+	void Release() override;
 
 
 private:
 private:
 	void SetParent(Element* parent);
 	void SetParent(Element* parent);
 
 
-	void ReleaseDeletedElements();
-	void ReleaseElements(ElementList& elements);
-
 	void DirtyOffset();
 	void DirtyOffset();
 	void UpdateOffset();
 	void UpdateOffset();
 
 
@@ -688,23 +631,29 @@ private:
 	void DirtyStackingContext();
 	void DirtyStackingContext();
 
 
 	void DirtyStructure();
 	void DirtyStructure();
+	void UpdateStructure();
 
 
-	void DirtyTransformState(bool perspective_changed, bool transform_changed, bool parent_transform_changed);
+	void DirtyTransformState(bool perspective_dirty, bool transform_dirty);
 	void UpdateTransformState();
 	void UpdateTransformState();
 
 
-	// Start an animation, replacing any existing animations of the same property name. If start_value is null, the element's current value is used.
-	ElementAnimationList::iterator StartAnimation(const String & property_name, const Property * start_value, int num_iterations, bool alternate_direction, float delay);
+	/// Start an animation, replacing any existing animations of the same property name. If start_value is null, the element's current value is used.
+	ElementAnimationList::iterator StartAnimation(PropertyId property_id, const Property * start_value, int num_iterations, bool alternate_direction, float delay, bool origin_is_animation_property);
 
 
-	// Add a key to an animation, extending its duration. If target_value is null, the element's current value is used.
-	bool AddAnimationKeyTime(const String & property_name, const Property * target_value, float time, Tween tween);
+	/// Add a key to an animation, extending its duration. If target_value is null, the element's current value is used.
+	bool AddAnimationKeyTime(PropertyId property_id, const Property * target_value, float time, Tween tween);
 
 
 	/// Start a transition of the given property on this element.
 	/// Start a transition of the given property on this element.
 	/// If an animation exists for the property, the call will be ignored. If a transition exists for this property, it will be replaced.
 	/// If an animation exists for the property, the call will be ignored. If a transition exists for this property, it will be replaced.
-	/// @return True if the transition was added.
+	/// @return True if the transition was added or replaced.
 	bool StartTransition(const Transition& transition, const Property& start_value, const Property& target_value);
 	bool StartTransition(const Transition& transition, const Property& start_value, const Property& target_value);
 
 
-	void DirtyAnimation();
+	/// Removes all transitions that are no longer part of the element's 'transition' property.
+	void UpdateTransition();
+
+	/// Starts new animations and removes animations no longer part of the element's 'animation' property.
 	void UpdateAnimation();
 	void UpdateAnimation();
+
+	/// Advances the animations (including transitions) forward in time.
 	void AdvanceAnimations();
 	void AdvanceAnimations();
 
 
 	// Original tag this element came from.
 	// Original tag this element came from.
@@ -752,7 +701,9 @@ private:
 
 
 	// The size of the element.
 	// The size of the element.
 	typedef std::vector< Box > BoxList;
 	typedef std::vector< Box > BoxList;
-	BoxList boxes;
+	Box main_box;
+	BoxList additional_boxes;
+
 	// And of the element's internal content.
 	// And of the element's internal content.
 	Vector2f content_offset;
 	Vector2f content_offset;
 	Vector2f content_box;
 	Vector2f content_box;
@@ -763,12 +714,9 @@ private:
 	// True if the element is visible and active.
 	// True if the element is visible and active.
 	bool visible;
 	bool visible;
 
 
-	ElementList children;
+	OwnedElementList children;
 	int num_non_dom_children;
 	int num_non_dom_children;
 
 
-	ElementList active_children;
-	ElementList deleted_children;
-
 	float z_index;
 	float z_index;
 	bool local_stacking_context;
 	bool local_stacking_context;
 	bool local_stacking_context_forced;
 	bool local_stacking_context_forced;
@@ -776,27 +724,32 @@ private:
 	ElementList stacking_context;
 	ElementList stacking_context;
 	bool stacking_context_dirty;
 	bool stacking_context_dirty;
 
 
-	// The element's font face; used to render text and resolve em / ex properties.
-	FontFaceHandle* font_face_handle;
-	
+	bool structure_dirty;
+
+	bool computed_values_are_default_initialized;
+
 	// Cached rendering information
 	// Cached rendering information
 	int clipping_ignore_depth;
 	int clipping_ignore_depth;
 	bool clipping_enabled;
 	bool clipping_enabled;
 	bool clipping_state_dirty;
 	bool clipping_state_dirty;
 
 
 	// Transform state
 	// Transform state
-	std::unique_ptr< TransformState > transform_state;
-	bool transform_state_perspective_dirty;
-	bool transform_state_transform_dirty;
-	bool transform_state_parent_transform_dirty;
+	UniquePtr< TransformState > transform_state;
+	bool dirty_transform;
+	bool dirty_perspective;
 
 
 	ElementAnimationList animations;
 	ElementAnimationList animations;
 	bool dirty_animation;
 	bool dirty_animation;
+	bool dirty_transition;
+
+	ElementMeta* element_meta;
 
 
 	friend class Context;
 	friend class Context;
 	friend class ElementStyle;
 	friend class ElementStyle;
 	friend class LayoutEngine;
 	friend class LayoutEngine;
 	friend class LayoutInlineBox;
 	friend class LayoutInlineBox;
+	friend struct ElementDeleter;
+	friend class ElementScroll;
 };
 };
 
 
 }
 }

+ 9 - 14
Include/RmlUi/Core/Element.inl

@@ -30,12 +30,15 @@ namespace Rml {
 namespace Core {
 namespace Core {
 
 
 // Returns the values of one of this element's properties.
 // Returns the values of one of this element's properties.
-// We can assume the property will exist based on the RCSS inheritance.
 template < typename T >
 template < typename T >
 T Element::GetProperty(const String& name)
 T Element::GetProperty(const String& name)
 {
 {
 	const Property* property = GetProperty(name);
 	const Property* property = GetProperty(name);
-	RMLUI_ASSERTMSG(property, "Invalid property name.");
+	if (!property)
+	{
+		Log::Message(Log::LT_WARNING, "Invalid property name %s.", name.c_str());
+		return T{};
+	}
 	return property->Get< T >();
 	return property->Get< T >();
 }
 }
 
 
@@ -43,10 +46,9 @@ T Element::GetProperty(const String& name)
 template< typename T >
 template< typename T >
 void Element::SetAttribute(const String& name, const T& value)
 void Element::SetAttribute(const String& name, const T& value)
 {
 {
-	attributes.Set(name, value);
-	AttributeNameList changed_attributes;
-	changed_attributes.insert(name);
-
+	Variant variant(value);
+	attributes[name] = variant;
+	ElementAttributes changed_attributes = { {name, variant} };
 	OnAttributeChange(changed_attributes);
 	OnAttributeChange(changed_attributes);
 }
 }
 
 
@@ -54,14 +56,7 @@ void Element::SetAttribute(const String& name, const T& value)
 template< typename T >
 template< typename T >
 T Element::GetAttribute(const String& name, const T& default_value) const
 T Element::GetAttribute(const String& name, const T& default_value) const
 {
 {
-	return attributes.Get(name, default_value);
-}
-
-// Iterates over the attributes.
-template< typename T >
-bool Element::IterateAttributes(int& index, String& name, T& value) const
-{
-	return attributes.Iterate(index, name, value);
+	return Get(attributes, name, default_value);
 }
 }
 
 
 }
 }

+ 54 - 61
Include/RmlUi/Core/ElementDocument.h

@@ -34,19 +34,28 @@
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
-class Stream;
-
-}
-}
-
-namespace Rml {
-namespace Core {
-
 class Context;
 class Context;
+class Stream;
 class DocumentHeader;
 class DocumentHeader;
 class ElementText;
 class ElementText;
 class StyleSheet;
 class StyleSheet;
 
 
+/**
+	 ModalFlag used for controlling the modal state of the document.
+		None:  Remove modal state.
+		Modal: Set modal state, other documents cannot receive focus.
+		Keep:  Modal state unchanged.
+
+	FocusFlag used for displaying the document.
+		None:     No focus.
+		Document: Focus the document.
+		Keep:     Focus the element in the document which last had focus.
+		Auto:     Focus the first tab element with the 'autofocus' attribute or else the document.
+*/
+enum class ModalFlag { None, Modal, Keep };
+enum class FocusFlag { None, Document, Keep, Auto };
+
+
 /**
 /**
 	Represents a document in the dom tree.
 	Represents a document in the dom tree.
 
 
@@ -62,49 +71,31 @@ public:
 	/// Process given document header
 	/// Process given document header
 	void ProcessHeader(const DocumentHeader* header);
 	void ProcessHeader(const DocumentHeader* header);
 
 
-	/// Returns itself as the current document
-	virtual ElementDocument* GetOwnerDocument();
-
 	/// Returns the document's context.
 	/// Returns the document's context.
-	/// @return The context this document exists within.
 	Context* GetContext();
 	Context* GetContext();
 
 
 	/// Sets the document's title.
 	/// Sets the document's title.
-	/// @param[in] title The new title of the document.
 	void SetTitle(const String& title);
 	void SetTitle(const String& title);
 	/// Returns the title of this document.
 	/// Returns the title of this document.
-	/// @return The document's title.
 	const String& GetTitle() const;
 	const String& GetTitle() const;
 
 
 	/// Returns the source address of this document.
 	/// Returns the source address of this document.
-	/// @return The source of this document, usually a file name.
 	const String& GetSourceURL() const;
 	const String& GetSourceURL() const;
 
 
 	/// Sets the style sheet this document, and all of its children, uses.
 	/// Sets the style sheet this document, and all of its children, uses.
-	/// @param[in] style_sheet The style sheet to set on the document.
-	void SetStyleSheet(StyleSheet* style_sheet);
+	void SetStyleSheet(SharedPtr<StyleSheet> style_sheet);
 	/// Returns the document's style sheet.
 	/// Returns the document's style sheet.
-	/// @return The document's style sheet.
-	virtual StyleSheet* GetStyleSheet() const;
+	const SharedPtr<StyleSheet>& GetStyleSheet() const override;
 
 
 	/// Brings the document to the front of the document stack.
 	/// Brings the document to the front of the document stack.
 	void PullToFront();
 	void PullToFront();
 	/// Sends the document to the back of the document stack.
 	/// Sends the document to the back of the document stack.
 	void PushToBack();
 	void PushToBack();
 
 
-	/**
-		Flags used for displaying the document.
-	 */
-	enum FocusFlags
-	{
-		NONE = 0,
-		FOCUS = (1 << 1),
-		MODAL = (1 << 2)
-	};
-
 	/// Show the document.
 	/// Show the document.
-	/// @param[in] focus_flags Flags controlling the changing of focus. Leave as FOCUS to switch focus to the document.
-	void Show(int focus_flags = FOCUS);
+	/// @param[in] modal_flag Flags controlling the modal state of the document, see the 'ModalFlag' description for details.
+	/// @param[in] focus_flag Flags controlling the focus, see the 'FocusFlag' description for details.
+	void Show(ModalFlag modal_flag = ModalFlag::None, FocusFlag focus_flag = FocusFlag::Auto);
 	/// Hide the document.
 	/// Hide the document.
 	void Hide();
 	void Hide();
 	/// Close the document.
 	/// Close the document.
@@ -112,10 +103,10 @@ public:
 
 
 	/// Creates the named element.
 	/// Creates the named element.
 	/// @param[in] name The tag name of the element.
 	/// @param[in] name The tag name of the element.
-	Element* CreateElement(const String& name);
+	ElementPtr CreateElement(const String& name);
 	/// Create a text element with the given text content.
 	/// Create a text element with the given text content.
 	/// @param[in] text The text content of the text element.
 	/// @param[in] text The text content of the text element.
-	ElementText* CreateTextNode(const String& text);
+	ElementPtr CreateTextNode(const String& text);
 
 
 	/// Does the document have modal display set.
 	/// Does the document have modal display set.
 	/// @return True if the document is hogging focus.
 	/// @return True if the document is hogging focus.
@@ -127,40 +118,42 @@ public:
 	/// @param[in] source_name Name of the the script the source comes from, useful for debug information.
 	/// @param[in] source_name Name of the the script the source comes from, useful for debug information.
 	virtual void LoadScript(Stream* stream, const String& source_name);
 	virtual void LoadScript(Stream* stream, const String& source_name);
 
 
-	/// Updates the layout if necessary.
-	/// The extra argument was added such that layout updates can be done only once per frame
-	inline void UpdateLayout(bool i_really_mean_it = false) { if (i_really_mean_it && layout_dirty && lock_layout == 0) _UpdateLayout(); }
-
-	/// Updates the position of the document based on the style properties.
-	void UpdatePosition();
-	
-	/// Increment/Decrement the layout lock
-	void LockLayout(bool lock);
+	/// Updates the document, including its layout. Users must call this manually before requesting information such as 
+	/// size or position of an element if any element in the document was recently changed, unless Context::Update has
+	/// already been called after the change. This has a perfomance penalty, only call when necessary.
+	void UpdateDocument();
 	
 	
 protected:
 protected:
-	/// Refreshes the document layout if required.
-	virtual void OnUpdate();
-
 	/// Repositions the document if necessary.
 	/// Repositions the document if necessary.
-	virtual void OnPropertyChange(const PropertyNameList& changed_properties);
+	void OnPropertyChange(const PropertyIdSet& changed_properties) override;
 
 
-	/// Sets the dirty flag on the layout so the document will format its children before the next render.
-	virtual void DirtyLayout();
+	/// Processes the 'onpropertychange' event, checking for a change in position or size.
+	void ProcessDefaultAction(Event& event) override;
+
+	/// Called during update if the element size has been changed.
+	void OnResize() override;
 
 
+private:
+	/// Find the next element to focus, starting at the current element
+	Element* FindNextTabElement(Element* current_element, bool forward);
+	/// Searches forwards or backwards for a focusable element in the given substree
+	Element* SearchFocusSubtree(Element* element, bool forward);
+
+	/// Sets the dirty flag on the layout so the document will format its children before the next render.
+	void DirtyLayout() override;
 	/// Returns true if the document has been marked as needing a re-layout.
 	/// Returns true if the document has been marked as needing a re-layout.
-	virtual bool IsLayoutDirty();
+	bool IsLayoutDirty() override;
 
 
 	/// Updates all sizes defined by the 'lp' unit.
 	/// Updates all sizes defined by the 'lp' unit.
-	virtual void DirtyDpProperties();
+	void DirtyDpProperties();
 
 
-	/// Processes the 'onpropertychange' event, checking for a change in position or size.
-	virtual void ProcessEvent(Event& event);
+	/// Updates the layout if necessary.
+	void UpdateLayout();
 
 
-private:
-	// Find the next element to focus, starting at the current element
-	bool FocusNextTabElement(Element* current_element, bool forward);
-	/// Searches forwards or backwards for a focusable element in the given substree
-	bool SearchFocusSubtree(Element* element, bool forward);
+	/// Updates the position of the document based on the style properties.
+	void UpdatePosition();
+	/// Sets the dirty flag for document positioning
+	void DirtyPosition();
 
 
 	// Title of the document
 	// Title of the document
 	String title;
 	String title;
@@ -169,7 +162,7 @@ private:
 	String source_url;
 	String source_url;
 
 
 	// The document's style sheet.
 	// The document's style sheet.
-	StyleSheet* style_sheet;
+	SharedPtr<StyleSheet> style_sheet;
 
 
 	Context* context;
 	Context* context;
 
 
@@ -178,12 +171,12 @@ private:
 
 
 	// Is the layout dirty?
 	// Is the layout dirty?
 	bool layout_dirty;
 	bool layout_dirty;
-	int lock_layout;
+
+	bool position_dirty;
 
 
 	friend class Context;
 	friend class Context;
 	friend class Factory;
 	friend class Factory;
-	
-	void _UpdateLayout();
+
 };
 };
 
 
 }
 }

+ 65 - 11
Include/RmlUi/Core/ElementInstancer.h

@@ -29,9 +29,10 @@
 #ifndef RMLUICOREELEMENTINSTANCER_H
 #ifndef RMLUICOREELEMENTINSTANCER_H
 #define RMLUICOREELEMENTINSTANCER_H
 #define RMLUICOREELEMENTINSTANCER_H
 
 
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "XMLParser.h"
 #include "XMLParser.h"
 #include "Header.h"
 #include "Header.h"
+#include "Element.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -40,19 +41,20 @@ class Element;
 
 
 /**
 /**
 	An element instancer provides a method for allocating
 	An element instancer provides a method for allocating
-	an deallocating elements.
-
-	Node handlers are reference counted, so that the same handler
-	can be used for multiple tags.
+	and deallocating elements.
 
 
 	It is important at the same instancer that allocated
 	It is important at the same instancer that allocated
 	the element releases it. This ensures there are no
 	the element releases it. This ensures there are no
 	issues with memory from different DLLs getting mixed up.
 	issues with memory from different DLLs getting mixed up.
 
 
+	The returned element is a unique pointer. When this is
+	destroyed, it will call	ReleaseElement on the instancer 
+	in which it was instanced.
+
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */ 
  */ 
 
 
-class RMLUICORE_API ElementInstancer : public ReferenceCountable
+class RMLUICORE_API ElementInstancer : public NonCopyMoveable
 {
 {
 public:
 public:
 	virtual ~ElementInstancer();
 	virtual ~ElementInstancer();
@@ -61,15 +63,67 @@ public:
 	/// @param[in] parent The element the new element is destined to be parented to.
 	/// @param[in] parent The element the new element is destined to be parented to.
 	/// @param[in] tag The tag of the element to instance.
 	/// @param[in] tag The tag of the element to instance.
 	/// @param[in] attributes Dictionary of attributes.
 	/// @param[in] attributes Dictionary of attributes.
-	virtual Element* InstanceElement(Element* parent, const String& tag, const XMLAttributes& attributes) = 0;
+	/// @return A unique pointer to the instanced element.
+	virtual ElementPtr InstanceElement(Element* parent, const String& tag, const XMLAttributes& attributes) = 0;
 	/// Releases an element instanced by this instancer.
 	/// Releases an element instanced by this instancer.
 	/// @param[in] element The element to release.
 	/// @param[in] element The element to release.
 	virtual void ReleaseElement(Element* element) = 0;
 	virtual void ReleaseElement(Element* element) = 0;
-	/// Release the instancer.
-	virtual void Release() = 0;
+};
+
+
+
+/**
+	The element instancer constructs a plain Element, and is used for most elements.
+	This is a slightly faster version of the generic instancer, making use of a memory
+	pool for allocations.
+ */
+
+class RMLUICORE_API ElementInstancerElement : public ElementInstancer
+{
+public:
+	ElementPtr InstanceElement(Element* parent, const String& tag, const XMLAttributes& attributes) override;
+	void ReleaseElement(Element* element) override;
+	~ElementInstancerElement();
+};
+
+/**
+	The element text default instancer constructs ElementTextDefault.
+	This is a slightly faster version of the generic instancer, making use of a memory
+	pool for allocations.
+ */
+
+class RMLUICORE_API ElementInstancerTextDefault : public ElementInstancer
+{
+public:
+	ElementPtr InstanceElement(Element* parent, const String& tag, const XMLAttributes& attributes) override;
+	void ReleaseElement(Element* element) override;
+};
+
+
+/**
+	Generic Instancer that creates the provided element type using new and delete. This instancer
+	is typically used specialized element types.
+ */
+
+template <typename T>
+class ElementInstancerGeneric : public ElementInstancer
+{
+public:
+	virtual ~ElementInstancerGeneric() {}
+
+	ElementPtr InstanceElement(Element* RMLUI_UNUSED_PARAMETER(parent), const String& tag, const XMLAttributes& RMLUI_UNUSED_PARAMETER(attributes)) override
+	{
+		RMLUI_UNUSED(parent);
+		RMLUI_UNUSED(attributes);
+		RMLUI_ZoneScopedN("ElementGenericInstance");
+		return ElementPtr(new T(tag));
+	}
 
 
-protected:
-	virtual void OnReferenceDeactivate();
+	void ReleaseElement(Element* element) override
+	{
+		RMLUI_ZoneScopedN("ElementGenericRelease");
+		delete element;
+	}
 };
 };
 
 
 }
 }

+ 0 - 63
Include/RmlUi/Core/ElementInstancerGeneric.inl

@@ -1,63 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-namespace Rml {
-namespace Core {
-
-template <typename T>
-ElementInstancerGeneric<T>::~ElementInstancerGeneric()
-{
-}
-
-// Instances an element given the tag name and attributes
-template <typename T>
-Element* ElementInstancerGeneric<T>::InstanceElement(Element* /*parent*/, const String& tag, const XMLAttributes& /*attributes*/)
-{
-	return new T(tag);
-}
-
-
-
-// Releases the given element
-template <typename T>
-void ElementInstancerGeneric<T>::ReleaseElement(Element* element)
-{
-	delete element;
-}
-
-
-
-// Release the instancer
-template <typename T>
-void ElementInstancerGeneric<T>::Release()
-{
-	delete this;
-}
-
-}
-}

+ 0 - 102
Include/RmlUi/Core/ElementReference.h

@@ -1,102 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREELEMENTREFERENCE_H
-#define RMLUICOREELEMENTREFERENCE_H
-
-#include "Header.h"
-
-namespace Rml {
-namespace Core {
-
-class Element;
-
-/**
-	A smart pointer for elements.
-
-	@author Peter Curry
- */
-
-class RMLUICORE_API ElementReference
-{
-public:
-	ElementReference(Element* element = NULL);
-	ElementReference(const ElementReference& copy);
-	~ElementReference();
-
-	/// Returns true if this reference is bound to an element.
-	/// @return True if this reference points to a non-NULL element, false otherwise.
-	operator bool() const;
-
-	/// Assigns a new element for this reference to point to.
-	/// @param element[in] The new element.
-	/// @return This element reference.
-	ElementReference& operator=(Element* element);
-	/// Assigns a new element for this reference, from another reference.
-	/// @param element_reference[in] The element reference to copy.
-	/// @return This element reference.
-	ElementReference& operator=(const ElementReference& element_reference);
-
-	/// Returns a reference to the underlying element.
-	/// @return The underlying element. This may be NULL.
-	Element* operator*();
-	/// Returns a reference to the underlying element.
-	/// @return The underlying element. This may be NULL.
-	Element* operator->();
-
-	/// Equality operator for the reference. Used for STL containers.
-	/// @param rhs[in] The other element to use in the comparison.
-	/// @return True if the elements are equivalent, false otherwise.
-	bool operator==(const ElementReference& rhs) const;
-	/// Equality operator for the reference.
-	/// @param rhs[in] The other element to use in the comparison.
-	/// @return True if the elements are equivalent, false otherwise.
-	bool operator==(const Element* rhs) const;
-
-	/// Less-than operator for the reference. Used for STL containers.
-	/// @param rhs[in] The other element to use in the comparison.
-	/// @return True if this element is less than the other element, false otherwise.
-	bool operator<(const ElementReference& rhs) const;
-
-	/// Inequality operator for the reference.
-	/// @param rhs[in] The other element to use in the comparison.
-	/// @return False if the elements are equivalent, true otherwise.
-	bool operator!=(const ElementReference& rhs) const;
-	/// Inequality operator for the reference.
-	/// @param rhs[in] A raw element to use in the comparison.
-	/// @return False if the elements are equivalent, true otherwise.
-	bool operator!=(const Element* rhs) const;
-
-private:
-	Element* element;
-};
-
-}
-}
-
-#endif

+ 5 - 7
Include/RmlUi/Core/ElementScroll.h

@@ -30,7 +30,6 @@
 #define RMLUICOREELEMENTSCROLL_H
 #define RMLUICOREELEMENTSCROLL_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "EventListener.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -44,7 +43,7 @@ class WidgetSliderScroll;
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API ElementScroll : public EventListener
+class RMLUICORE_API ElementScroll
 {
 {
 public:
 public:
 	enum Orientation
 	enum Orientation
@@ -54,7 +53,7 @@ public:
 	};
 	};
 
 
 	ElementScroll(Element* element);
 	ElementScroll(Element* element);
-	virtual ~ElementScroll();
+	~ElementScroll();
 
 
 	/// Updates the increment / decrement arrows.
 	/// Updates the increment / decrement arrows.
 	void Update();
 	void Update();
@@ -73,7 +72,7 @@ public:
 
 
 	/// Returns one of the scrollbar elements.
 	/// Returns one of the scrollbar elements.
 	/// @param[in] orientation Which scrollbar to return.
 	/// @param[in] orientation Which scrollbar to return.
-	/// @return The requested scrollbar, or NULL if it does not exist.
+	/// @return The requested scrollbar, or nullptr if it does not exist.
 	Element* GetScrollbar(Orientation orientation);
 	Element* GetScrollbar(Orientation orientation);
 	/// Returns the size, in pixels, of one of the scrollbars; for a vertical scrollbar, this is width, for a horizontal scrollbar, this is height.
 	/// Returns the size, in pixels, of one of the scrollbars; for a vertical scrollbar, this is width, for a horizontal scrollbar, this is height.
 	/// @param[in] orientation Which scrollbar (vertical or horizontal) to query.
 	/// @param[in] orientation Which scrollbar (vertical or horizontal) to query.
@@ -83,9 +82,8 @@ public:
 	/// Formats the enabled scrollbars based on the current size of the host element.
 	/// Formats the enabled scrollbars based on the current size of the host element.
 	void FormatScrollbars();
 	void FormatScrollbars();
 
 
-protected:
-	/// Handles the 'onchange' events for the scrollbars.
-	void ProcessEvent(Event& event);
+	/// Clears the scrollbars, resetting it to initial conditions.
+	void ClearScrollbars();
 
 
 private:
 private:
 	struct Scrollbar
 	struct Scrollbar

+ 5 - 5
Include/RmlUi/Core/ElementText.h

@@ -30,8 +30,8 @@
 #define RMLUICOREELEMENTTEXT_H
 #define RMLUICOREELEMENTTEXT_H
 
 
 #include "Header.h"
 #include "Header.h"
+#include "Types.h"
 #include "Element.h"
 #include "Element.h"
-#include "WString.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -51,10 +51,10 @@ public:
 	/// Sets the raw string this text element contains. The actual rendered text may be different due to whitespace
 	/// Sets the raw string this text element contains. The actual rendered text may be different due to whitespace
 	/// formatting.
 	/// formatting.
 	/// @param[in] text The new string to set on this element.
 	/// @param[in] text The new string to set on this element.
-	virtual void SetText(const WString& text) = 0;
+	virtual void SetText(const String& text) = 0;
 	/// Returns the raw string this text element contains.
 	/// Returns the raw string this text element contains.
 	/// @return This element's raw text.
 	/// @return This element's raw text.
-	virtual const WString& GetText() const = 0;
+	virtual const String& GetText() const = 0;
 
 
 	/// Generates a token of text from this element, returning only the width.
 	/// Generates a token of text from this element, returning only the width.
 	/// @param[out] token_width The window (in pixels) of the token.
 	/// @param[out] token_width The window (in pixels) of the token.
@@ -70,14 +70,14 @@ public:
 	/// @param[in] right_spacing_width The width (in pixels) of the spacing (consisting of margins, padding, etc) that must be remaining on the right of the line if the last of the text is rendered onto this line.
 	/// @param[in] right_spacing_width The width (in pixels) of the spacing (consisting of margins, padding, etc) that must be remaining on the right of the line if the last of the text is rendered onto this line.
 	/// @param[in] trim_whitespace_prefix If we're collapsing whitespace, whether or not to remove all prefixing whitespace or collapse it down to a single space.
 	/// @param[in] trim_whitespace_prefix If we're collapsing whitespace, whether or not to remove all prefixing whitespace or collapse it down to a single space.
 	/// @return True if the line reached the end of the element's text, false if not.
 	/// @return True if the line reached the end of the element's text, false if not.
-	virtual bool GenerateLine(WString& line, int& line_length, float& line_width, int line_begin, float maximum_line_width, float right_spacing_width, bool trim_whitespace_prefix) = 0;
+	virtual bool GenerateLine(String& line, int& line_length, float& line_width, int line_begin, float maximum_line_width, float right_spacing_width, bool trim_whitespace_prefix) = 0;
 
 
 	/// Clears all lines of generated text and prepares the element for generating new lines.
 	/// Clears all lines of generated text and prepares the element for generating new lines.
 	virtual void ClearLines() = 0;
 	virtual void ClearLines() = 0;
 	/// Adds a new line into the text element.
 	/// Adds a new line into the text element.
 	/// @param[in] line_position The position of this line, as an offset from the first line.
 	/// @param[in] line_position The position of this line, as an offset from the first line.
 	/// @param[in] line The contents of the line.
 	/// @param[in] line The contents of the line.
-	virtual void AddLine(const Vector2f& line_position, const WString& line) = 0;
+	virtual void AddLine(const Vector2f& line_position, const String& line) = 0;
 
 
 	/// Prevents the element from dirtying its document's layout when its text is changed.
 	/// Prevents the element from dirtying its document's layout when its text is changed.
 	virtual void SuppressAutoLayout() = 0;
 	virtual void SuppressAutoLayout() = 0;

+ 7 - 38
Include/RmlUi/Core/ElementUtilities.h

@@ -31,16 +31,14 @@
 
 
 #include "Header.h"
 #include "Header.h"
 #include "Box.h"
 #include "Box.h"
-#include "WString.h"
 #include "Types.h"
 #include "Types.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
 class Context;
 class Context;
-class FontFaceHandle;
 class RenderInterface;
 class RenderInterface;
-class ViewState;
+namespace Style { struct ComputedValues; }
 
 
 /**
 /**
 	Utility functions for dealing with elements.
 	Utility functions for dealing with elements.
@@ -79,27 +77,15 @@ public:
 	/// @param[in] tag Class name to search for.
 	/// @param[in] tag Class name to search for.
 	static void GetElementsByClassName(ElementList& elements, Element* root_element, const String& class_name);
 	static void GetElementsByClassName(ElementList& elements, Element* root_element, const String& class_name);
 
 
-	/// Returns an element's font face.
-	/// @param[in] element The element to determine the font face for.
-	/// @return The element's font face. This will be NULL if no valid RCSS font styles have been set up for this element.
-	static FontFaceHandle* GetFontFaceHandle(Element* element);
 	/// Returns an element's density-independent pixel ratio, defined by it's context
 	/// Returns an element's density-independent pixel ratio, defined by it's context
 	/// @param[in] element The element to determine the density-independent pixel ratio for.
 	/// @param[in] element The element to determine the density-independent pixel ratio for.
 	/// @return The density-independent pixel ratio of the context, or 1.0 if no context assigned.
 	/// @return The density-independent pixel ratio of the context, or 1.0 if no context assigned.
 	static float GetDensityIndependentPixelRatio(Element* element);
 	static float GetDensityIndependentPixelRatio(Element* element);
-	/// Returns an element's font size, if it has a font defined.
-	/// @param[in] element The element to determine the font size for.
-	/// @return The font size as determined by the element's font, or 0 if it has no font specified.
-	static int GetFontSize(Element* element);
-	/// Returns an element's line height, if it has a font defined.
-	/// @param[in] element The element to determine the line height for.
-	/// @return The line height as specified by the element's font and line height styles.
-	static int GetLineHeight(Element* element);
 	/// Returns the width of a string rendered within the context of the given element.
 	/// Returns the width of a string rendered within the context of the given element.
 	/// @param[in] element The element to measure the string from.
 	/// @param[in] element The element to measure the string from.
 	/// @param[in] string The string to measure.
 	/// @param[in] string The string to measure.
 	/// @return The string width, in pixels.
 	/// @return The string width, in pixels.
-	static int GetStringWidth(Element* element, const WString& string);
+	static int GetStringWidth(Element* element, const String& string);
 
 
 	/// Bind and instance all event attributes on the given element onto the element
 	/// Bind and instance all event attributes on the given element onto the element
 	/// @param element Element to bind events on
 	/// @param element Element to bind events on
@@ -115,7 +101,7 @@ public:
 	/// @param[in] element The element to generate the clipping region from.
 	/// @param[in] element The element to generate the clipping region from.
 	/// @param[in] context The context of the element; if this is not supplied, it will be derived from the element.
 	/// @param[in] context The context of the element; if this is not supplied, it will be derived from the element.
 	/// @return The visibility of the given element within its clipping region.
 	/// @return The visibility of the given element within its clipping region.
-	static bool SetClippingRegion(Element* element, Context* context = NULL);
+	static bool SetClippingRegion(Element* element, Context* context = nullptr);
 	/// Applies the clip region from the render interface to the renderer
 	/// Applies the clip region from the render interface to the renderer
 	/// @param[in] context The context to read the clip region from
 	/// @param[in] context The context to read the clip region from
 	/// @param[in] render_interface The render interface to update.
 	/// @param[in] render_interface The render interface to update.
@@ -134,11 +120,6 @@ public:
 	/// @param[in] inline_element True if the element is placed in an inline context, false if not.
 	/// @param[in] inline_element True if the element is placed in an inline context, false if not.
 	static void BuildBox(Box& box, const Vector2f& containing_block, Element* element, bool inline_element = false);
 	static void BuildBox(Box& box, const Vector2f& containing_block, Element* element, bool inline_element = false);
 
 
-	/// Sizes and positions an element within its parent. Any relative values will be evaluated against the size of the
-	/// element parent's content area.
-	/// @param element[in] The element to size and position.
-	/// @param offset[in] The offset of the element inside its parent's content area.
-	static bool PositionElement(Element* element, const Vector2f& offset);
 	/// Sizes an element, and positions it within its parent offset from the borders of its content area. Any relative
 	/// Sizes an element, and positions it within its parent offset from the borders of its content area. Any relative
 	/// values will be evaluated against the size of the element parent's content area.
 	/// values will be evaluated against the size of the element parent's content area.
 	/// @param element[in] The element to size and position.
 	/// @param element[in] The element to size and position.
@@ -146,23 +127,11 @@ public:
 	/// @param anchor[in] Defines which corner or edge the border is to be positioned relative to.
 	/// @param anchor[in] Defines which corner or edge the border is to be positioned relative to.
 	static bool PositionElement(Element* element, const Vector2f& offset, PositionAnchor anchor);
 	static bool PositionElement(Element* element, const Vector2f& offset, PositionAnchor anchor);
 
 
-	/// Applies an element's `perspective' and `transform' properties.
+	/// Applies an element's accumulated transform matrix, determined from its and ancestor's `perspective' and `transform' properties.
+	/// Note: All calls to RenderInterface::SetTransform must go through here.
 	/// @param[in] element		The element whose transform to apply.
 	/// @param[in] element		The element whose transform to apply.
-	/// @param[in] apply		Whether to apply (true) or unapply (false) the transform.
-	/// @return true if the element has a transform and it could be applied.
-	static bool ApplyTransform(Element &element, bool apply = true);
-	/// Unapplies an element's `perspective' and `transform' properties.
-	/// @param[in] element		The element whose transform to unapply.
-	/// @return true if the element has a transform and it could be unapplied.
-	static bool UnapplyTransform(Element &element);
-
-	/// Projects the mouse cursor coordinates into a transformed element's plane.
-	/// @param[in/out] dict		The dictionary with the projected mouse coordinates.
-	/// @param[in] old_dict		The dictionary with the original mouse coordinates.
-	/// @param[in] element		The element to project the mouse coordinates into.
-	/// @param[in] view		The current global projection and view matrices.
-	/// @return true, if the mouse coordinates could be updated.
-	static bool ProjectMouse(Dictionary &dict, const Dictionary &old_dict, Element &element, const ViewState *view = 0);
+	/// @return true if a render interface is available to set the transform.
+	static bool ApplyTransform(Element &element);
 };
 };
 
 
 }
 }

+ 36 - 17
Include/RmlUi/Core/Event.h

@@ -29,15 +29,20 @@
 #ifndef RMLUICOREEVENT_H
 #ifndef RMLUICOREEVENT_H
 #define RMLUICOREEVENT_H
 #define RMLUICOREEVENT_H
 
 
+#include "Header.h"
 #include "Dictionary.h"
 #include "Dictionary.h"
 #include "ScriptInterface.h"
 #include "ScriptInterface.h"
-#include "Header.h"
+#include "ID.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
 class Element;
 class Element;
 class EventInstancer;
 class EventInstancer;
+struct EventSpecification;
+
+enum class EventPhase { None, Capture = 1, Target = 2, Bubble = 4 };
+enum class DefaultActionPhase { None, Target = (int)EventPhase::Target, Bubble = (int)EventPhase::Bubble, TargetAndBubble = ((int)Target | (int)Bubble) };
 
 
 /**
 /**
 	An event that propogates through the element hierarchy. Events follow the DOM3 event specification. See
 	An event that propogates through the element hierarchy. Events follow the DOM3 event specification. See
@@ -56,11 +61,10 @@ public:
 	/// @param[in] type The event type
 	/// @param[in] type The event type
 	/// @param[in] parameters The event parameters
 	/// @param[in] parameters The event parameters
 	/// @param[in] interruptible Can this event have is propagation stopped?
 	/// @param[in] interruptible Can this event have is propagation stopped?
-	Event(Element* target, const String& type, const Dictionary& parameters, bool interruptible = false);
+	Event(Element* target, EventId id, const String& type, const Dictionary& parameters, bool interruptible);
 	/// Destructor
 	/// Destructor
 	virtual ~Event();
 	virtual ~Event();
 
 
-	enum EventPhase { PHASE_UNKNOWN, PHASE_CAPTURE, PHASE_TARGET, PHASE_BUBBLE };
 
 
 	/// Get the current propagation phase.
 	/// Get the current propagation phase.
 	/// @return Current phase the event is in.
 	/// @return Current phase the event is in.
@@ -81,59 +85,74 @@ public:
 	Element* GetTargetElement() const;
 	Element* GetTargetElement() const;
 
 
 	/// Get the event type.
 	/// Get the event type.
-	/// @return The event type.
 	const String& GetType() const;
 	const String& GetType() const;
+	/// Get the event id.
+	EventId GetId() const;
 	/// Checks if the event is of a certain type.
 	/// Checks if the event is of a certain type.
 	/// @param type The name of the type to check for.
 	/// @param type The name of the type to check for.
 	/// @return True if the event is of the requested type, false otherwise.
 	/// @return True if the event is of the requested type, false otherwise.
 	bool operator==(const String& type) const;
 	bool operator==(const String& type) const;
+	/// Checks if the event is of a certain id.
+	bool operator==(EventId id) const;
 
 
-	/// Has the event been stopped?
-	/// @return True if the event is still propogating
+	/// Returns true if the event is still propagating.
 	bool IsPropagating() const;
 	bool IsPropagating() const;
-	/// Stops the propagation of the event wherever it is
+	/// Returns true if the event is still immediate propagating.
+	bool IsImmediatePropagating() const;
+
+	/// Stops propagation of the event, but finish all listeners on the current element.
 	void StopPropagation();
 	void StopPropagation();
+	/// Stops propagation of the event, including to any other listeners on the current element.
+	void StopImmediatePropagation();
 
 
 	/// Returns the value of one of the event's parameters.
 	/// Returns the value of one of the event's parameters.
 	/// @param key[in] The name of the desired parameter.
 	/// @param key[in] The name of the desired parameter.
 	/// @return The value of the requested parameter.
 	/// @return The value of the requested parameter.
 	template < typename T >
 	template < typename T >
-	T GetParameter(const String& key, const T& default_value)
+	T GetParameter(const String& key, const T& default_value) const
 	{
 	{
-		return parameters.Get(key, default_value);
+		return Get(parameters, key, default_value);
 	}
 	}
 	/// Access the dictionary of parameters
 	/// Access the dictionary of parameters
 	/// @return The dictionary of parameters
 	/// @return The dictionary of parameters
-	const Dictionary* GetParameters() const;
+	const Dictionary& GetParameters() const;
 
 
-	/// Release this event.
-	virtual void OnReferenceDeactivate();
+	/// Return the unprojected mouse screen position.
+	/// Note: Only specified for events with 'mouse_x' and 'mouse_y' parameters.
+	const Vector2f& GetUnprojectedMouseScreenPos() const;
 
 
 private:
 private:
+	/// Release this event.
+	void Release() override;
+
 	/// Project the mouse coordinates to the current element to enable
 	/// Project the mouse coordinates to the current element to enable
 	/// interacting with transformed elements.
 	/// interacting with transformed elements.
 	void ProjectMouse(Element* element);
 	void ProjectMouse(Element* element);
 
 
 protected:
 protected:
-	String type;
 	Dictionary parameters;
 	Dictionary parameters;
 
 
 	Element* target_element;
 	Element* target_element;
 	Element* current_element;
 	Element* current_element;
 
 
 private:
 private:
-	Dictionary parameters_backup;
-
+	String type;
+	EventId id;
 	bool interruptible;
 	bool interruptible;
-	bool interruped;
-
+	
+	bool interrupted;
+	bool interrupted_immediate;
 	EventPhase phase;
 	EventPhase phase;
 
 
+	bool has_mouse_position;
+	Vector2f mouse_screen_position;
+
 	EventInstancer* instancer;
 	EventInstancer* instancer;
 
 
 	friend class Factory;
 	friend class Factory;
 };
 };
 
 
+
 }
 }
 }
 }
 
 

+ 4 - 9
Include/RmlUi/Core/EventInstancer.h

@@ -29,7 +29,7 @@
 #ifndef RMLUICOREEVENTINSTANCER_H
 #ifndef RMLUICOREEVENTINSTANCER_H
 #define RMLUICOREEVENTINSTANCER_H
 #define RMLUICOREEVENTINSTANCER_H
 
 
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "Header.h"
 #include "Header.h"
 
 
 namespace Rml {
 namespace Rml {
@@ -44,27 +44,22 @@ class Event;
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICORE_API EventInstancer : public ReferenceCountable
+class RMLUICORE_API EventInstancer : public Releasable
 {
 {
 public:
 public:
 	virtual ~EventInstancer();
 	virtual ~EventInstancer();
 
 
 	/// Instance an event object.
 	/// Instance an event object.
 	/// @param[in] target Target element of this event.
 	/// @param[in] target Target element of this event.
+	/// @param[in] id EventId of this event.
 	/// @param[in] name Name of this event.
 	/// @param[in] name Name of this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] interruptible If the event propagation can be stopped.
 	/// @param[in] interruptible If the event propagation can be stopped.
-	virtual Event* InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible) = 0;
+	virtual EventPtr InstanceEvent(Element* target, EventId id, const String& type, const Dictionary& parameters, bool interruptible) = 0;
 
 
 	/// Releases an event instanced by this instancer.
 	/// Releases an event instanced by this instancer.
 	/// @param[in] event The event to release.
 	/// @param[in] event The event to release.
 	virtual void ReleaseEvent(Event* event) = 0;
 	virtual void ReleaseEvent(Event* event) = 0;
-
-	/// Releases this event instancer.
-	virtual void Release() = 0;
-
-private:
-	virtual void OnReferenceDeactivate();
 };
 };
 
 
 }
 }

+ 3 - 9
Include/RmlUi/Core/EventListenerInstancer.h

@@ -29,8 +29,8 @@
 #ifndef RMLUICOREEVENTLISTENERINSTANCER_H
 #ifndef RMLUICOREEVENTLISTENERINSTANCER_H
 #define RMLUICOREEVENTLISTENERINSTANCER_H
 #define RMLUICOREEVENTLISTENERINSTANCER_H
 
 
-#include "ReferenceCountable.h"
-#include "String.h"
+#include "Traits.h"
+#include "Types.h"
 #include "Header.h"
 #include "Header.h"
 #include "Element.h"
 #include "Element.h"
 
 
@@ -46,7 +46,7 @@ class EventListener;
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICORE_API EventListenerInstancer : public ReferenceCountable
+class RMLUICORE_API EventListenerInstancer
 {
 {
 public:
 public:
 	virtual ~EventListenerInstancer();
 	virtual ~EventListenerInstancer();
@@ -55,12 +55,6 @@ public:
 	/// @param value Value of the event.
 	/// @param value Value of the event.
 	/// @param element Element that triggers the events.
 	/// @param element Element that triggers the events.
 	virtual EventListener* InstanceEventListener(const String& value, Element* element) = 0;
 	virtual EventListener* InstanceEventListener(const String& value, Element* element) = 0;
-
-	/// Releases this event listener instancer.
-	virtual void Release() = 0;
-
-protected:
-	virtual void OnReferenceDeactivate();
 };
 };
 
 
 }
 }

+ 42 - 40
Include/RmlUi/Core/Factory.h

@@ -31,7 +31,6 @@
 
 
 #include "XMLParser.h"
 #include "XMLParser.h"
 #include "Header.h"
 #include "Header.h"
-#include <map>
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -51,6 +50,9 @@ class FontEffect;
 class FontEffectInstancer;
 class FontEffectInstancer;
 class StyleSheet;
 class StyleSheet;
 class PropertyDictionary;
 class PropertyDictionary;
+class PropertySpecification;
+class DecoratorInstancerInterface;
+enum class EventId : uint16_t;
 
 
 /**
 /**
 	The Factory contains a registry of instancers for different types.
 	The Factory contains a registry of instancers for different types.
@@ -69,30 +71,31 @@ public:
 	/// Cleanup and shutdown the factory
 	/// Cleanup and shutdown the factory
 	static void Shutdown();
 	static void Shutdown();
 
 
-	/// Registers the instancer to use when instancing contexts.
+	/// Registers a non-owning pointer to the instancer used to instance contexts.
 	/// @param[in] instancer The new context instancer.
 	/// @param[in] instancer The new context instancer.
-	static ContextInstancer* RegisterContextInstancer(ContextInstancer* instancer);
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	static void RegisterContextInstancer(ContextInstancer* instancer);
 	/// Instances a new context.
 	/// Instances a new context.
 	/// @param[in] name The name of the new context.
 	/// @param[in] name The name of the new context.
-	/// @return The new context, or NULL if no context could be created.
-	static Context* InstanceContext(const String& name);
+	/// @return The new context, or nullptr if no context could be created.
+	static ContextPtr InstanceContext(const String& name);
 
 
-	/// Registers an element instancer that will be used to instance an element when the specified tag is encountered.
+	/// Registers a non-owning pointer to the element instancer that will be used to instance an element when the specified tag is encountered.
 	/// @param[in] name Name of the instancer; elements with this as their tag will use this instancer.
 	/// @param[in] name Name of the instancer; elements with this as their tag will use this instancer.
 	/// @param[in] instancer The instancer to call when the tag is encountered.
 	/// @param[in] instancer The instancer to call when the tag is encountered.
-	/// @return The added instancer if the registration was successful, NULL otherwise.
-	static ElementInstancer* RegisterElementInstancer(const String& name, ElementInstancer* instancer);
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	static void RegisterElementInstancer(const String& name, ElementInstancer* instancer);
 	/// Returns the element instancer for the specified tag.
 	/// Returns the element instancer for the specified tag.
 	/// @param[in] tag Name of the tag to get the instancer for.
 	/// @param[in] tag Name of the tag to get the instancer for.
-	/// @return The requested element instancer, or NULL if no such instancer is registered.
+	/// @return The requested element instancer, or nullptr if no such instancer is registered.
 	static ElementInstancer* GetElementInstancer(const String& tag);
 	static ElementInstancer* GetElementInstancer(const String& tag);
 	/// Instances a single element.
 	/// Instances a single element.
-	/// @param[in] parent The parent of the new element, or NULL for a root tag.
+	/// @param[in] parent The parent of the new element, or nullptr for a root tag.
 	/// @param[in] instancer The name of the instancer to create the element with.
 	/// @param[in] instancer The name of the instancer to create the element with.
 	/// @param[in] tag The tag of the element to be instanced.
 	/// @param[in] tag The tag of the element to be instanced.
 	/// @param[in] attributes The attributes to instance the element with.
 	/// @param[in] attributes The attributes to instance the element with.
-	/// @return The instanced element, or NULL if the instancing failed.
-	static Element* InstanceElement(Element* parent, const String& instancer, const String& tag, const XMLAttributes& attributes);
+	/// @return The instanced element, or nullptr if the instancing failed.
+	static ElementPtr InstanceElement(Element* parent, const String& instancer, const String& tag, const XMLAttributes& attributes);
 
 
 	/// Instances a single text element containing a string. The string is assumed to contain no RML markup, but will
 	/// Instances a single text element containing a string. The string is assumed to contain no RML markup, but will
 	/// be translated and therefore may have some introduced. In this case more than one element may be instanced.
 	/// be translated and therefore may have some introduced. In this case more than one element may be instanced.
@@ -108,43 +111,43 @@ public:
 	/// Instances a document from a stream.
 	/// Instances a document from a stream.
 	/// @param[in] context The context that is creating the document.
 	/// @param[in] context The context that is creating the document.
 	/// @param[in] stream The stream to instance from.
 	/// @param[in] stream The stream to instance from.
-	/// @return The instanced document, or NULL if an error occurred.
-	static ElementDocument* InstanceDocumentStream(Rml::Core::Context* context, Stream* stream);
+	/// @return The instanced document, or nullptr if an error occurred.
+	static ElementPtr InstanceDocumentStream(Rml::Core::Context* context, Stream* stream);
 
 
-	/// Registers an instancer that will be used to instance decorators.
+	/// Registers a non-owning pointer to an instancer that will be used to instance decorators.
 	/// @param[in] name The name of the decorator the instancer will be called for.
 	/// @param[in] name The name of the decorator the instancer will be called for.
 	/// @param[in] instancer The instancer to call when the decorator name is encountered.
 	/// @param[in] instancer The instancer to call when the decorator name is encountered.
-	/// @return The added instancer if the registration was successful, NULL otherwise.
-	static DecoratorInstancer* RegisterDecoratorInstancer(const String& name, DecoratorInstancer* instancer);
-	/// Attempts to instance a decorator from an instancer registered with the factory.
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	/// @return The added instancer if the registration was successful, nullptr otherwise.
+	static void RegisterDecoratorInstancer(const String& name, DecoratorInstancer* instancer);
+	/// Retrieves a decorator instancer registered with the factory.
 	/// @param[in] name The name of the desired decorator type.
 	/// @param[in] name The name of the desired decorator type.
-	/// @param[in] properties The properties associated with the decorator.
-	/// @return The newly instanced decorator, or NULL if the decorator could not be instanced.
-	static Decorator* InstanceDecorator(const String& name, const PropertyDictionary& properties);
+	/// @return The decorator instancer it it exists, nullptr otherwise.
+	static DecoratorInstancer* GetDecoratorInstancer(const String& name);
 
 
-	/// Registers an instancer that will be used to instance font effects.
+	/// Registers a non-owning pointer to an instancer that will be used to instance font effects.
 	/// @param[in] name The name of the font effect the instancer will be called for.
 	/// @param[in] name The name of the font effect the instancer will be called for.
 	/// @param[in] instancer The instancer to call when the font effect name is encountered.
 	/// @param[in] instancer The instancer to call when the font effect name is encountered.
-	/// @return The added instancer if the registration was successful, NULL otherwise.
-	static FontEffectInstancer* RegisterFontEffectInstancer(const String& name, FontEffectInstancer* instancer);
-	/// Attempts to instance a font effect from an instancer registered with the factory.
-	/// @param[in] name The name of the desired font effect type.
-	/// @param[in] properties The properties associated with the font effect.
-	/// @return The newly instanced font effect, or NULL if the font effect could not be instanced.
-	static FontEffect* InstanceFontEffect(const String& name, const PropertyDictionary& properties);
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	/// @return The added instancer if the registration was successful, nullptr otherwise.
+	static void RegisterFontEffectInstancer(const String& name, FontEffectInstancer* instancer);
+	/// Retrieves a font-effect instancer registered with the factory.
+	/// @param[in] name The name of the desired font-effect type.
+	/// @return The font-effect instancer it it exists, nullptr otherwise.
+	static FontEffectInstancer* GetFontEffectInstancer(const String& name);
 
 
 	/// Creates a style sheet from a user-generated string.
 	/// Creates a style sheet from a user-generated string.
 	/// @param[in] string The contents of the style sheet.
 	/// @param[in] string The contents of the style sheet.
 	/// @return A pointer to the newly created style sheet.
 	/// @return A pointer to the newly created style sheet.
-	static StyleSheet* InstanceStyleSheetString(const String& string);
+	static SharedPtr<StyleSheet> InstanceStyleSheetString(const String& string);
 	/// Creates a style sheet from a file.
 	/// Creates a style sheet from a file.
 	/// @param[in] file_name The location of the style sheet file.
 	/// @param[in] file_name The location of the style sheet file.
 	/// @return A pointer to the newly created style sheet.
 	/// @return A pointer to the newly created style sheet.
-	static StyleSheet* InstanceStyleSheetFile(const String& file_name);
+	static SharedPtr<StyleSheet> InstanceStyleSheetFile(const String& file_name);
 	/// Creates a style sheet from an Stream.
 	/// Creates a style sheet from an Stream.
 	/// @param[in] stream A pointer to the stream containing the style sheet's contents.
 	/// @param[in] stream A pointer to the stream containing the style sheet's contents.
 	/// @return A pointer to the newly created style sheet.
 	/// @return A pointer to the newly created style sheet.
-	static StyleSheet* InstanceStyleSheetStream(Stream* stream);
+	static SharedPtr<StyleSheet> InstanceStyleSheetStream(Stream* stream);
 	/// Clears the style sheet cache. This will force style sheets to be reloaded.
 	/// Clears the style sheet cache. This will force style sheets to be reloaded.
 	static void ClearStyleSheetCache();
 	static void ClearStyleSheetCache();
 	/// Clears the template cache. This will force template to be reloaded.
 	/// Clears the template cache. This will force template to be reloaded.
@@ -152,21 +155,20 @@ public:
 
 
 	/// Registers an instancer for all events.
 	/// Registers an instancer for all events.
 	/// @param[in] instancer The instancer to be called.
 	/// @param[in] instancer The instancer to be called.
-	/// @return The registered instanced on success, NULL on failure.
-	static EventInstancer* RegisterEventInstancer(EventInstancer* instancer);
-	/// Instance and event object
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown.
+	static void RegisterEventInstancer(EventInstancer* instancer);
+	/// Instance an event object
 	/// @param[in] target Target element of this event.
 	/// @param[in] target Target element of this event.
 	/// @param[in] name Name of this event.
 	/// @param[in] name Name of this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] parameters Additional parameters for this event.
 	/// @param[in] interruptible If the event propagation can be stopped.
 	/// @param[in] interruptible If the event propagation can be stopped.
 	/// @return The instanced event.
 	/// @return The instanced event.
-	static Event* InstanceEvent(Element* target, const String& name, const Dictionary& parameters, bool interruptible);
+	static EventPtr InstanceEvent(Element* target, EventId id, const String& type, const Dictionary& parameters, bool interruptible);
 
 
 	/// Register the instancer to be used for all event listeners.
 	/// Register the instancer to be used for all event listeners.
-	/// @return The registered instancer on success, NULL on failure.
-	static EventListenerInstancer* RegisterEventListenerInstancer(EventListenerInstancer* instancer);
-	/// Instance an event listener with the given string. This is used for instancing listeners for the on* events from
-	/// RML.
+	/// @lifetime The instancer must be kept alive until after the call to Core::Shutdown, or until a new instancer is set.
+	static void RegisterEventListenerInstancer(EventListenerInstancer* instancer);
+	/// Instance an event listener with the given string. This is used for instancing listeners for the on* events from RML.
 	/// @param[in] value The parameters to the event listener.
 	/// @param[in] value The parameters to the event listener.
 	/// @return The instanced event listener.
 	/// @return The instanced event listener.
 	static EventListener* InstanceEventListener(const String& value, Element* element);
 	static EventListener* InstanceEventListener(const String& value, Element* element);

+ 3 - 9
Include/RmlUi/Core/FileInterface.h

@@ -31,7 +31,7 @@
 
 
 #include "Header.h"
 #include "Header.h"
 #include "Types.h"
 #include "Types.h"
-#include "ReferenceCountable.h"
+#include "Traits.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -46,7 +46,7 @@ namespace Core {
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API FileInterface : public ReferenceCountable
+class RMLUICORE_API FileInterface : public NonCopyMoveable
 {
 {
 public:
 public:
 	FileInterface();
 	FileInterface();
@@ -54,7 +54,7 @@ public:
 
 
 	/// Opens a file.
 	/// Opens a file.
 	/// @param file The file handle to write to.
 	/// @param file The file handle to write to.
-	/// @return A valid file handle, or NULL on failure
+	/// @return A valid file handle, or nullptr on failure
 	virtual FileHandle Open(const String& path) = 0;
 	virtual FileHandle Open(const String& path) = 0;
 	/// Closes a previously opened file.
 	/// Closes a previously opened file.
 	/// @param file The file handle previously opened through Open().
 	/// @param file The file handle previously opened through Open().
@@ -82,12 +82,6 @@ public:
 	/// @param file The handle of the file to be queried.
 	/// @param file The handle of the file to be queried.
 	/// @return The length of the file in bytes.
 	/// @return The length of the file in bytes.
 	virtual size_t Length(FileHandle file);
 	virtual size_t Length(FileHandle file);
-
-	/// Called when this file interface is released.
-	virtual void Release();
-
-protected:
-	virtual void OnReferenceDeactivate();
 };
 };
 
 
 }
 }

+ 0 - 130
Include/RmlUi/Core/FontDatabase.h

@@ -1,130 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREFONTDATABASE_H
-#define RMLUICOREFONTDATABASE_H
-
-#include "StringUtilities.h"
-#include "Header.h"
-#include "Font.h"
-#include "FontProvider.h"
-
-namespace Rml {
-namespace Core {
-
-class FontEffect;
-class FontFamily;
-class FontFaceHandle;
-class PropertyDictionary;
-
-/**
-	The font database contains all font families currently in use by RmlUi.
-
-	@author Peter Curry
- */
-
-class RMLUICORE_API FontDatabase
-{
-public:
-
-    enum FontProviderType
-    {
-        FreeType = 0,
-        BitmapFont
-    };
-
-	static bool Initialise();
-	static void Shutdown();
-
-	/// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
-	/// @param[in] file_name The file to load the face from.
-	/// @return True if the face was loaded successfully, false otherwise.
-	static bool LoadFontFace(const String& file_name);
-	/// Adds a new font face to the database, ignoring any family, style and weight information stored in the face itself.
-	/// @param[in] file_name The file to load the face from.
-	/// @param[in] family The family to add the face to.
-	/// @param[in] style The style of the face (normal or italic).
-	/// @param[in] weight The weight of the face (normal or bold).
-	/// @return True if the face was loaded successfully, false otherwise.
-	static bool LoadFontFace(const String& file_name, const String& family, Font::Style style, Font::Weight weight);
-	/// Adds a new font face to the database, loading from memory. The face's family, style and weight will be determined from the face itself.
-	/// @param[in] data The font data.
-	/// @param[in] data_length Length of the data.
-	/// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(FontProviderType font_provider_type, const byte* data, int data_length);
-	/// Adds a new font face to the database, loading from memory.
-	/// @param[in] data The font data.
-	/// @param[in] data_length Length of the data.
-	/// @param[in] family The family to add the face to.
-	/// @param[in] style The style of the face (normal or italic).
-	/// @param[in] weight The weight of the face (normal or bold).
-	/// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(FontProviderType font_provider_type, const byte* data, int data_length, const String& family, Font::Style style, Font::Weight weight);
-
-	/// Returns a handle to a font face that can be used to position and render text. This will return the closest match
-	/// it can find, but in the event a font family is requested that does not exist, NULL will be returned instead of a
-	/// valid handle.
-	/// @param[in] family The family of the desired font handle.
-	/// @param[in] charset The set of characters required in the font face, as a comma-separated list of unicode ranges.
-	/// @param[in] style The style of the desired font handle.
-	/// @param[in] weight The weight of the desired font handle.
-	/// @param[in] size The size of desired handle, in points.
-	/// @return A valid handle if a matching (or closely matching) font face was found, NULL otherwise.
-	static FontFaceHandle* GetFontFaceHandle(const String& family, const String& charset, Font::Style style, Font::Weight weight, int size);
-
-	/// Returns a font effect, either a newly-instanced effect from the factory or an identical
-	/// shared effect.
-	/// @param[in] name The name of the desired font effect type.
-	/// @param[in] properties The properties associated with the font effect.
-	/// @return The requested font effect, or NULL if the font effect could not be found or instanced.
-	static FontEffect* GetFontEffect(const String& name, const PropertyDictionary& properties);
-
-	/// Removes a font effect from the font database's cache.
-	/// @param[in] The effect to release.
-	static void ReleaseFontEffect(const FontEffect* effect);
-
-    static void AddFontProvider(FontProvider * provider);
-
-    static void RemoveFontProvider(FontProvider * provider);
-
-private:
-	FontDatabase(void);
-	~FontDatabase(void);
-
-    static FontProviderType GetFontProviderType(const String& file_name);
-
-    typedef std::vector< FontProvider *> FontProviderTable;
-
-    static FontProviderTable font_provider_table;
-	static FontDatabase* instance;
-};
-
-}
-}
-
-#endif

+ 14 - 46
Include/RmlUi/Core/FontEffect.h

@@ -34,22 +34,19 @@
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
-class FontEffectInstancer;
-
 /**
 /**
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class FontEffect : public ReferenceCountable
+class FontEffect
 {
 {
 public:
 public:
+	// Behind or in front of main text
+	enum class Layer { Back, Front };
+
 	FontEffect();
 	FontEffect();
 	virtual ~FontEffect();
 	virtual ~FontEffect();
 
 
-	/// Returns the name of the effect; this is the type that instanced the effect.
-	/// @return The effect's name.
-	const String& GetName() const;
-
 	/// Asks the font effect if it requires, and will generate, its own unique texture. If it does
 	/// Asks the font effect if it requires, and will generate, its own unique texture. If it does
 	/// not, it will share the font's base layer's textures instead.
 	/// not, it will share the font's base layer's textures instead.
 	/// @return True if the effect generates its own textures, false if not. The default implementation returns false.
 	/// @return True if the effect generates its own textures, false if not. The default implementation returns false.
@@ -77,53 +74,24 @@ public:
 	/// @return The colour of the effect.
 	/// @return The colour of the effect.
 	const Colourb& GetColour() const;
 	const Colourb& GetColour() const;
 
 
-	/// Sets the z-index of the font effect. An effect with a higher z-index will be rendered after
-	/// an effect with a lower z-index. By default, all effects have a z-index of 0.
-	/// @param[in] z-index The new z-index of the effect.
-	void SetZIndex(float z_index);
-	/// Returns the font effect's z-index.
-	/// @return The z-index of the effect.
-	float GetZIndex() const;
-
-	/// Sets the specificity of the font effect.
-	/// @param[in] specificity The specificity of the effect.
-	void SetSpecificity(int specificity);
-	/// Returns the specificity of the font effect. This is used when multiple pseudo-classes are
-	/// active on an element, each with similarly-named font effects.
-	/// @return The specificity of the effect.
-	int GetSpecificity() const;
-
-	/// Returns the font effect's generation key.
-	/// @return A hash of the effect's properties used to generate the geometry and texture data.
-	const String& GetGenerationKey() const;
-
-protected:
-	/// Releases the effect through its instancer.
-	virtual void OnReferenceDeactivate();
+	Layer GetLayer() const;
+	void SetLayer(Layer layer);
 
 
-private:
-	FontEffectInstancer* instancer;
+	/// Returns the font effect's fingerprint.
+	/// @return A hash of the effect's type and properties used to generate the geometry and texture data.
+	size_t GetFingerprint() const;
+	void SetFingerprint(size_t fingerprint);
 
 
-	// The name of the effect.
-	String name;
+private:
+	Layer layer;
 
 
 	// The colour of the effect's geometry.
 	// The colour of the effect's geometry.
 	Colourb colour;
 	Colourb colour;
 
 
-	// The z-index of this font effect, used to resolve render order when multiple font effects are rendered.
-	float z_index;
-	// The maximum specificity of the properties used to define the font effect.
-	int specificity;
-
-	// A string identifying the properties that affected the generation of the effect's geometry and texture data.
-	String generation_key;
-
-	friend class Factory;
+	// A hash value identifying the properties that affected the generation of the effect's geometry and texture data.
+	size_t fingerprint;
 };
 };
 
 
-typedef std::vector< FontEffect* > FontEffectList;
-typedef std::unordered_map< String, FontEffect* > FontEffectMap;
-
 }
 }
 }
 }
 
 

+ 8 - 16
Include/RmlUi/Core/FontEffectInstancer.h

@@ -29,7 +29,7 @@
 #ifndef RMLUICOREFONTEFFECTINSTANCER_H
 #ifndef RMLUICOREFONTEFFECTINSTANCER_H
 #define RMLUICOREFONTEFFECTINSTANCER_H
 #define RMLUICOREFONTEFFECTINSTANCER_H
 
 
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "Header.h"
 #include "Header.h"
 #include "PropertyDictionary.h"
 #include "PropertyDictionary.h"
 #include "PropertySpecification.h"
 #include "PropertySpecification.h"
@@ -48,23 +48,18 @@ class FontEffect;
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API FontEffectInstancer : public ReferenceCountable
+class RMLUICORE_API FontEffectInstancer
 {
 {
 public:
 public:
 	FontEffectInstancer();
 	FontEffectInstancer();
 	virtual ~FontEffectInstancer();
 	virtual ~FontEffectInstancer();
 
 
 	/// Instances a font effect given the property tag and attributes from the RCSS file.
 	/// Instances a font effect given the property tag and attributes from the RCSS file.
-	/// @param[in] name The type of font effect desired. For example, "title-font-effect: outline;" is declared as type "outline".
+	/// @param[in] name The type of font effect desired. For example, "font-effect: outline(1px black);" is declared as type "outline".
 	/// @param[in] properties All RCSS properties associated with the font effect.
 	/// @param[in] properties All RCSS properties associated with the font effect.
-	/// @return The font effect if it was instanced successfully, NULL if an error occured.
-	virtual FontEffect* InstanceFontEffect(const String& name, const PropertyDictionary& properties) = 0;
-	/// Releases the given font effect.
-	/// @param[in] font_effect Font effect to release. This is guaranteed to have been constructed by this instancer.
-	virtual void ReleaseFontEffect(FontEffect* font_effect) = 0;
-
-	/// Releases the instancer.
-	virtual void Release() = 0;
+	/// @param[in] interface An interface for querying the active style sheet.
+	/// @return A shared_ptr to the font-effect if it was instanced successfully.
+	virtual SharedPtr<FontEffect> InstanceFontEffect(const String& name, const PropertyDictionary& properties) = 0;
 
 
 	/// Returns the property specification associated with the instancer.
 	/// Returns the property specification associated with the instancer.
 	const PropertySpecification& GetPropertySpecification() const;
 	const PropertySpecification& GetPropertySpecification() const;
@@ -81,16 +76,13 @@ protected:
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] type The type of shorthand to declare.
 	/// @param[in] type The type of shorthand to declare.
 	/// @param True if all the property names exist, false otherwise.
 	/// @param True if all the property names exist, false otherwise.
-	bool RegisterShorthand(const String& shorthand_name, const String& property_names, PropertySpecification::ShorthandType type = PropertySpecification::AUTO);
-
-	// Releases the instancer.
-	virtual void OnReferenceDeactivate();
+	ShorthandId RegisterShorthand(const String& shorthand_name, const String& property_names, ShorthandType type);
 
 
 private:
 private:
 	PropertySpecification properties;
 	PropertySpecification properties;
 
 
 	// Properties that define the geometry.
 	// Properties that define the geometry.
-	std::unordered_set< String > volatile_properties;
+	SmallUnorderedSet< PropertyId > volatile_properties;
 
 
 	friend class Factory;
 	friend class Factory;
 };
 };

+ 137 - 0
Include/RmlUi/Core/FontEngineInterface.h

@@ -0,0 +1,137 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREFONTENGINEINTERFACE_H
+#define RMLUICOREFONTENGINEINTERFACE_H
+
+#include "Header.h"
+#include "Types.h"
+#include "ComputedValues.h"
+#include "Geometry.h"
+
+namespace Rml {
+namespace Core {
+
+
+/**
+	The abstract base class for an application-specific font engine implementation.
+	
+	By default, RmlUi will use its own font engine with characters rendered through FreeType. To use your own engine,
+	provide a concrete implementation of this class and install it through Core::SetFontEngineInterface().
+ */
+
+
+class RMLUICORE_API FontEngineInterface
+{
+public:
+	FontEngineInterface();
+	virtual ~FontEngineInterface();
+
+	/// Called by RmlUi when it wants to load a font face from file.
+	/// @param[in] file_name The file to load the face from.
+	/// @param[in] fallback_face True to use this font face for unknown characters in other font faces.
+	/// @return True if the face was loaded successfully, false otherwise.
+	virtual bool LoadFontFace(const String& file_name, bool fallback_face);
+
+	/// Called by RmlUi when it wants to load a font face from memory, registered using the provided family, style, and weight.
+	/// @param[in] data A pointer to the data.
+	/// @param[in] data_size Size of the data in bytes.
+	/// @param[in] family The family to register the font as.
+	/// @param[in] style The style to register the font as.
+	/// @param[in] weight The weight to register the font as.
+	/// @param[in] fallback_face True to use this font face for unknown characters in other font faces.
+	/// @return True if the face was loaded successfully, false otherwise.
+	/// Note: The debugger plugin will load its embedded font faces through this method using the family name 'rmlui-debugger-font'.
+	virtual bool LoadFontFace(const byte* data, int data_size, const String& family, Style::FontStyle style, Style::FontWeight weight, bool fallback_face);
+
+	/// Called by RmlUi when a font configuration is resolved for an element. Should return a handle that 
+	/// can later be used to resolve properties of the face, and generate string geometry to be rendered.
+	/// @param[in] family The family of the desired font handle.
+	/// @param[in] style The style of the desired font handle.
+	/// @param[in] weight The weight of the desired font handle.
+	/// @param[in] size The size of desired handle, in points.
+	/// @return A valid handle if a matching (or closely matching) font face was found, NULL otherwise.
+	virtual FontFaceHandle GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size);
+
+	/// Called by RmlUi when a list of font effects is resolved for an element with a given font face.
+	/// @param[in] handle The font handle.
+	/// @param[in] font_effects The list of font effects to generate the configuration for.
+	/// @return A handle to the prepared font effects which will be used when generating geometry for a string.
+	virtual FontEffectsHandle PrepareFontEffects(FontFaceHandle handle, const FontEffectList &font_effects);
+
+	/// Should return the point size of this font face.
+	/// @param[in] handle The font handle.
+	/// @return The face's point size.
+	virtual int GetSize(FontFaceHandle handle);
+	/// Should return the pixel height of a lower-case x in this font face.
+	/// @param[in] handle The font handle.
+	/// @return The height of a lower-case x.
+	virtual int GetXHeight(FontFaceHandle handle);
+	/// Should return the default height between this font face's baselines.
+	/// @param[in] handle The font handle.
+	/// @return The default line height.
+	virtual int GetLineHeight(FontFaceHandle handle);
+
+	/// Should return the font's baseline, as a pixel offset from the bottom of the font.
+	/// @param[in] handle The font handle.
+	/// @return The font's baseline.
+	virtual int GetBaseline(FontFaceHandle handle);
+
+	/// Should return the font's underline, as a pixel offset from the bottom of the font.
+	/// @param[in] handle The font handle.
+	/// @param[out] thickness The font's underline thickness in pixels.
+	/// @return The underline pixel offset.
+	virtual float GetUnderline(FontFaceHandle handle, float &thickness);
+
+	/// Called by RmlUi when it wants to retrieve the width of a string when rendered with this handle.
+	/// @param[in] handle The font handle.
+	/// @param[in] string The string to measure.
+	/// @param[in] prior_character The optionally-specified character that immediately precedes the string. This may have an impact on the string width due to kerning.
+	/// @return The width, in pixels, this string will occupy if rendered with this handle.
+	virtual int GetStringWidth(FontFaceHandle handle, const String& string, Character prior_character = Character::Null);
+
+	/// Called by RmlUi when it wants to retrieve the geometry required to render a single line of text.
+	/// @param[in] face_handle The font handle.
+	/// @param[in] font_effects_handle The handle to the prepared font effects for which the geometry should be generated.
+	/// @param[in] string The string to render.
+	/// @param[in] position The position of the baseline of the first character to render.
+	/// @param[in] colour The colour to render the text.
+	/// @param[out] geometry An array of geometries to generate the geometry into.
+	/// @return The width, in pixels, of the string geometry.
+	virtual int GenerateString(FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string, const Vector2f& position, const Colourb& colour, GeometryList& geometry);
+
+	/// Called by RmlUi to determine if the text geometry is required to be re-generated. Whenever the returned version
+	/// is changed, all geometry belonging to the given face handle will be re-generated.
+	/// @param[in] face_handle The font handle.
+	/// @return The version required for using any geometry generated with the face handle.
+	virtual int GetVersion(FontFaceHandle handle);
+};
+
+}
+}
+
+#endif

+ 21 - 11
Include/RmlUi/Core/FontGlyph.h

@@ -29,7 +29,7 @@
 #ifndef RMLUICOREFONTGLYPH_H
 #ifndef RMLUICOREFONTGLYPH_H
 #define RMLUICOREFONTGLYPH_H
 #define RMLUICOREFONTGLYPH_H
 
 
-#include <vector>
+#include "Types.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -43,13 +43,8 @@ namespace Core {
 class FontGlyph
 class FontGlyph
 {
 {
 public:
 public:
-	FontGlyph() : character(0), dimensions(0,0), bearing(0,0), advance(0), bitmap_data(NULL),
-		bitmap_dimensions(0,0)
-	{
-	}
-
-	/// The unicode code point for this glyph.
-	word character;
+	FontGlyph() : dimensions(0,0), bearing(0,0), advance(0), bitmap_data(nullptr), bitmap_dimensions(0,0)
+	{}
 
 
 	/// The glyph's bounding box. Not to be confused with the dimensions of the glyph's bitmap!
 	/// The glyph's bounding box. Not to be confused with the dimensions of the glyph's bitmap!
 	Vector2i dimensions;
 	Vector2i dimensions;
@@ -61,13 +56,28 @@ public:
 	int advance;
 	int advance;
 
 
 	/// 8-bit opacity information for the glyph's bitmap. The size of the data is given by the
 	/// 8-bit opacity information for the glyph's bitmap. The size of the data is given by the
-	/// dimensions, below. This will be NULL if the glyph has no bitmap data.
-	byte* bitmap_data;
+	/// dimensions, below. This will be nullptr if the glyph has no bitmap data.
+	const byte* bitmap_data;
 	/// The dimensions of the glyph's bitmap.
 	/// The dimensions of the glyph's bitmap.
 	Vector2i bitmap_dimensions;
 	Vector2i bitmap_dimensions;
+
+	// Bitmap_data may point to this member or another font glyph data.
+	UniquePtr<byte[]> bitmap_owned_data;
+
+	// Create a copy with its bitmap data owned by another glyph.
+	FontGlyph WeakCopy() const 
+	{
+		FontGlyph glyph;
+		glyph.dimensions = dimensions;
+		glyph.bearing = bearing;
+		glyph.advance = advance;
+		glyph.bitmap_data = bitmap_data;
+		glyph.bitmap_dimensions = bitmap_dimensions;
+		return glyph;
+	}
 };
 };
 
 
-typedef std::vector< FontGlyph > FontGlyphList;
+using FontGlyphMap = UnorderedMap<Character, FontGlyph>;
 
 
 }
 }
 }
 }

+ 0 - 71
Include/RmlUi/Core/FontProvider.h

@@ -1,71 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREFONTPROVIDER_H
-#define RMLUICOREFONTPROVIDER_H
-
-#include "StringUtilities.h"
-#include "Font.h"
-#include "Header.h"
-
-namespace Rml {
-namespace Core {
-
-class FontFaceHandle;
-class FontFamily;
-
-/**
-    The font database contains all font families currently in use by RmlUi.
-    @author Peter Curry
- */
-
-class RMLUICORE_API FontProvider
-{
-public:
-
-    /// Returns a handle to a font face that can be used to position and render text. This will return the closest match
-    /// it can find, but in the event a font family is requested that does not exist, NULL will be returned instead of a
-    /// valid handle.
-    /// @param[in] family The family of the desired font handle.
-    /// @param[in] charset The set of characters required in the font face, as a comma-separated list of unicode ranges.
-    /// @param[in] style The style of the desired font handle.
-    /// @param[in] weight The weight of the desired font handle.
-    /// @param[in] size The size of desired handle, in points.
-    /// @return A valid handle if a matching (or closely matching) font face was found, NULL otherwise.
-    FontFaceHandle* GetFontFaceHandle(const String& family, const String& charset, Font::Style style, Font::Weight weight, int size);
-
-protected:
-
-    typedef std::map< String, FontFamily*, StringUtilities::StringComparei > FontFamilyMap;
-    FontFamilyMap font_families;
-};
-
-}
-}
-
-#endif

+ 0 - 102
Include/RmlUi/Core/FreeType/FontProvider.h

@@ -1,102 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREFREETYPEFONTPROVIDER_H
-#define RMLUICOREFREETYPEFONTPROVIDER_H
-
-#include "../StringUtilities.h"
-#include "../Font.h"
-#include "../FontProvider.h"
-
-namespace Rml {
-namespace Core {
-
-
-class FontEffect;
-class FontFaceHandle;
-class PropertyDictionary;
-
-namespace FreeType {
-
-class FontFamily;
-
-/**
-    The font database contains all font families currently in use by RmlUi.
-    @author Peter Curry
- */
-
-class RMLUICORE_API FontProvider : public Rml::Core::FontProvider
-{
-public:
-    static bool Initialise();
-    static void Shutdown();
-
-    /// Adds a new font face to the database. The face's family, style and weight will be determined from the face itself.
-    /// @param[in] file_name The file to load the face from.
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const String& file_name);
-    /// Adds a new font face to the database, ignoring any family, style and weight information stored in the face itself.
-    /// @param[in] file_name The file to load the face from.
-    /// @param[in] family The family to add the face to.
-    /// @param[in] style The style of the face (normal or italic).
-    /// @param[in] weight The weight of the face (normal or bold).
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const String& file_name, const String& family, Font::Style style, Font::Weight weight);
-    /// Adds a new font face to the database, loading from memory. The face's family, style and weight will be determined from the face itself.
-    /// @param[in] data The font data.
-    /// @param[in] data_length Length of the data.
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const byte* data, int data_length);
-    /// Adds a new font face to the database, loading from memory.
-    /// @param[in] data The font data.
-    /// @param[in] data_length Length of the data.
-    /// @param[in] family The family to add the face to.
-    /// @param[in] style The style of the face (normal or italic).
-    /// @param[in] weight The weight of the face (normal or bold).
-    /// @return True if the face was loaded successfully, false otherwise.
-    static bool LoadFontFace(const byte* data, int data_length, const String& family, Font::Style style, Font::Weight weight);
-
-private:
-    FontProvider(void);
-    ~FontProvider(void);
-
-    // Adds a loaded face to the appropriate font family.
-    bool AddFace(void* face, const String& family, Font::Style style, Font::Weight weight, bool release_stream);
-    // Loads a FreeType face.
-    void* LoadFace(const String& file_name);
-    // Loads a FreeType face from memory.
-    void* LoadFace(const byte* data, int data_length, const String& source, bool local_data);
-
-    static FontProvider* instance;
-};
-
-}
-}
-}
-
-#endif

+ 1 - 2
Include/RmlUi/Core/Geometry.h

@@ -49,7 +49,7 @@ struct Texture;
 class RMLUICORE_API Geometry
 class RMLUICORE_API Geometry
 {
 {
 public:
 public:
-	Geometry(Element* host_element = NULL);
+	Geometry(Element* host_element = nullptr);
 	Geometry(Context* host_context);
 	Geometry(Context* host_context);
 	~Geometry();
 	~Geometry();
 
 
@@ -91,7 +91,6 @@ private:
 
 
 	CompiledGeometryHandle compiled_geometry;
 	CompiledGeometryHandle compiled_geometry;
 	bool compile_attempted;
 	bool compile_attempted;
-	bool fixed_texcoords;
 };
 };
 
 
 typedef std::vector< Geometry > GeometryList;
 typedef std::vector< Geometry > GeometryList;

+ 8 - 0
Include/RmlUi/Core/GeometryUtilities.h

@@ -63,6 +63,14 @@ public:
 	/// @param[in] bottom_right_texcoord The texture coordinates at the bottom-right of the quad.
 	/// @param[in] bottom_right_texcoord The texture coordinates at the bottom-right of the quad.
 	/// @param[in] index_offset The offset to be added to the generated indices; this should be the number of vertices already in the array.
 	/// @param[in] index_offset The offset to be added to the generated indices; this should be the number of vertices already in the array.
 	static void GenerateQuad(Vertex* vertices, int* indices, const Vector2f& origin, const Vector2f& dimensions, const Colourb& colour, const Vector2f& top_left_texcoord, const Vector2f& bottom_right_texcoord, int index_offset = 0);
 	static void GenerateQuad(Vertex* vertices, int* indices, const Vector2f& origin, const Vector2f& dimensions, const Colourb& colour, const Vector2f& top_left_texcoord, const Vector2f& bottom_right_texcoord, int index_offset = 0);
+	
+	/// Generates the geometry required to render a line above, below or through a line of text.
+	/// @param[out] geometry The geometry to append the newly created geometry into.
+	/// @param[in] position The position of the baseline of the lined text.
+	/// @param[in] width The width of the string to line.
+	/// @param[in] decoration_type The type for vertical positioning of line.
+	/// @param[in] colour The colour to draw the line in.
+	static void GenerateLine(FontFaceHandle font_face_handle, Geometry* geometry, const Vector2f& position, int width, Style::TextDecoration decoration_type, const Colourb& colour);
 
 
 private:
 private:
 	GeometryUtilities();
 	GeometryUtilities();

+ 206 - 0
Include/RmlUi/Core/ID.h

@@ -0,0 +1,206 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+ 
+#ifndef RMLUICOREID_H
+#define RMLUICOREID_H
+
+namespace Rml {
+namespace Core {
+
+
+enum class ShorthandId : uint16_t
+{
+	Invalid,
+
+	/*
+	  The following values define the shorthand ids for the main stylesheet specification.
+	  These values must not be used in places that have their own property specification,
+	  such as decorators and font-effects.
+	*/
+	Margin,
+	Padding,
+	BorderWidth,
+	BorderColor,
+	BorderTop,
+	BorderRight,
+	BorderBottom,
+	BorderLeft,
+	Border,
+	Overflow,
+	Background,
+	Font,
+	PerspectiveOrigin,
+	TransformOrigin,
+
+	NumDefinedIds,
+	FirstCustomId = NumDefinedIds
+};
+
+
+enum class PropertyId : uint16_t
+{
+	Invalid,
+
+	/*
+	  The following values define the property ids for the main stylesheet specification.
+	  These values must not be used in places that have their own property specification,
+	  such as decorators and font-effects.
+	*/
+	MarginTop,
+	MarginRight,
+	MarginBottom,
+	MarginLeft,
+	PaddingTop,
+	PaddingRight,
+	PaddingBottom,
+	PaddingLeft,
+	BorderTopWidth,
+	BorderRightWidth,
+	BorderBottomWidth,
+	BorderLeftWidth,
+	BorderTopColor,
+	BorderRightColor,
+	BorderBottomColor,
+	BorderLeftColor,
+	Display,
+	Position,
+	Top,
+	Right,
+	Bottom,
+	Left,
+	Float,
+	Clear,
+	ZIndex,
+	Width,
+	MinWidth,
+	MaxWidth,
+	Height,
+	MinHeight,
+	MaxHeight,
+	LineHeight,
+	VerticalAlign,
+	OverflowX,
+	OverflowY,
+	Clip,
+	Visibility,
+	BackgroundColor,
+	Color,
+	ImageColor,
+	FontFamily,
+	FontStyle,
+	FontWeight,
+	FontSize,
+	TextAlign,
+	TextDecoration,
+	TextTransform,
+	WhiteSpace,
+	Cursor,
+	Drag,
+	TabIndex,
+	ScrollbarMargin,
+
+	Perspective,
+	PerspectiveOriginX,
+	PerspectiveOriginY,
+	Transform,
+	TransformOriginX,
+	TransformOriginY,
+	TransformOriginZ,
+
+	Transition,
+	Animation,
+
+	Opacity,
+	PointerEvents,
+	Focus,
+
+	Decorator,
+	FontEffect,
+
+	NumDefinedIds,
+	FirstCustomId = NumDefinedIds
+};
+
+
+
+enum class EventId : uint16_t 
+{
+	Invalid,
+
+	// Core events
+	Mousedown,
+	Mousescroll,
+	Mouseover,
+	Mouseout,
+	Focus,
+	Blur,
+	Keydown,
+	Keyup,
+	Textinput,
+	Mouseup,
+	Click,
+	Dblclick,
+	Load,
+	Unload,
+	Show,
+	Hide,
+	Mousemove,
+	Dragmove,
+	Drag,
+	Dragstart,
+	Dragover,
+	Dragdrop,
+	Dragout,
+	Dragend,
+	Handledrag,
+	Resize,
+	Scroll,
+	Animationend,
+	Transitionend,
+
+	// Controls events
+	Change,
+	Submit,
+	Tabchange,
+	Columnadd,
+	Rowadd,
+	Rowchange,
+	Rowremove,
+	Rowupdate,
+
+	NumDefinedIds,
+
+	// Custom IDs start here
+	FirstCustomId = NumDefinedIds
+};
+
+}
+}
+
+#endif

+ 37 - 43
Include/RmlUi/Core/Lua/Interpreter.h

@@ -37,6 +37,9 @@ namespace Rml {
 namespace Core {
 namespace Core {
 namespace Lua {
 namespace Lua {
 
 
+class LuaDocumentElementInstancer;
+class LuaEventListenerInstancer;
+
 /**
 /**
     This initializes the Lua interpreter, and has functions to load the scripts or
     This initializes the Lua interpreter, and has functions to load the scripts or
     call functions that exist in Lua.
     call functions that exist in Lua.
@@ -46,6 +49,24 @@ namespace Lua {
 class RMLUILUA_API Interpreter : public Plugin
 class RMLUILUA_API Interpreter : public Plugin
 {
 {
 public:
 public:
+	/** Creates the plugin.
+	@remark This is equivilent to calling Initialise(nullptr). */
+	static void Initialise();
+
+	/** Creates the plugin and adds RmlUi to an existing Lua state if one is provided.
+	 @remark If nullptr is passed as an argument, the plugin will automatically create the lua state during initialisation
+	   and close the state during the call to Rml::Core::Shutdown(). Otherwise, if a Lua state is provided, the user is 
+	   responsible for closing the provided Lua state. The state must then be closed after the call to Rml::Core::Shutdown().
+	 @remark The plugin registers the "body" tag to generate a LuaDocument rather than a Rml::Core::ElementDocument. */
+	static void Initialise(lua_State* L);
+
+	/**
+	@return The lua_State that the Interpreter created in Interpreter::Startup()
+	@remark This class lacks a SetLuaState for a reason. If you have to use a seperate Lua binding and want to keep the types
+	from RmlUi, then use this lua_State; it will already have all of the libraries loaded, and all of the types defined.
+	Alternatively, you can call RegisterCoreTypes(lua_State*) with your own Lua state if you need them defined in it. */
+	static lua_State* GetLuaState();
+
     /** This function calls luaL_loadfile and then lua_pcall, reporting the errors (if any)
     /** This function calls luaL_loadfile and then lua_pcall, reporting the errors (if any)
     @param[in] file Fully qualified file name to execute.
     @param[in] file Fully qualified file name to execute.
     @remark Somewhat misleading name if you are used to the Lua meaning of "load file". It behaves
     @remark Somewhat misleading name if you are used to the Lua meaning of "load file". It behaves
@@ -77,53 +98,26 @@ public:
     /** removes 'res' number of items from the stack
     /** removes 'res' number of items from the stack
     @param[in] res Number of results to remove from the stack.   */
     @param[in] res Number of results to remove from the stack.   */
     static void EndCall(int res = 0);
     static void EndCall(int res = 0);
-
-    /** This will populate the global Lua table with all of the Lua core types by calling LuaType<T>::Register
-    @param[in] L The lua_State to use to register the types
-    @remark This is called automatically inside of Interpreter::Startup(), so you do not have to 
-    call this function upon initialization of the Interpreter. If you are using RmlControlsLua, then you
-    \em will need to call Rml::Controls::Lua::RegisterTypes(lua_State*)     */
-    static void RegisterCoreTypes(lua_State* L);
-
-    /** 
-    @return The lua_State that the Interpreter created in Interpreter::Startup()
-    @remark This class lacks a SetLuaState for a reason. If you have to use a seperate Lua binding and want to keep the types
-    from RmlUi, then use this lua_State; it will already have all of the libraries loaded, and all of the types defined.
-    Alternatively, you can call RegisterCoreTypes(lua_State*) with your own Lua state if you need them defined in it. */
-    static lua_State* GetLuaState();
-
-    /** Creates the plugin. 
-	@remark This is equivilent to calling Initialise(NULL).
-      */
-    static void Initialise();
-    /** Creates the plugin and adds RmlUi to an existing Lua context if one is provided.
-	 @remark Call this function only once, and special care must be taken when destroying the lua_State passed to this method.
-	 Interpreter::Shutdown() calles lua_close on the lua_State pointer provided here, do not call Interpreter::Shutdown if you
-	 must call lua_close yourself or if you need to continue to use the lua_State pointer provided here.  Internally, it calls
-	 Interpreter::Startup() and registers the "body" tag to generate a LuaDocument rather than a Rml::Core::ElementDocument.
-	 If the argument provided is NULL, a Lua context is created automatically instead. */
-    static void Initialise(lua_State *_L);
-
-    /** Stops the plugin by calling lua_close
-	 @remark Shutdown calls lua_Close on the lua_State associated with the Interpreter.  If a lua_State was provided in the
-	 original call to Initialise, Shutdown should not be called OR you must not call lua_Close from within your code. */
-	static void Shutdown();
     
     
-    /** @sa Rml::Core::Plugin::GetEventClasses */
-    virtual int GetEventClasses();
-    /** @sa Rml::Core::Plugin::OnInitialise */
-    virtual void OnInitialise();
-    /** Currently does nothing. You must call Interpreter::Shutdown yourself at the appropriate time.
-    @sa Rml::Core::Plugin::OnShutdown    */
-    virtual void OnShutdown();
 private:
 private:
-    /** Creates a lua_State for @var _L and calls luaL_openlibs, then calls Interpreter::RegisterCoreTypes(lua_State*)
-    @remark called by Interpreter::Initialise()    */
-    void Startup();
+    int GetEventClasses() override;
+    
+	void OnInitialise() override;
+    
+	void OnShutdown() override;
+
+	/** This will populate the global Lua table with all of the Lua core types by calling LuaType<T>::Register
+	@param[in] L The lua_State to use to register the types
+	@remark This is called automatically inside of Interpreter::Startup(), so you do not have to
+	call this function upon initialization of the Interpreter. If you are using RmlControlsLua, then you
+	\em will need to call Rml::Controls::Lua::RegisterTypes(lua_State*)     */
+	static void RegisterCoreTypes(lua_State* L);
 
 
-    /** Lua state that Interpreter::Initialise() creates.    */
-    static lua_State* _L;
+	LuaDocumentElementInstancer* lua_document_element_instancer = nullptr;
+	LuaEventListenerInstancer* lua_event_listener_instancer = nullptr;
+	bool owns_lua_state = false;
 };
 };
+
 }
 }
 }
 }
 }
 }

+ 9 - 11
Include/RmlUi/Core/Lua/LuaType.h

@@ -51,27 +51,25 @@
 #define LUASETTER(type,varname) { #varname, type##SetAttr##varname },
 #define LUASETTER(type,varname) { #varname, type##SetAttr##varname },
 
 
 #define CHECK_BOOL(L,narg) (lua_toboolean((L),(narg)) > 0 ? true : false )
 #define CHECK_BOOL(L,narg) (lua_toboolean((L),(narg)) > 0 ? true : false )
-#define LUACHECKOBJ(obj) if((obj) == NULL) { lua_pushnil(L); return 1; }
+#define LUACHECKOBJ(obj) if((obj) == nullptr) { lua_pushnil(L); return 1; }
 
 
  /** Used to remove repetitive typing at the cost of flexibility. When you use this, you @em must have 
  /** Used to remove repetitive typing at the cost of flexibility. When you use this, you @em must have 
  functions with the same name as defined in the macro. For example, if you used @c Element as type, you would
  functions with the same name as defined in the macro. For example, if you used @c Element as type, you would
  have to have functions named @c ElementMethods, @c ElementGetters, @c ElementSetters that return the appropriate
  have to have functions named @c ElementMethods, @c ElementGetters, @c ElementSetters that return the appropriate
  types.
  types.
  @param is_reference_counted true if the type inherits from Rml::Core::ReferenceCountable, false otherwise*/
  @param is_reference_counted true if the type inherits from Rml::Core::ReferenceCountable, false otherwise*/
-#define LUACORETYPEDEFINE(type,is_ref_counted) \
+#define LUACORETYPEDEFINE(type) \
     template<> const char* GetTClassName<type>() { return #type; } \
     template<> const char* GetTClassName<type>() { return #type; } \
     template<> RegType<type>* GetMethodTable<type>() { return type##Methods; } \
     template<> RegType<type>* GetMethodTable<type>() { return type##Methods; } \
     template<> luaL_Reg* GetAttrTable<type>() { return type##Getters; } \
     template<> luaL_Reg* GetAttrTable<type>() { return type##Getters; } \
     template<> luaL_Reg* SetAttrTable<type>() { return type##Setters; } \
     template<> luaL_Reg* SetAttrTable<type>() { return type##Setters; } \
-    template<> bool IsReferenceCounted<type>() { return (is_ref_counted); } \
 
 
 //We can't use LUACORETYPEDEFINE due to namespace issues
 //We can't use LUACORETYPEDEFINE due to namespace issues
-#define LUACONTROLSTYPEDEFINE(type,is_ref_counted) \
+#define LUACONTROLSTYPEDEFINE(type) \
     template<> const char* GetTClassName<type>() { return #type; } \
     template<> const char* GetTClassName<type>() { return #type; } \
     template<> RegType<type>* GetMethodTable<type>() { return Rml::Controls::Lua::type##Methods; } \
     template<> RegType<type>* GetMethodTable<type>() { return Rml::Controls::Lua::type##Methods; } \
     template<> luaL_Reg* GetAttrTable<type>() { return Rml::Controls::Lua::type##Getters; } \
     template<> luaL_Reg* GetAttrTable<type>() { return Rml::Controls::Lua::type##Getters; } \
     template<> luaL_Reg* SetAttrTable<type>() { return Rml::Controls::Lua::type##Setters; } \
     template<> luaL_Reg* SetAttrTable<type>() { return Rml::Controls::Lua::type##Setters; } \
-    template<> bool IsReferenceCounted<type>() { return (is_ref_counted); } \
 
 
 /** Used to remove repetitive typing at the cost of flexibility. It creates function prototypes for
 /** Used to remove repetitive typing at the cost of flexibility. It creates function prototypes for
 getting the name of the type, method tables, and if it is reference counted.
 getting the name of the type, method tables, and if it is reference counted.
@@ -82,7 +80,6 @@ the LUACORETYPEDEFINE macro, or make sure that the function signatures are @em e
     template<> RMLUILUA_API RegType<type>* GetMethodTable<type>(); \
     template<> RMLUILUA_API RegType<type>* GetMethodTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* GetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* GetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* SetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* SetAttrTable<type>(); \
-    template<> RMLUILUA_API bool IsReferenceCounted<type>(); \
 
 
 /** Used to remove repetitive typing at the cost of flexibility. It creates function prototypes for
 /** Used to remove repetitive typing at the cost of flexibility. It creates function prototypes for
 getting the name of the type, method tables, and if it is reference counted.
 getting the name of the type, method tables, and if it is reference counted.
@@ -93,7 +90,6 @@ the LUACORETYPEDEFINE macro, or make sure that the function signatures are @em e
     template<> RMLUILUA_API RegType<type>* GetMethodTable<type>(); \
     template<> RMLUILUA_API RegType<type>* GetMethodTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* GetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* GetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* SetAttrTable<type>(); \
     template<> RMLUILUA_API luaL_Reg* SetAttrTable<type>(); \
-    template<> RMLUILUA_API bool IsReferenceCounted<type>(); \
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -114,8 +110,7 @@ template<typename T> RMLUILUA_API luaL_Reg* GetAttrTable();
 template<typename T> RMLUILUA_API luaL_Reg* SetAttrTable();
 template<typename T> RMLUILUA_API luaL_Reg* SetAttrTable();
 /** String representation of the class */
 /** String representation of the class */
 template<typename T> RMLUILUA_API const char* GetTClassName();
 template<typename T> RMLUILUA_API const char* GetTClassName();
-/** bool for if it is reference counted */
-template<typename T> RMLUILUA_API bool IsReferenceCounted();
+
 /** gets called from the LuaType<T>::Register function, right before @c _regfunctions.
 /** gets called from the LuaType<T>::Register function, right before @c _regfunctions.
 If you want to inherit from another class, in the function you would want
 If you want to inherit from another class, in the function you would want
 to call @c _regfunctions<superclass>, where method is metatable_index - 1. Anything
 to call @c _regfunctions<superclass>, where method is metatable_index - 1. Anything
@@ -149,14 +144,14 @@ public:
     static inline int push(lua_State *L, T* obj, bool gc=false);
     static inline int push(lua_State *L, T* obj, bool gc=false);
     /** Statically casts the item at the position on the Lua stack
     /** Statically casts the item at the position on the Lua stack
     @param narg[in] Position of the item to cast on the Lua stack
     @param narg[in] Position of the item to cast on the Lua stack
-    @return A pointer to an object of type T or @c NULL   */
+    @return A pointer to an object of type T or @c nullptr   */
     static inline T* check(lua_State* L, int narg);
     static inline T* check(lua_State* L, int narg);
 
 
     /** For calling a C closure with upvalues. Used by the functions defined by RegType
     /** For calling a C closure with upvalues. Used by the functions defined by RegType
     @return The value that RegType.func returns   */
     @return The value that RegType.func returns   */
     static inline int thunk(lua_State* L);
     static inline int thunk(lua_State* L);
     /** String representation of the pointer. Called by the __tostring metamethod  */
     /** String representation of the pointer. Called by the __tostring metamethod  */
-    static inline void tostring(char* buff, void* obj);
+    static inline void tostring(char* buff, size_t buff_size, void* obj);
     //these are metamethods
     //these are metamethods
     /** The __gc metamethod. If the object was pushed by push(lua_State*,T*,bool) with the third
     /** The __gc metamethod. If the object was pushed by push(lua_State*,T*,bool) with the third
     argument as true, it will either decrease the reference count or call delete depending on if
     argument as true, it will either decrease the reference count or call delete depending on if
@@ -177,7 +172,10 @@ public:
     metatable, and the getters exist in __getters and setters in __setters. The reason for __getters and __setters
     metatable, and the getters exist in __getters and setters in __setters. The reason for __getters and __setters
     is to have the objects use a 'dot' syntax for properties and a 'colon' syntax for methods.*/
     is to have the objects use a 'dot' syntax for properties and a 'colon' syntax for methods.*/
     static inline void _regfunctions(lua_State* L, int meta, int method);
     static inline void _regfunctions(lua_State* L, int meta, int method);
+
 private:
 private:
+	static constexpr size_t max_pointer_string_size = 32;
+
     LuaType(); //hide constructor
     LuaType(); //hide constructor
 
 
 };
 };

+ 23 - 40
Include/RmlUi/Core/Lua/LuaType.inl

@@ -88,13 +88,13 @@ int LuaType<T>::push(lua_State *L, T* obj, bool gc)
     int mt = lua_gettop(L); //mt = 1
     int mt = lua_gettop(L); //mt = 1
     T** ptrHold = (T**)lua_newuserdata(L,sizeof(T**)); //->[2] = empty userdata
     T** ptrHold = (T**)lua_newuserdata(L,sizeof(T**)); //->[2] = empty userdata
     int ud = lua_gettop(L); //ud = 2
     int ud = lua_gettop(L); //ud = 2
-    if(ptrHold != NULL)
+    if(ptrHold != nullptr)
     {
     {
         *ptrHold = obj; 
         *ptrHold = obj; 
         lua_pushvalue(L, mt); // ->[3] = copy of [1]
         lua_pushvalue(L, mt); // ->[3] = copy of [1]
         lua_setmetatable(L, -2); //[-2 = 2] -> [2]'s metatable = [3]; pop [3]
         lua_setmetatable(L, -2); //[-2 = 2] -> [2]'s metatable = [3]; pop [3]
-        char name[32];
-        tostring(name,obj);
+        char name[max_pointer_string_size];
+        tostring(name, max_pointer_string_size, obj);
         lua_getfield(L,LUA_REGISTRYINDEX,"DO NOT TRASH"); //->[3] = value returned from function
         lua_getfield(L,LUA_REGISTRYINDEX,"DO NOT TRASH"); //->[3] = value returned from function
         if(lua_isnil(L,-1) ) //if [3] hasn't been created yet, then create it
         if(lua_isnil(L,-1) ) //if [3] hasn't been created yet, then create it
         {
         {
@@ -116,13 +116,6 @@ int LuaType<T>::push(lua_State *L, T* obj, bool gc)
             lua_setfield(L,-2,name); //represents t[k] = v, [-2 = 3] = t -> v = [4], k = <ClassName>; pop [4]
             lua_setfield(L,-2,name); //represents t[k] = v, [-2 = 3] = t -> v = [4], k = <ClassName>; pop [4]
         }
         }
 
 
-        if(IsReferenceCounted<T>())
-        {
-            //If you look at the gc_T function, reference countables do not
-            //care about the "DO NOT TRASH" table
-            ((Rml::Core::ReferenceCountable*)obj)->AddReference();
-        }
-
         lua_pop(L,1); // -> pop [3]
         lua_pop(L,1); // -> pop [3]
     }
     }
     lua_settop(L,ud); //[ud = 2] -> remove everything that is above 2, top = [2]
     lua_settop(L,ud); //[ud = 2] -> remove everything that is above 2, top = [2]
@@ -136,8 +129,8 @@ template<typename T>
 T* LuaType<T>::check(lua_State* L, int narg)
 T* LuaType<T>::check(lua_State* L, int narg)
 {
 {
     T** ptrHold = static_cast<T**>(lua_touserdata(L,narg));
     T** ptrHold = static_cast<T**>(lua_touserdata(L,narg));
-    if(ptrHold == NULL)
-        return NULL;
+    if(ptrHold == nullptr)
+        return nullptr;
     return (*ptrHold);
     return (*ptrHold);
 }
 }
 
 
@@ -152,9 +145,9 @@ int LuaType<T>::thunk(lua_State* L)
     lua_remove(L, 1);  // remove self so member function args start at index 1
     lua_remove(L, 1);  // remove self so member function args start at index 1
     // get member function from upvalue
     // get member function from upvalue
     RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
     RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
-    //at the moment, there isn't a case where NULL is acceptable to be used in the function, so check
+    //at the moment, there isn't a case where nullptr is acceptable to be used in the function, so check
     //for it here, rather than individually for each function
     //for it here, rather than individually for each function
-    if(obj == NULL)
+    if(obj == nullptr)
     {
     {
         lua_pushnil(L);
         lua_pushnil(L);
         return 1;
         return 1;
@@ -166,9 +159,9 @@ int LuaType<T>::thunk(lua_State* L)
 
 
 
 
 template<typename T>
 template<typename T>
-void LuaType<T>::tostring(char* buff, void* obj)
+void LuaType<T>::tostring(char* buff, size_t buff_size, void* obj)
 {
 {
-    sprintf(buff,"%p",obj);
+    snprintf(buff, buff_size, "%p", obj);
 }
 }
 
 
 
 
@@ -176,30 +169,20 @@ template<typename T>
 int LuaType<T>::gc_T(lua_State* L)
 int LuaType<T>::gc_T(lua_State* L)
 {
 {
     T * obj = check(L,1); //[1] = this userdata
     T * obj = check(L,1); //[1] = this userdata
-    if(obj == NULL)
+    if(obj == nullptr)
         return 0;
         return 0;
-    if(IsReferenceCounted<T>())
-    {
-        // ReferenceCountables do not care about the "DO NOT TRASH" table.
-        // Because userdata is pushed which contains a pointer to the pointer
-        // of 'obj', 'obj' will be garbage collected for every time 'obj' was pushed.
-        ((Rml::Core::ReferenceCountable*)obj)->RemoveReference();
-        return 0;
-    }
+
     lua_getfield(L,LUA_REGISTRYINDEX,"DO NOT TRASH"); //->[2] = return value from this
     lua_getfield(L,LUA_REGISTRYINDEX,"DO NOT TRASH"); //->[2] = return value from this
     if(lua_istable(L,-1) ) //[-1 = 2], if it is a table
     if(lua_istable(L,-1) ) //[-1 = 2], if it is a table
     {
     {
-        char name[32];
-        tostring(name,obj);
-        lua_getfield(L,-1, std::string(name).c_str()); //[-1 = 2] -> [3] = the value returned from if <ClassName> exists in the table to not gc
+        char name[max_pointer_string_size];
+        tostring(name, max_pointer_string_size, obj);
+        lua_getfield(L,-1,name); //[-1 = 2] -> [3] = the value returned from if <ClassName> exists in the table to not gc
         if(lua_isnoneornil(L,-1) ) //[-1 = 3] if it doesn't exist, then we are free to garbage collect c++ side
         if(lua_isnoneornil(L,-1) ) //[-1 = 3] if it doesn't exist, then we are free to garbage collect c++ side
-        {
-            if(!IsReferenceCounted<T>())
-            {
-                delete obj;
-                obj = NULL;
-            }
-        }
+		{
+			delete obj;
+			obj = nullptr;
+		}
     }
     }
     lua_pop(L,3); //balance function
     lua_pop(L,3); //balance function
     return 0;
     return 0;
@@ -209,10 +192,10 @@ int LuaType<T>::gc_T(lua_State* L)
 template<typename T>
 template<typename T>
 int LuaType<T>::tostring_T(lua_State* L)
 int LuaType<T>::tostring_T(lua_State* L)
 {
 {
-    char buff[32];
+    char buff[max_pointer_string_size];
     T** ptrHold = (T**)lua_touserdata(L,1);
     T** ptrHold = (T**)lua_touserdata(L,1);
     T *obj = *ptrHold;
     T *obj = *ptrHold;
-    sprintf(buff, "%p", obj);
+    snprintf(buff, max_pointer_string_size, "%p", obj);
     lua_pushfstring(L, "%s (%s)", GetTClassName<T>(), buff);
     lua_pushfstring(L, "%s (%s)", GetTClassName<T>(), buff);
     return 1;
     return 1;
 }
 }
@@ -243,7 +226,7 @@ int LuaType<T>::index(lua_State* L)
             {
             {
                 lua_pushvalue(L,1); //push the userdata to the stack [6]
                 lua_pushvalue(L,1); //push the userdata to the stack [6]
                 if(lua_pcall(L,1,1,0) != 0) //remove one, result is at [6]
                 if(lua_pcall(L,1,1,0) != 0) //remove one, result is at [6]
-                    Report(L, String(GetTClassName<T>()).Append(".__index for ").Append(lua_tostring(L,2)).Append(": "));
+                    Report(L, String(GetTClassName<T>()).append(".__index for ").append(lua_tostring(L,2)).append(": "));
             }
             }
             else
             else
             {
             {
@@ -257,7 +240,7 @@ int LuaType<T>::index(lua_State* L)
                         lua_pushvalue(L,1); //[1] = object -> [7] = object
                         lua_pushvalue(L,1); //[1] = object -> [7] = object
                         lua_pushvalue(L,2); //[2] = key -> [8] = key
                         lua_pushvalue(L,2); //[2] = key -> [8] = key
                         if(lua_pcall(L,2,1,0) != 0) //call function at top of stack (__index) -> pop top 2 as args; [7] = return value
                         if(lua_pcall(L,2,1,0) != 0) //call function at top of stack (__index) -> pop top 2 as args; [7] = return value
-                            Report(L, String(GetTClassName<T>()).Append(".__index for ").Append(lua_tostring(L,2)).Append(": "));
+                            Report(L, String(GetTClassName<T>()).append(".__index for ").append(lua_tostring(L,2)).append(": "));
                     }
                     }
                     else if(lua_istable(L,-1) )
                     else if(lua_istable(L,-1) )
                         lua_getfield(L,-1,key); //shorthand version of above -> [7] = return value
                         lua_getfield(L,-1,key); //shorthand version of above -> [7] = return value
@@ -299,7 +282,7 @@ int LuaType<T>::newindex(lua_State* L)
         lua_pushvalue(L,1); //userdata at [7]
         lua_pushvalue(L,1); //userdata at [7]
         lua_pushvalue(L,3); //[8] = copy of [3]
         lua_pushvalue(L,3); //[8] = copy of [3]
         if(lua_pcall(L,2,0,0) != 0) //call function, pop 2 off push 0 on
         if(lua_pcall(L,2,0,0) != 0) //call function, pop 2 off push 0 on
-            Report(L, String(GetTClassName<T>()).Append(".__newindex for ").Append(lua_tostring(L,2)).Append(": ")); 
+            Report(L, String(GetTClassName<T>()).append(".__newindex for ").append(lua_tostring(L,2)).append(": ")); 
     }
     }
     else
     else
         lua_pop(L,1); //not a setter function.
         lua_pop(L,1); //not a setter function.

+ 2 - 2
Include/RmlUi/Core/Lua/Utilities.h

@@ -42,14 +42,14 @@ namespace Lua {
 
 
 /** casts the variant to its specific type before pushing it to the stack 
 /** casts the variant to its specific type before pushing it to the stack 
 @relates Rml::Core::Lua::LuaType */
 @relates Rml::Core::Lua::LuaType */
-void RMLUILUA_API PushVariant(lua_State* L, Variant* var);
+void RMLUILUA_API PushVariant(lua_State* L, const Variant* var);
 
 
 /** If there are errors on the top of the stack, this will print those out to the log.
 /** If there are errors on the top of the stack, this will print those out to the log.
 @param L A Lua state, and if not passed in, will use the Interpreter's state
 @param L A Lua state, and if not passed in, will use the Interpreter's state
 @param place A string that will be printed to the log right before the error message, seperated by a space. Set
 @param place A string that will be printed to the log right before the error message, seperated by a space. Set
 this when you would get no information about where the error happens.
 this when you would get no information about where the error happens.
 @relates Rml::Core::Lua::Interpreter   */
 @relates Rml::Core::Lua::Interpreter   */
-void RMLUILUA_API Report(lua_State* L = NULL, const Rml::Core::String& place = "");
+void RMLUILUA_API Report(lua_State* L = nullptr, const Rml::Core::String& place = "");
 
 
 //Helper function, so that the types don't have to define individual functions themselves
 //Helper function, so that the types don't have to define individual functions themselves
 // to fill the Elements.As table
 // to fill the Elements.As table

+ 5 - 2
Include/RmlUi/Core/Matrix4.h

@@ -4,6 +4,7 @@
  * For the latest information, see http://github.com/mikke89/RmlUi
  * For the latest information, see http://github.com/mikke89/RmlUi
  *
  *
  * Copyright (c) 2014 Markus Schöngart
  * Copyright (c) 2014 Markus Schöngart
+ * Copyright (c) 2019 The RmlUi Team, and contributors
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * of this software and associated documentation files (the "Software"), to deal
@@ -422,8 +423,7 @@ class Matrix4
 			{ return MatrixMultiplier< Component, Storage, Storage2 >::Multiply(*this, other); }
 			{ return MatrixMultiplier< Component, Storage, Storage2 >::Multiply(*this, other); }
 
 
 		/// Multiplies this matrix by another matrix in place.
 		/// Multiplies this matrix by another matrix in place.
-		/// @param[in] other The scalar value to multiply by.
-		/// @return The result of the scale.
+		/// @return The result of the multiplication.
 		inline const ThisType& operator*=(const ThisType& other) noexcept
 		inline const ThisType& operator*=(const ThisType& other) noexcept
 			{ *this = *this * other; return *this; }
 			{ *this = *this * other; return *this; }
 		inline const ThisType& operator*=(const TransposeType& other) noexcept
 		inline const ThisType& operator*=(const TransposeType& other) noexcept
@@ -465,6 +465,9 @@ class Matrix4
 		/// @param f The depth coordinate of the far clipping plane
 		/// @param f The depth coordinate of the far clipping plane
 		/// @return The specified perspective projection matrix.
 		/// @return The specified perspective projection matrix.
 		static ThisType ProjectPerspective(Component l, Component r, Component b, Component t, Component n, Component f) noexcept;
 		static ThisType ProjectPerspective(Component l, Component r, Component b, Component t, Component n, Component f) noexcept;
+		/// Create a perspective projection matrix
+		/// @param d The distance to the z-plane
+		static ThisType Perspective(Component d) noexcept;
 
 
 		/// Return a translation matrix.
 		/// Return a translation matrix.
 		/// @return A translation matrix.
 		/// @return A translation matrix.

+ 13 - 0
Include/RmlUi/Core/Matrix4.inl

@@ -4,6 +4,7 @@
  * For the latest information, see http://github.com/mikke89/RmlUi
  * For the latest information, see http://github.com/mikke89/RmlUi
  *
  *
  * Copyright (c) 2014 Markus Schöngart
  * Copyright (c) 2014 Markus Schöngart
+ * Copyright (c) 2019 The RmlUi Team, and contributors
  *
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * of this software and associated documentation files (the "Software"), to deal
@@ -517,6 +518,18 @@ Matrix4< Component, Storage > Matrix4< Component, Storage >::ProjectPerspective(
 	);
 	);
 }
 }
 
 
+// Create a perspective projection matrix
+template< typename Component, class Storage>
+Matrix4< Component, Storage > Matrix4< Component, Storage >::Perspective(Component d) noexcept
+{
+	return Matrix4< Component, Storage >::FromRows(
+		Matrix4< Component, Storage >::VectorType(1, 0, 0, 0),
+		Matrix4< Component, Storage >::VectorType(0, 1, 0, 0),
+		Matrix4< Component, Storage >::VectorType(0, 0, 1, 0),
+		Matrix4< Component, Storage >::VectorType(0, 0, -static_cast<Component>(1)/d, 1)
+	);
+}
+
 // Return a translation matrix.
 // Return a translation matrix.
 template< typename Component, class Storage>
 template< typename Component, class Storage>
 Matrix4< Component, Storage > Matrix4< Component, Storage >::Translate(const Vector3< Component >& v) noexcept
 Matrix4< Component, Storage > Matrix4< Component, Storage >::Translate(const Vector3< Component >& v) noexcept

+ 1 - 1
Include/RmlUi/Core/Platform.h

@@ -49,7 +49,7 @@
 	#define RMLUI_DEBUG
 	#define RMLUI_DEBUG
 #endif
 #endif
 
 
-#if defined __LP64__ || defined _M_X64 || defined __MINGW64__ || defined _LP64
+#if defined __LP64__ || defined _M_X64 || defined _WIN64 || defined __MINGW64__ || defined _LP64
     #define RMLUI_ARCH_64
     #define RMLUI_ARCH_64
 #else
 #else
     #define RMLUI_ARCH_32
     #define RMLUI_ARCH_32

+ 83 - 0
Include/RmlUi/Core/Profiling.h

@@ -0,0 +1,83 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREPROFILING_H
+#define RMLUICOREPROFILING_H
+
+
+#ifdef RMLUI_ENABLE_PROFILING
+
+#define TRACY_ENABLE
+#include "../../../Dependencies/tracy/Tracy.hpp"
+
+#define RMLUI_ZoneNamed(x,y)       ZoneNamed(x,y)
+#define RMLUI_ZoneNamedN(x,y,z)    ZoneNamedN(x,y,z)
+#define RMLUI_ZoneNamedC(x,y,z)    ZoneNamedC(x,y,z)
+#define RMLUI_ZoneNamedNC(x,y,z,w) ZoneNamedNC(x,y,z,w)
+
+#define RMLUI_ZoneScoped           ZoneScoped
+#define RMLUI_ZoneScopedN(x)       ZoneScopedN(x)
+#define RMLUI_ZoneScopedC(x)       ZoneScopedC(x)
+#define RMLUI_ZoneScopedNC(x,y)    ZoneScopedNC(x,y)
+
+#define RMLUI_ZoneText(x,y)        ZoneText(x,y)
+#define RMLUI_ZoneName(x,y)        ZoneName(x,y)
+
+#define RMLUI_TracyPlot(name,val)  TracyPlot(name,val)
+
+#define RMLUI_FrameMark            FrameMark
+#define RMLUI_FrameMarkNamed(x)    FrameMarkNamed(x)
+#define RMLUI_FrameMarkStart(x)    FrameMarkStart(x)
+#define RMLUI_FrameMarkEnd(x)      FrameMarkEnd(x)
+
+#else
+
+#define RMLUI_ZoneNamed(x,y)
+#define RMLUI_ZoneNamedN(x,y,z)
+#define RMLUI_ZoneNamedC(x,y,z)
+#define RMLUI_ZoneNamedNC(x,y,z,w)
+
+#define RMLUI_ZoneScoped
+#define RMLUI_ZoneScopedN(x)
+#define RMLUI_ZoneScopedC(x)
+#define RMLUI_ZoneScopedNC(x,y)
+
+#define RMLUI_ZoneText(x,y)
+#define RMLUI_ZoneName(x,y)
+
+#define RMLUI_TracyPlot(name,val)
+
+#define RMLUI_FrameMark
+#define RMLUI_FrameMarkNamed(x)
+#define RMLUI_FrameMarkStart(x)
+#define RMLUI_FrameMarkEnd(x)
+
+#endif
+
+
+#endif

+ 33 - 24
Source/Core/BitmapFont/FontFace.h → Include/RmlUi/Core/PropertiesIteratorView.h

@@ -15,7 +15,7 @@
  *
  *
  * The above copyright notice and this permission notice shall be included in
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  * all copies or substantial portions of the Software.
- *
+ * 
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -25,45 +25,54 @@
  * THE SOFTWARE.
  * THE SOFTWARE.
  *
  *
  */
  */
+#ifndef RMLUICOREPROPERTIESITERATORVIEW_H
+#define RMLUICOREPROPERTIESITERATORVIEW_H
 
 
-#ifndef RMLUICOREBITMAPFONTFACE_H
-#define RMLUICOREBITMAPFONTFACE_H
-
-#include "../../../Include/RmlUi/Core/FontFace.h"
-#include "BitmapFontDefinitions.h"
+#include "Types.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
-namespace BitmapFont {
 
 
-class FontFaceHandle;
+class PropertiesIterator;
 
 
 /**
 /**
-	@author Peter Curry
+	Provides an iterator for properties defined in the element's style or definition.
+	Construct it through the desired Element.
+	Warning: Modifying the underlying style invalidates the iterator.
+
+	Usage:
+		for(auto it = element.IterateLocalProperties(); !it.AtEnd(); ++it) { ... }
+
+	Note: Not an std-style iterator. Implemented as a wrapper over the internal
+	iterator to avoid exposing internal headers to the user.
+
+	@author Michael R. P. Ragazzon
  */
  */
 
 
-class FontFace : public Rml::Core::FontFace
-{
+class RMLUICORE_API PropertiesIteratorView {
 public:
 public:
-	FontFace(BitmapFontDefinitions *_face, Font::Style style, Font::Weight weight, bool release_stream);
-	~FontFace();
+	PropertiesIteratorView(UniquePtr<PropertiesIterator> ptr);
+	PropertiesIteratorView(PropertiesIteratorView&& other) noexcept;
+	PropertiesIteratorView& operator=(PropertiesIteratorView&& other) noexcept;
+	PropertiesIteratorView(const PropertiesIteratorView& other) = delete;
+	PropertiesIteratorView& operator=(const PropertiesIteratorView&) = delete;
+	~PropertiesIteratorView();
+
+	PropertiesIteratorView& operator++();
 
 
-	/// Returns a handle for positioning and rendering this face at the given size.
-	/// @param[in] charset The set of characters in the handle, as a comma-separated list of unicode ranges.
-	/// @param[in] size The size of the desired handle, in points.
-	/// @return The shared font handle.
-	Rml::Core::FontFaceHandle* GetHandle(const String& charset, int size);
+	// Returns true when all properties have been iterated over.
+	// @warning The getters and operator++ can only be called if the iterator is not at the end.
+	bool AtEnd() const;
 
 
-	/// Releases the face's FreeType face structure. This will mean handles for new sizes cannot be constructed,
-	/// but existing ones can still be fetched.
-	void ReleaseFace();
+	PropertyId GetId() const;
+	const String& GetName() const;
+	const Property& GetProperty() const;
 
 
 private:
 private:
-	BitmapFontDefinitions *face;
+	UniquePtr<PropertiesIterator> ptr;
 };
 };
 
 
-}
 }
 }
 }
 }
 
 
-#endif
+#endif

+ 20 - 12
Include/RmlUi/Core/Property.h

@@ -37,6 +37,14 @@ namespace Core {
 
 
 class PropertyDefinition;
 class PropertyDefinition;
 
 
+struct RMLUICORE_API PropertySource {
+	PropertySource(String path, int line_number, String rule_name) : path(path), line_number(line_number), rule_name(rule_name) {}
+	String path;
+	int line_number;
+	String rule_name;
+};
+
+
 /**
 /**
 	@author Peter Curry
 	@author Peter Curry
  */
  */
@@ -75,27 +83,28 @@ public:
 		PC = 1 << 16,				// number suffixed by 'pc'; fetch as < float >
 		PC = 1 << 16,				// number suffixed by 'pc'; fetch as < float >
 		PPI_UNIT = INCH | CM | MM | PT | PC,
 		PPI_UNIT = INCH | CM | MM | PT | PC,
 
 
-		TRANSFORM = 1 << 17,			// transform; fetch as < TransformRef >, may be empty
+		TRANSFORM = 1 << 17,			// transform; fetch as < TransformPtr >, may be empty
 		TRANSITION = 1 << 18,           // transition; fetch as < TransitionList >
 		TRANSITION = 1 << 18,           // transition; fetch as < TransitionList >
 		ANIMATION = 1 << 19,            // animation; fetch as < AnimationList >
 		ANIMATION = 1 << 19,            // animation; fetch as < AnimationList >
+		DECORATOR = 1 << 20,            // decorator; fetch as < DecoratorsPtr >
+		FONTEFFECT = 1 << 21,           // font-effect; fetch as < FontEffectsPtr >
 
 
 		LENGTH = PX | DP | PPI_UNIT | EM | REM,
 		LENGTH = PX | DP | PPI_UNIT | EM | REM,
 		LENGTH_PERCENT = LENGTH | PERCENT,
 		LENGTH_PERCENT = LENGTH | PERCENT,
 		NUMBER_LENGTH_PERCENT = NUMBER | LENGTH | PERCENT,
 		NUMBER_LENGTH_PERCENT = NUMBER | LENGTH | PERCENT,
-		ANGLE = NUMBER | DEG | RAD
+		ABSOLUTE_LENGTH = PX | DP | PPI_UNIT,
+		ANGLE = DEG | RAD
 	};
 	};
 
 
 	Property();
 	Property();
 	template < typename PropertyType >
 	template < typename PropertyType >
-	Property(PropertyType value, Unit unit, int specificity = -1) : value(Variant(value)), unit(unit), specificity(specificity)
+	Property(PropertyType value, Unit unit, int specificity = -1) : value(value), unit(unit), specificity(specificity)
 	{
 	{
-		definition = NULL;
+		definition = nullptr;
 		parser_index = -1;
 		parser_index = -1;
-
-		source_line_number = 0;
 	}
 	}
-
-	~Property();	
+	template<typename EnumType, typename = typename std::enable_if< std::is_enum<EnumType>::value, EnumType >::type>
+	Property(EnumType value) : value(static_cast<int>(value)), unit(KEYWORD), specificity(-1) {}
 
 
 	/// Get the value of the property as a string.
 	/// Get the value of the property as a string.
 	String ToString() const;
 	String ToString() const;
@@ -114,11 +123,10 @@ public:
 	Unit unit;
 	Unit unit;
 	int specificity;
 	int specificity;
 
 
-	const PropertyDefinition* definition;
-	int parser_index;
+	const PropertyDefinition* definition = nullptr;
+	int parser_index = -1;
 
 
-	String source;
-	int source_line_number;
+	SharedPtr<const PropertySource> source;
 };
 };
 
 
 }
 }

+ 8 - 3
Include/RmlUi/Core/PropertyDefinition.h

@@ -42,13 +42,13 @@ namespace Core {
 
 
 enum class RelativeTarget { None, ContainingBlockWidth, ContainingBlockHeight, FontSize, ParentFontSize, LineHeight };
 enum class RelativeTarget { None, ContainingBlockWidth, ContainingBlockHeight, FontSize, ParentFontSize, LineHeight };
 
 
-class RMLUICORE_API PropertyDefinition
+class RMLUICORE_API PropertyDefinition final
 {
 {
 public:
 public:
-	PropertyDefinition(const String& default_value, bool inherited, bool forces_layout);
+	PropertyDefinition(PropertyId id, const String& default_value, bool inherited, bool forces_layout);
 	PropertyDefinition(const PropertyDefinition &) = delete; 
 	PropertyDefinition(const PropertyDefinition &) = delete; 
 	PropertyDefinition& operator=(const PropertyDefinition &) = delete;
 	PropertyDefinition& operator=(const PropertyDefinition &) = delete;
-	virtual ~PropertyDefinition();
+	~PropertyDefinition();
 
 
 	/// Registers a parser to parse values for this definition.
 	/// Registers a parser to parse values for this definition.
 	/// @param[in] parser_name The name of the parser (default parsers are 'string', 'keyword', 'number' and 'colour').
 	/// @param[in] parser_name The name of the parser (default parsers are 'string', 'keyword', 'number' and 'colour').
@@ -82,7 +82,12 @@ public:
 	/// Returns the target for resolving values with percent and possibly number units.
 	/// Returns the target for resolving values with percent and possibly number units.
 	RelativeTarget GetRelativeTarget() const;
 	RelativeTarget GetRelativeTarget() const;
 
 
+	/// Return the property id
+	PropertyId GetId() const;
+
 private:
 private:
+	PropertyId id;
+
 	Property default_value;
 	Property default_value;
 	bool inherited;
 	bool inherited;
 	bool forces_layout;
 	bool forces_layout;

+ 11 - 15
Include/RmlUi/Core/PropertyDictionary.h

@@ -47,28 +47,21 @@ class RMLUICORE_API PropertyDictionary
 {
 {
 public:
 public:
 	PropertyDictionary();
 	PropertyDictionary();
-	~PropertyDictionary();
 
 
-	/// Sets a property on the dictionary. Any existing property with a similar name will be overwritten.
-	/// @param[in] name The name of the property to add.
-	/// @param[in] property The value of the new property.
-	void SetProperty(const String& name, const Property& property);
+	/// Sets a property on the dictionary. Any existing property with the same id will be overwritten.
+	void SetProperty(PropertyId id, const Property& property);
 	/// Removes a property from the dictionary, if it exists.
 	/// Removes a property from the dictionary, if it exists.
-	/// @param[in] name The name of the property to remove.
-	void RemoveProperty(const String& name);
-	/// Returns the value of the property with the requested name, if one exists.
-	/// @param[in] name The name of the desired property.
-	const Property* GetProperty(const String& name) const;
+	void RemoveProperty(PropertyId id);
+	/// Returns the value of the property with the requested id, if one exists.
+	const Property* GetProperty(PropertyId id) const;
 
 
 	/// Returns the number of properties in the dictionary.
 	/// Returns the number of properties in the dictionary.
-	/// @return The number of properties in the dictionary.
 	int GetNumProperties() const;
 	int GetNumProperties() const;
 	/// Returns the map of properties in the dictionary.
 	/// Returns the map of properties in the dictionary.
-	/// @return The property map.
 	const PropertyMap& GetProperties() const;
 	const PropertyMap& GetProperties() const;
 
 
 	/// Imports into the dictionary, and optionally defines the specificity of, potentially
 	/// Imports into the dictionary, and optionally defines the specificity of, potentially
-	/// un-specified properties. In the case of name conflicts, the incoming properties will
+	/// un-specified properties. In the case of id conflicts, the incoming properties will
 	/// overwrite the existing properties if their specificity (or their forced specificity)
 	/// overwrite the existing properties if their specificity (or their forced specificity)
 	/// are at least equal.
 	/// are at least equal.
 	/// @param[in] property_dictionary The properties to import.
 	/// @param[in] property_dictionary The properties to import.
@@ -76,17 +69,20 @@ public:
 	void Import(const PropertyDictionary& property_dictionary, int specificity = -1);
 	void Import(const PropertyDictionary& property_dictionary, int specificity = -1);
 
 
 	/// Merges the contents of another fully-specified property dictionary with this one.
 	/// Merges the contents of another fully-specified property dictionary with this one.
-	/// Properties defined in the new dictionary will overwrite those with the same name as
+	/// Properties defined in the new dictionary will overwrite those with the same id as
 	/// appropriate.
 	/// appropriate.
 	/// @param[in] property_dictionary The dictionary to merge.
 	/// @param[in] property_dictionary The dictionary to merge.
 	/// @param[in] specificity_offset The specificities of all incoming properties will be offset by this value.
 	/// @param[in] specificity_offset The specificities of all incoming properties will be offset by this value.
 	void Merge(const PropertyDictionary& property_dictionary, int specificity_offset = 0);
 	void Merge(const PropertyDictionary& property_dictionary, int specificity_offset = 0);
 
 
+	/// Set the source of all properties in the dictionary to the given one.
+	void SetSourceOfAllProperties(const SharedPtr<const PropertySource>& property_source);
+
 private:
 private:
 	// Sets a property on the dictionary and its specificity if there is no name conflict, or its
 	// Sets a property on the dictionary and its specificity if there is no name conflict, or its
 	// specificity (given by the parameter, not read from the property itself) is at least equal to
 	// specificity (given by the parameter, not read from the property itself) is at least equal to
 	// the specificity of the conflicting property.
 	// the specificity of the conflicting property.
-	void SetProperty(const String& name, const Rml::Core::Property& property, int specificity);
+	void SetProperty(PropertyId id, const Rml::Core::Property& property, int specificity);
 
 
 	PropertyMap properties;
 	PropertyMap properties;
 };
 };

+ 235 - 0
Include/RmlUi/Core/PropertyIdSet.h

@@ -0,0 +1,235 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+#ifndef RMLUICOREPROPERTYIDSET_H
+#define RMLUICOREPROPERTYIDSET_H
+
+#include "Types.h"
+#include "ID.h"
+#include <bitset>
+
+namespace Rml {
+namespace Core {
+
+class PropertyIdSetIterator;
+
+
+/*
+	PropertyIdSet is a 'set'-like container for PropertyIds. 
+	
+	It is quite cheap to construct and use, requiring no dynamic allocation for the library-defined IDs as they are based around a bitset.
+	Custom IDs on the other hand need to use a more trafitional set, and are thus more expensive to insert. 
+
+	Supports union and intersection operations between two sets, as well as iteration through the IDs that are inserted.
+*/
+
+
+class RMLUICORE_API PropertyIdSet {
+private:
+	static constexpr size_t N = (size_t)PropertyId::NumDefinedIds;
+	std::bitset<N> defined_ids;
+	SmallOrderedSet<PropertyId> custom_ids;
+
+public:
+	PropertyIdSet() {
+		static_assert((size_t)PropertyId::Invalid == 0, "PropertyIdSet makes an assumption that PropertyId::Invalid is zero.");
+	}
+
+	void Insert(PropertyId id) {
+		if ((size_t)id < N)
+			defined_ids.set((size_t)id);
+		else
+			custom_ids.insert(id);
+	}
+
+	void Clear() {
+		defined_ids.reset();
+		custom_ids.clear();
+	}
+	void Erase(PropertyId id) {
+		if ((size_t)id < N)
+			defined_ids.reset((size_t)id);
+		else
+			custom_ids.erase(id);
+	}
+
+	bool Empty() const {
+		return defined_ids.none() && custom_ids.empty();
+	}
+	bool Contains(PropertyId id) const {
+		if ((size_t)id < N)
+			return defined_ids.test((size_t)id);
+		else
+			return custom_ids.count(id) == 1;
+	}
+
+	size_t Size() const {
+		return defined_ids.count() + custom_ids.size();
+	}
+
+	// Union with another set
+	PropertyIdSet& operator|=(const PropertyIdSet& other)
+	{
+		defined_ids |= other.defined_ids;
+		custom_ids.insert(other.custom_ids.begin(), other.custom_ids.end());
+		return *this;
+	}
+
+	PropertyIdSet operator|(const PropertyIdSet& other) const
+	{
+		PropertyIdSet result = *this;
+		result |= other;
+		return result;
+	}
+
+	// Intersection with another set
+	PropertyIdSet& operator&=(const PropertyIdSet& other)
+	{
+		defined_ids &= other.defined_ids;
+		if (custom_ids.size() > 0 && other.custom_ids.size() > 0)
+		{
+			for (auto it = custom_ids.begin(); it != custom_ids.end();)
+				if (other.custom_ids.count(*it) == 0)
+					it = custom_ids.erase(it);
+				else
+					++it;
+		}
+		else
+		{
+			custom_ids.clear();
+		}
+		return *this;
+	}
+	
+	PropertyIdSet operator&(const PropertyIdSet& other) const
+	{
+		PropertyIdSet result;
+		result.defined_ids = (defined_ids & other.defined_ids);
+		if (custom_ids.size() > 0 && other.custom_ids.size() > 0)
+		{
+			for (PropertyId id : custom_ids)
+				if (other.custom_ids.count(id) == 1)
+					result.custom_ids.insert(id);
+		}
+		return result;
+	}
+
+	// Iterator support. Iterates through all the PropertyIds that are set (contained).
+	// @note: Modifying the container invalidates the iterators. Only const_iterators are provided.
+	inline PropertyIdSetIterator begin() const;
+	inline PropertyIdSetIterator end() const;
+
+	// Erases the property id represented by a valid iterator. Invalidates any previous iterators.
+	// @return A new valid iterator pointing to the next element or end().
+	inline PropertyIdSetIterator Erase(const PropertyIdSetIterator& it);
+};
+
+
+
+class RMLUICORE_API PropertyIdSetIterator
+{
+public:
+	using CustomIdsIt = SmallOrderedSet<PropertyId>::const_iterator;
+
+	PropertyIdSetIterator() : container(nullptr), defined_ids_index(0), custom_ids_iterator() {}
+	PropertyIdSetIterator(const PropertyIdSet* container, size_t defined_ids_index, CustomIdsIt custom_ids_iterator)
+		: container(container), defined_ids_index(defined_ids_index), custom_ids_iterator(custom_ids_iterator) 
+	{
+		ProceedToNextValid();
+	}
+	
+	PropertyIdSetIterator& operator++() {
+		if (defined_ids_index < N)
+			++defined_ids_index;
+		else
+			++custom_ids_iterator;
+		ProceedToNextValid();
+		return *this;
+	}
+
+	bool operator==(const PropertyIdSetIterator& other) const {
+		return container == other.container && defined_ids_index == other.defined_ids_index && custom_ids_iterator == other.custom_ids_iterator;
+	}
+	bool operator!=(const PropertyIdSetIterator& other) const { 
+		return !(*this == other); 
+	}
+
+	PropertyId operator*() const { 
+		if (defined_ids_index < N)
+			return static_cast<PropertyId>(defined_ids_index);
+		else
+			return *custom_ids_iterator;
+	}
+
+private:
+
+	inline void ProceedToNextValid()
+	{
+		for (; defined_ids_index < N; ++defined_ids_index)
+		{
+			if (container->Contains( static_cast<PropertyId>(defined_ids_index) ))
+				return;
+		}
+	}
+
+	static constexpr size_t N = (size_t)PropertyId::NumDefinedIds;
+	const PropertyIdSet* container;
+	size_t defined_ids_index;
+	CustomIdsIt custom_ids_iterator;
+
+	friend PropertyIdSetIterator PropertyIdSet::Erase(const PropertyIdSetIterator&);
+};
+
+
+
+PropertyIdSetIterator PropertyIdSet::begin() const {
+	return PropertyIdSetIterator(this, 1, custom_ids.begin());
+}
+
+PropertyIdSetIterator PropertyIdSet::end() const {
+	return PropertyIdSetIterator(this, N, custom_ids.end());
+}
+
+PropertyIdSetIterator PropertyIdSet::Erase(const PropertyIdSetIterator& it_in) {
+	RMLUI_ASSERT(it_in.container == this);
+	PropertyIdSetIterator it = it_in;
+	if (it.defined_ids_index < N)
+	{
+		defined_ids.reset(it.defined_ids_index);
+		++it;
+	}
+	else
+	{
+		it.custom_ids_iterator = custom_ids.erase(it.custom_ids_iterator);
+	}
+	return it;
+}
+
+}
+}
+
+#endif

+ 2 - 7
Include/RmlUi/Core/PropertyParser.h

@@ -36,7 +36,7 @@
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
-typedef std::map < String, int, StringUtilities::StringComparei > ParameterMap;
+typedef UnorderedMap< String, int > ParameterMap;
 
 
 /**
 /**
 	A property parser takes a property declaration in string form, validates it, and converts it to a Property.
 	A property parser takes a property declaration in string form, validates it, and converts it to a Property.
@@ -47,9 +47,7 @@ typedef std::map < String, int, StringUtilities::StringComparei > ParameterMap;
 class RMLUICORE_API PropertyParser
 class RMLUICORE_API PropertyParser
 {
 {
 public:
 public:
-	virtual ~PropertyParser()
-	{
-	}
+	virtual ~PropertyParser() {}
 
 
 	/// Called to parse a RCSS declaration.
 	/// Called to parse a RCSS declaration.
 	/// @param[out] property The property to set the parsed value on.
 	/// @param[out] property The property to set the parsed value on.
@@ -57,9 +55,6 @@ public:
 	/// @param[in] parameters The list of parameters defined for this property.
 	/// @param[in] parameters The list of parameters defined for this property.
 	/// @return True if the value was parsed successfully, false otherwise.
 	/// @return True if the value was parsed successfully, false otherwise.
 	virtual bool ParseValue(Property& property, const String& value, const ParameterMap& parameters) const = 0;
 	virtual bool ParseValue(Property& property, const String& value, const ParameterMap& parameters) const = 0;
-
-	/// Called when the parser is released. This should free all dynamic memory used by the parser.
-	virtual void Release() = 0;
 };
 };
 
 
 }
 }

+ 63 - 43
Include/RmlUi/Core/PropertySpecification.h

@@ -32,12 +32,32 @@
 #include "Header.h"
 #include "Header.h"
 #include "Element.h"
 #include "Element.h"
 #include "PropertyDefinition.h"
 #include "PropertyDefinition.h"
+#include "PropertyIdSet.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
+class StyleSheetSpecification;
 class PropertyDictionary;
 class PropertyDictionary;
-struct PropertyShorthandDefinition;
+class PropertyIdNameMap;
+class ShorthandIdNameMap;
+struct ShorthandDefinition;
+
+enum class ShorthandType
+{
+	// Normal; properties that fail to parse fall-through to the next until they parse correctly, and any
+	// undeclared are not set.
+	FallThrough,
+	// A single failed parse will abort, and any undeclared are replicated from the last declared property.
+	Replicate,
+	// For 'padding', 'margin', etc; up to four properties are expected.
+	Box,
+	// Repeatedly resolves the full value string on each property, whether it is a normal property or another shorthand.
+	RecursiveRepeat,
+	// Comma-separated list of properties or shorthands, the number of declared values must match the specified.
+	RecursiveCommaSeparated
+};
+
 
 
 /**
 /**
 	A property specification stores a group of property definitions.
 	A property specification stores a group of property definitions.
@@ -48,22 +68,7 @@ struct PropertyShorthandDefinition;
 class RMLUICORE_API PropertySpecification
 class RMLUICORE_API PropertySpecification
 {
 {
 public:
 public:
-	enum ShorthandType
-	{
-		// Normal; properties that fail to parse fall-through to the next until they parse correctly, and any
-		// undeclared are not set.
-		FALL_THROUGH,
-		// A single failed parse will abort, and any undeclared are replicated from the last declared property.
-		REPLICATE,
-		// For 'padding', 'margin', etc; up four properties are expected.
-		BOX,
-		// Recursively resolves the full value string on each property, whether it is a normal property or another shorthand.
-		RECURSIVE,
-		// BOX if four properties are shorthanded and they end in '-top', '-right', etc, otherwise FALL_THROUGH.
-		AUTO
-	};
-
-	PropertySpecification();
+	PropertySpecification(size_t reserve_num_properties, size_t reserve_num_shorthands);
 	~PropertySpecification();
 	~PropertySpecification();
 
 
 	/// Registers a property with a new definition.
 	/// Registers a property with a new definition.
@@ -71,52 +76,67 @@ public:
 	/// @param[in] default_value The default value to be used for an element if it has no other definition provided.
 	/// @param[in] default_value The default value to be used for an element if it has no other definition provided.
 	/// @param[in] inherited True if this property is inherited from parent to child, false otherwise.
 	/// @param[in] inherited True if this property is inherited from parent to child, false otherwise.
 	/// @param[in] forces_layout True if this property requires its parent to be reformatted if changed.
 	/// @param[in] forces_layout True if this property requires its parent to be reformatted if changed.
+	/// @param[in] id If 'Invalid' then automatically assigns a new id, otherwise assigns the given id.
 	/// @return The new property definition, ready to have parsers attached.
 	/// @return The new property definition, ready to have parsers attached.
-	PropertyDefinition& RegisterProperty(const String& property_name, const String& default_value, bool inherited, bool forces_layout);
+	PropertyDefinition& RegisterProperty(const String& property_name, const String& default_value, bool inherited, bool forces_layout, PropertyId id = PropertyId::Invalid);
 	/// Returns a property definition.
 	/// Returns a property definition.
-	/// @param[in] property_name The name of the desired property.
-	/// @return The appropriate property definition if it could be found, NULL otherwise.
+	/// @param[in] id The id of the desired property.
+	/// @return The appropriate property definition if it could be found, nullptr otherwise.
+	const PropertyDefinition* GetProperty(PropertyId id) const;
 	const PropertyDefinition* GetProperty(const String& property_name) const;
 	const PropertyDefinition* GetProperty(const String& property_name) const;
 
 
-	/// Returns the list of the names of all registered property definitions.
-	/// @return The list with stored property names.
-	const PropertyNameList& GetRegisteredProperties() const;
-
-	/// Returns the list of the names of all registered inherited property definitions.
-	/// @return The list with stored property names.
-	const PropertyNameList& GetRegisteredInheritedProperties() const;
+	/// Returns the id set of all registered property definitions.
+	const PropertyIdSet& GetRegisteredProperties() const;
+	/// Returns the id set of all registered inherited property definitions.
+	const PropertyIdSet& GetRegisteredInheritedProperties() const;
+	/// Returns the id set of all registered property definitions that may dirty the layout.
+	const PropertyIdSet& GetRegisteredPropertiesForcingLayout() const;
 
 
 	/// Registers a shorthand property definition.
 	/// Registers a shorthand property definition.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
 	/// @param[in] shorthand_name The name to register the new shorthand property under.
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] properties A comma-separated list of the properties this definition is shorthand for. The order in which they are specified here is the order in which the values will be processed.
 	/// @param[in] type The type of shorthand to declare.
 	/// @param[in] type The type of shorthand to declare.
+	/// @param[in] id If 'Invalid' then automatically assigns a new id, otherwise assigns the given id.
 	/// @param True if all the property names exist, false otherwise.
 	/// @param True if all the property names exist, false otherwise.
-	bool RegisterShorthand(const String& shorthand_name, const String& property_names, ShorthandType type = AUTO);
+	ShorthandId RegisterShorthand(const String& shorthand_name, const String& property_names, ShorthandType type, ShorthandId id = ShorthandId::Invalid);
 	/// Returns a shorthand definition.
 	/// Returns a shorthand definition.
 	/// @param[in] shorthand_name The name of the desired shorthand.
 	/// @param[in] shorthand_name The name of the desired shorthand.
-	/// @return The appropriate shorthand definition if it could be found, NULL otherwise.
-	const PropertyShorthandDefinition* GetShorthand(const String& shorthand_name) const;
-
-	/// Parses a property declaration, setting any parsed and validated properties on the given dictionary.
-	/// @param dictionary The property dictionary which will hold all declared properties.
-	/// @param property_name The name of the declared property.
-	/// @param property_value The values the property is being set to.
+	/// @return The appropriate shorthand definition if it could be found, nullptr otherwise.
+	const ShorthandDefinition* GetShorthand(ShorthandId id) const;
+	const ShorthandDefinition* GetShorthand(const String& shorthand_name) const;
+
+	/// Parse declaration by name, whether its a property or shorthand.
+	bool ParsePropertyDeclaration(PropertyDictionary& dictionary, const String& property_name, const String& property_value) const;
+	/// Parse property declaration by ID
+	bool ParsePropertyDeclaration(PropertyDictionary& dictionary, PropertyId property_id, const String& property_value) const;
+	/// Parses a shorthand declaration, setting any parsed and validated properties on the given dictionary.
 	/// @return True if all properties were parsed successfully, false otherwise.
 	/// @return True if all properties were parsed successfully, false otherwise.
-	bool ParsePropertyDeclaration(PropertyDictionary& dictionary, const String& property_name, const String& property_value, const String& source_file = "", int source_line_number = 0) const;
+	bool ParseShorthandDeclaration(PropertyDictionary& dictionary, ShorthandId shorthand_id, const String& property_value) const;
+
 	/// Sets all undefined properties in the dictionary to their defaults.
 	/// Sets all undefined properties in the dictionary to their defaults.
-	/// @param dictionary[in] The dictionary to set the default values on.
+	/// @param dictionary[in-out] The dictionary to set the default values on.
 	void SetPropertyDefaults(PropertyDictionary& dictionary) const;
 	void SetPropertyDefaults(PropertyDictionary& dictionary) const;
 
 
+	/// Returns the properties of dictionary converted to a string.
+	String PropertiesToString(const PropertyDictionary& dictionary) const;
+
 private:
 private:
-	typedef std::unordered_map< String, PropertyDefinition* > PropertyMap;
-	typedef std::unordered_map< String, PropertyShorthandDefinition* > ShorthandMap;
+	using Properties = std::vector< UniquePtr<PropertyDefinition> >;
+	using Shorthands = std::vector< UniquePtr<ShorthandDefinition> >;
+
+	Properties properties;
+	Shorthands shorthands;
 
 
-	PropertyMap properties;
-	ShorthandMap shorthands;
-	PropertyNameList property_names;
-	PropertyNameList inherited_property_names;
+	UniquePtr<PropertyIdNameMap> property_map;
+	UniquePtr<ShorthandIdNameMap> shorthand_map;
+
+	PropertyIdSet property_names;
+	PropertyIdSet inherited_property_names;
+	PropertyIdSet properties_forcing_layout;
 
 
 	bool ParsePropertyValues(StringList& values_list, const String& values, bool split_values) const;
 	bool ParsePropertyValues(StringList& values_list, const String& values, bool split_values) const;
+
+	friend class StyleSheetSpecification;
 };
 };
 
 
 }
 }

+ 0 - 78
Include/RmlUi/Core/ReferenceCountable.h

@@ -1,78 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICOREREFERENCECOUNTABLE_H
-#define RMLUICOREREFERENCECOUNTABLE_H
-
-#include "Header.h"
-
-namespace Rml {
-namespace Core {
-
-/**
-	A base class for any class that wishes to be reference counted.
-	@author Robert Curry
-*/
-
-class RMLUICORE_API ReferenceCountable
-{
-public:
-	/// Constructor.
-	/// @param[in] initial_count The initial reference count of the object.
-	ReferenceCountable(int initial_count = 1);
-	/// Destructor. The reference count must be 0 when this is invoked.
-	virtual ~ReferenceCountable();
-
-	/// Returns the number of references outstanding against this object.
-	virtual int GetReferenceCount();
-	/// Increases the reference count. If this pushes the count above 0, OnReferenceActivate() will be called. 
-	virtual void AddReference();
-	/// Decreases the reference count. If this pushes the count to 0, OnReferenceDeactivate() will be called. 
-	virtual void RemoveReference();
-
-	/// Catches incorrect copy attempts.
-	ReferenceCountable& operator=(const ReferenceCountable& copy);
-
-	/// If any reference countable objects are still allocated, this function will write a leak report to the log.
-	static void DumpLeakReport();
-
-protected:		
-	/// A hook method called when the reference count climbs from 0.
-	virtual void OnReferenceActivate();
-	/// A hook method called when the reference count drops to 0.
-	virtual void OnReferenceDeactivate();
-
-private:
-	// The number of references against this object.
-	int reference_count;
-};
-
-}
-}
-
-#endif

+ 9 - 29
Include/RmlUi/Core/RenderInterface.h

@@ -29,7 +29,7 @@
 #ifndef RMLUICORERENDERINTERFACE_H
 #ifndef RMLUICORERENDERINTERFACE_H
 #define RMLUICORERENDERINTERFACE_H
 #define RMLUICORERENDERINTERFACE_H
 
 
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "Header.h"
 #include "Header.h"
 #include "Texture.h"
 #include "Texture.h"
 #include "Vertex.h"
 #include "Vertex.h"
@@ -47,7 +47,7 @@ class Context;
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API RenderInterface : public ReferenceCountable
+class RMLUICORE_API RenderInterface : public NonCopyMoveable
 {
 {
 public:
 public:
 	RenderInterface();
 	RenderInterface();
@@ -59,18 +59,18 @@ public:
 	/// @param[in] num_vertices The number of vertices passed to the function.
 	/// @param[in] num_vertices The number of vertices passed to the function.
 	/// @param[in] indices The geometry's index data.
 	/// @param[in] indices The geometry's index data.
 	/// @param[in] num_indices The number of indices passed to the function. This will always be a multiple of three.
 	/// @param[in] num_indices The number of indices passed to the function. This will always be a multiple of three.
-	/// @param[in] texture The texture to be applied to the geometry. This may be NULL, in which case the geometry is untextured.
+	/// @param[in] texture The texture to be applied to the geometry. This may be nullptr, in which case the geometry is untextured.
 	/// @param[in] translation The translation to apply to the geometry.
 	/// @param[in] translation The translation to apply to the geometry.
 	virtual void RenderGeometry(Vertex* vertices, int num_vertices, int* indices, int num_indices, TextureHandle texture, const Vector2f& translation) = 0;
 	virtual void RenderGeometry(Vertex* vertices, int num_vertices, int* indices, int num_indices, TextureHandle texture, const Vector2f& translation) = 0;
 
 
 	/// Called by RmlUi when it wants to compile geometry it believes will be static for the forseeable future.
 	/// Called by RmlUi when it wants to compile geometry it believes will be static for the forseeable future.
 	/// If supported, this should be return a pointer to an optimised, application-specific version of the data. If
 	/// If supported, this should be return a pointer to an optimised, application-specific version of the data. If
-	/// not, do not override the function or return NULL; the simpler RenderGeometry() will be called instead.
+	/// not, do not override the function or return nullptr; the simpler RenderGeometry() will be called instead.
 	/// @param[in] vertices The geometry's vertex data.
 	/// @param[in] vertices The geometry's vertex data.
 	/// @param[in] num_vertices The number of vertices passed to the function.
 	/// @param[in] num_vertices The number of vertices passed to the function.
 	/// @param[in] indices The geometry's index data.
 	/// @param[in] indices The geometry's index data.
 	/// @param[in] num_indices The number of indices passed to the function. This will always be a multiple of three.
 	/// @param[in] num_indices The number of indices passed to the function. This will always be a multiple of three.
-	/// @param[in] texture The texture to be applied to the geometry. This may be NULL, in which case the geometry is untextured.
+	/// @param[in] texture The texture to be applied to the geometry. This may be nullptr, in which case the geometry is untextured.
 	/// @return The application-specific compiled geometry. Compiled geometry will be stored and rendered using RenderCompiledGeometry() in future calls, and released with ReleaseCompiledGeometry() when it is no longer needed.
 	/// @return The application-specific compiled geometry. Compiled geometry will be stored and rendered using RenderCompiledGeometry() in future calls, and released with ReleaseCompiledGeometry() when it is no longer needed.
 	virtual CompiledGeometryHandle CompileGeometry(Vertex* vertices, int num_vertices, int* indices, int num_indices, TextureHandle texture);
 	virtual CompiledGeometryHandle CompileGeometry(Vertex* vertices, int num_vertices, int* indices, int num_indices, TextureHandle texture);
 	/// Called by RmlUi when it wants to render application-compiled geometry.
 	/// Called by RmlUi when it wants to render application-compiled geometry.
@@ -107,35 +107,15 @@ public:
 	/// @param texture The texture handle to release.
 	/// @param texture The texture handle to release.
 	virtual void ReleaseTexture(TextureHandle texture);
 	virtual void ReleaseTexture(TextureHandle texture);
 
 
-	/// Returns the native horizontal texel offset for the renderer.
-	/// @return The renderer's horizontal texel offset. The default implementation returns 0.
-	virtual float GetHorizontalTexelOffset();
-	/// Returns the native vertical texel offset for the renderer.
-	/// @return The renderer's vertical texel offset. The default implementation returns 0.
-	virtual float GetVerticalTexelOffset();
-
-	/// Returns the number of pixels per inch.
-	/// @returns The number of pixels per inch. The default implementation returns 100.
-	virtual float GetPixelsPerInch();
-
-	/// Called by RmlUi when it wants to set the current transform matrix to a new matrix.
-	/// @param[in] transform The new transform to apply.
-	virtual void PushTransform(const Matrix4f& transform);
-	/// Called by RmlUi when it wants to revert the latest transform change.
-	/// @param[in] transform This is the transform to unapply.
-	///            It always equals the argument of the latest call to PushTransform().
-	virtual void PopTransform(const Matrix4f& transform);
-
-	/// Called when this render interface is released.
-	virtual void Release();
+	/// Called by RmlUi when it wants the renderer to use a new transform matrix.
+	/// If no transform applies to the current element, nullptr is submitted. Then it expects the renderer to use an identity matrix or otherwise omit the multiplication with the transform.
+	/// @param[in] transform The new transform to apply, or nullptr if no transform applies to the current element.
+	virtual void SetTransform(const Matrix4f* transform);
 
 
 	/// Get the context currently being rendered. This is only valid during RenderGeometry,
 	/// Get the context currently being rendered. This is only valid during RenderGeometry,
 	/// CompileGeometry, RenderCompiledGeometry, EnableScissorRegion and SetScissorRegion.
 	/// CompileGeometry, RenderCompiledGeometry, EnableScissorRegion and SetScissorRegion.
 	Context* GetContext() const;
 	Context* GetContext() const;
 
 
-protected:
-	virtual void OnReferenceDeactivate();
-
 private:
 private:
 	Context* context;
 	Context* context;
 
 

+ 9 - 12
Include/RmlUi/Core/ScriptInterface.h

@@ -30,7 +30,7 @@
 #define RMLUICORESCRIPTINTERFACE_H
 #define RMLUICORESCRIPTINTERFACE_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "Types.h"
 #include "Types.h"
 
 
 namespace Rml {
 namespace Rml {
@@ -41,17 +41,14 @@ namespace Core {
 	@author Peter Curry
 	@author Peter Curry
  */
  */
 
 
-class RMLUICORE_API ScriptInterface : public ReferenceCountable
-{
-	public:
-		virtual ~ScriptInterface()
-		{
-		}
-
-		virtual ScriptObject GetScriptObject() const
-		{
-			return NULL;
-		}
+class RMLUICORE_API ScriptInterface : public Releasable {
+public:
+	virtual ~ScriptInterface() { }
+
+	virtual ScriptObject GetScriptObject() const
+	{
+		return nullptr;
+	}
 };
 };
 
 
 }
 }

+ 101 - 0
Include/RmlUi/Core/Spritesheet.h

@@ -0,0 +1,101 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUISPRITESHEET_H
+#define RMLUISPRITESHEET_H
+
+#include "Types.h"
+#include "Texture.h"
+
+namespace Rml {
+namespace Core {
+
+struct Spritesheet;
+
+
+struct Rectangle {
+	Rectangle(float x = 0, float y = 0, float width = 0, float height = 0) : x(x), y(y), width(width), height(height) {}
+	float x, y, width, height;
+};
+
+struct Sprite {
+	Rectangle rectangle; // in 'px' units
+	const Spritesheet* sprite_sheet;
+};
+using SpriteMap = UnorderedMap<String, Sprite>; // key: sprite name (as given in @spritesheet)
+
+
+/**
+	Spritesheet holds a list of sprite names given in the @spritesheet at-rule in RCSS.
+ */
+struct Spritesheet {
+	String name;
+	String image_source;
+	String definition_source;
+	int definition_line_number;
+	Texture texture;
+	StringList sprite_names;
+
+	Spritesheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const Texture& texture)
+		: name(name), image_source(image_source), definition_source(definition_source), definition_line_number(definition_line_number), texture(texture) {}
+};
+
+using SpritesheetMap = SmallUnorderedMap<String, SharedPtr<const Spritesheet>>; // key: spritesheet name (as given in @spritesheet)
+using SpriteDefinitionList = std::vector<std::pair<String, Rectangle>>; // Sprite name and rectangle
+
+
+/**
+	SpritesheetList holds all the spritesheets and sprites given in a style sheet.
+ */
+class SpritesheetList {
+public:
+	/// Adds a new sprite sheet to the list and inserts all sprites with unique names into the global list.
+	bool AddSpriteSheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, const SpriteDefinitionList& sprite_definitions);
+
+	/// Get a sprite from its name if it exists.
+	/// Note: The pointer is invalidated whenever another sprite is added. Do not store it around.
+	const Sprite* GetSprite(const String& name) const;
+
+	/// Merge 'other' into this.
+	void Merge(const SpritesheetList& other);
+
+	void Reserve(size_t size_sprite_sheets, size_t size_sprites);
+	size_t NumSpriteSheets() const;
+	size_t NumSprites() const;
+
+	String ToString() const;
+
+private:
+	SpritesheetMap spritesheet_map;
+	SpriteMap sprite_map;
+};
+
+
+}
+}
+
+#endif

+ 84 - 88
Include/RmlUi/Core/Stream.h

@@ -30,7 +30,7 @@
 #define RMLUICORESTREAM_H
 #define RMLUICORESTREAM_H
 
 
 #include "Header.h"
 #include "Header.h"
-#include "ReferenceCountable.h"
+#include "Traits.h"
 #include "Types.h"
 #include "Types.h"
 #include "URL.h"
 #include "URL.h"
 #include <list>
 #include <list>
@@ -45,94 +45,90 @@ class StreamListener;
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICORE_API Stream : public ReferenceCountable
+class RMLUICORE_API Stream : public NonCopyMoveable
 {
 {
-	public:
-		// Stream modes.
-		enum StreamMode
-		{
-			MODE_WRITE = 1 << 0,
-			MODE_APPEND = 1 << 1,
-			MODE_READ = 1 << 2,
-			MODE_ASYNC = 1 << 3,
-
-			MODE_MASK = MODE_WRITE | MODE_APPEND | MODE_READ
-		};
-
-		Stream();
-		virtual ~Stream();
-
-		/// Closes the stream.
-		virtual void Close();
-
-		/// Returns the mode the stream was opened in.
-		int GetStreamMode() const;
-
-		/// Obtain the source url of this stream (if available)
-		const URL& GetSourceURL() const;
-
-		/// Are we at the end of the stream
-		virtual bool IsEOS() const;
-
-		/// Returns the size of this stream (in bytes).
-		virtual size_t Length() const = 0;
-
-		/// Returns the position of the stream pointer (in bytes).
-		virtual size_t Tell() const = 0;
-		/// Sets the stream position (in bytes).
-		virtual bool Seek(long offset, int origin) const = 0;
-
-		/// Read from the stream.
-		virtual size_t Read(void* buffer, size_t bytes) const = 0;
-		/// Read from the stream into another stream.
-		virtual size_t Read(Stream* stream, size_t bytes) const;
-		/// Read from the stream and append to the string buffer
-		virtual size_t Read(String& buffer, size_t bytes) const;
-		/// Read from the stream, without increasing the stream offset.
-		virtual size_t Peek(void* buffer, size_t bytes) const;
-
-		/// Write to the stream at the current position.
-		virtual size_t Write(const void* buffer, size_t bytes) = 0;
-		/// Write to this stream from another stream.
-		virtual size_t Write(const Stream* stream, size_t bytes);
-		/// Write a character array to the stream.
-		virtual size_t Write(const char* string);
-		/// Write a string to the stream
-		virtual size_t Write(const String& string);
-
-		/// Truncate the stream to the specified length.
-		virtual size_t Truncate(size_t bytes) = 0;
-
-		/// Push onto the front of the stream.
-		virtual size_t PushFront(const void* buffer, size_t bytes);
-		/// Push onto the back of the stream.
-		virtual size_t PushBack(const void* buffer, size_t bytes);
-
-		/// Pop from the front of the stream.
-		virtual size_t PopFront(size_t bytes);
-		/// Pop from the back of the stream.
-		virtual size_t PopBack(size_t bytes);
-
-		/// Returns true if the stream is ready for reading, false otherwise.
-		/// This is usually only implemented on streams supporting asynchronous
-		/// operations.
-		virtual bool IsReadReady() = 0;
-		/// Returns true if the stream is ready for writing, false otherwise.
-		/// This is usually only implemented on streams supporting asynchronous
-		/// operations.
-		virtual bool IsWriteReady() = 0;
-
-	protected:
-
-		/// Sets the mode on the stream; should be called by a stream when it is opened.
-		void SetStreamDetails(const URL& url, int stream_mode);		
-
-		/// Deletes the stream.
-		virtual void OnReferenceDeactivate();
-
-	private:
-		URL url;
-		int stream_mode;
+public:
+	// Stream modes.
+	enum StreamMode
+	{
+		MODE_WRITE = 1 << 0,
+		MODE_APPEND = 1 << 1,
+		MODE_READ = 1 << 2,
+		MODE_ASYNC = 1 << 3,
+
+		MODE_MASK = MODE_WRITE | MODE_APPEND | MODE_READ
+	};
+
+	Stream();
+	virtual ~Stream();
+
+	/// Closes the stream.
+	virtual void Close();
+
+	/// Returns the mode the stream was opened in.
+	int GetStreamMode() const;
+
+	/// Obtain the source url of this stream (if available)
+	const URL& GetSourceURL() const;
+
+	/// Are we at the end of the stream
+	virtual bool IsEOS() const;
+
+	/// Returns the size of this stream (in bytes).
+	virtual size_t Length() const = 0;
+
+	/// Returns the position of the stream pointer (in bytes).
+	virtual size_t Tell() const = 0;
+	/// Sets the stream position (in bytes).
+	virtual bool Seek(long offset, int origin) const = 0;
+
+	/// Read from the stream.
+	virtual size_t Read(void* buffer, size_t bytes) const = 0;
+	/// Read from the stream into another stream.
+	virtual size_t Read(Stream* stream, size_t bytes) const;
+	/// Read from the stream and append to the string buffer
+	virtual size_t Read(String& buffer, size_t bytes) const;
+	/// Read from the stream, without increasing the stream offset.
+	virtual size_t Peek(void* buffer, size_t bytes) const;
+
+	/// Write to the stream at the current position.
+	virtual size_t Write(const void* buffer, size_t bytes) = 0;
+	/// Write to this stream from another stream.
+	virtual size_t Write(const Stream* stream, size_t bytes);
+	/// Write a character array to the stream.
+	virtual size_t Write(const char* string);
+	/// Write a string to the stream
+	virtual size_t Write(const String& string);
+
+	/// Truncate the stream to the specified length.
+	virtual size_t Truncate(size_t bytes) = 0;
+
+	/// Push onto the front of the stream.
+	virtual size_t PushFront(const void* buffer, size_t bytes);
+	/// Push onto the back of the stream.
+	virtual size_t PushBack(const void* buffer, size_t bytes);
+
+	/// Pop from the front of the stream.
+	virtual size_t PopFront(size_t bytes);
+	/// Pop from the back of the stream.
+	virtual size_t PopBack(size_t bytes);
+
+	/// Returns true if the stream is ready for reading, false otherwise.
+	/// This is usually only implemented on streams supporting asynchronous
+	/// operations.
+	virtual bool IsReadReady() = 0;
+	/// Returns true if the stream is ready for writing, false otherwise.
+	/// This is usually only implemented on streams supporting asynchronous
+	/// operations.
+	virtual bool IsWriteReady() = 0;
+
+protected:
+	/// Sets the mode on the stream; should be called by a stream when it is opened.
+	void SetStreamDetails(const URL& url, int stream_mode);		
+
+private:
+	URL url;
+	int stream_mode;
 };
 };
 
 
 }
 }

+ 13 - 17
Include/RmlUi/Core/StreamMemory.h

@@ -49,46 +49,42 @@ public:
 	StreamMemory(size_t initial_size);
 	StreamMemory(size_t initial_size);
 	/// Read only memory stream based on the existing buffer
 	/// Read only memory stream based on the existing buffer
 	StreamMemory(const byte* buffer, size_t buffer_size);
 	StreamMemory(const byte* buffer, size_t buffer_size);
-	/// Copy a memory stream
-	StreamMemory(const StreamMemory& copy); 
 	virtual ~StreamMemory();
 	virtual ~StreamMemory();
 
 
-	StreamMemory& operator=(const StreamMemory& copy);
-
 	/// Close the stream
 	/// Close the stream
-	virtual void Close();	
+	void Close() override;	
 
 
 	/// Are we at the end of the stream
 	/// Are we at the end of the stream
-	virtual bool IsEOS() const;
+	bool IsEOS() const override;
 
 
 	/// Size of this stream ( in bytes )
 	/// Size of this stream ( in bytes )
-	virtual size_t Length() const;
+	size_t Length() const override;
 
 
 	/// Get Stream position ( in bytes )
 	/// Get Stream position ( in bytes )
-	size_t Tell() const;
+	size_t Tell() const override;
 
 
 	/// Set Stream position ( in bytes )
 	/// Set Stream position ( in bytes )
-	bool Seek(long offset, int origin) const;
+	bool Seek(long offset, int origin) const override;
 
 
 	/// Read from the stream
 	/// Read from the stream
 	using Stream::Read;
 	using Stream::Read;
-	virtual size_t Read(void* buffer, size_t bytes) const;
+	size_t Read(void* buffer, size_t bytes) const override;
 
 
 	/// Peek into the stream
 	/// Peek into the stream
-	virtual size_t Peek(void *buffer, size_t bytes) const;
+	size_t Peek(void *buffer, size_t bytes) const override;
 
 
 	/// Write to the stream
 	/// Write to the stream
 	using Stream::Write;
 	using Stream::Write;
-	virtual size_t Write(const void* buffer, size_t bytes);
+	size_t Write(const void* buffer, size_t bytes) override;
 
 
 	/// Truncate the stream to the specified length
 	/// Truncate the stream to the specified length
-	virtual size_t Truncate(size_t bytes);
+	size_t Truncate(size_t bytes) override;
 
 
 	/// Push onto the front of the stream
 	/// Push onto the front of the stream
-	virtual size_t PushFront(const void* buffer, size_t bytes);
+	size_t PushFront(const void* buffer, size_t bytes) override;
 
 
 	/// Pop from the front of the stream
 	/// Pop from the front of the stream
-	virtual size_t PopFront(size_t bytes);
+	size_t PopFront(size_t bytes) override;
 
 
 	/// Raw access to the stream
 	/// Raw access to the stream
 	const byte* RawStream() const;
 	const byte* RawStream() const;
@@ -97,10 +93,10 @@ public:
 	void Erase(size_t offset, size_t bytes);
 	void Erase(size_t offset, size_t bytes);
 
 
 	/// Does the stream have data available for reading
 	/// Does the stream have data available for reading
-	virtual bool IsReadReady();
+	bool IsReadReady() override;
 
 
 	/// Is the stream able to accept data now
 	/// Is the stream able to accept data now
-	virtual bool IsWriteReady();
+	bool IsWriteReady() override;
 
 
 	/// Sets this streams source URL, useful data that is stored
 	/// Sets this streams source URL, useful data that is stored
 	/// in memory streams that originated from files
 	/// in memory streams that originated from files

+ 0 - 101
Include/RmlUi/Core/String.h

@@ -1,101 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICORESTRING_H
-#define RMLUICORESTRING_H
-
-#include "Header.h"
-#include "StringBase.h"
-#include <stdarg.h>
-#include <string.h>
-#include <vector>
-
-namespace Rml {
-namespace Core {
-
-typedef StringBase< char > String;
-typedef std::vector< String > StringList;
-
-// Template specialisation of the constructor and FormatString() methods that use variable argument lists.
-template<>
-RMLUICORE_API StringBase<char>::StringBase(StringBase<char>::size_type max_size, const char* fmt, ...);
-template<>
-RMLUICORE_API int StringBase<char>::FormatString(StringBase<char>::size_type max_size, const char* fmt, ...);
-
-// Global operators for adding C strings to strings.
-RMLUICORE_API String operator+(const char* cstring, const String& string);
-
-// partial specialization follows
-
-/* !!! CHANGING THIS METHOD BREAKS ABI COMPATIBILITY DUE TO INLINING !!! */
-template<>
-RMLUICORE_API_INLINE bool StringBase< char >::operator<(const char * compare) const
-{
-	return strcmp( value, compare ) < 0;
-}
-
-/* !!! CHANGING THIS METHOD BREAKS ABI COMPATIBILITY DUE TO INLINING !!! */
-template<>
-RMLUICORE_API_INLINE bool StringBase< char >::operator==(const char * compare) const
-{
-	return strcmp( value, compare ) == 0;
-}
-
-/* !!! CHANGING THIS METHOD BREAKS ABI COMPATIBILITY DUE TO INLINING !!! */
-template<>
-RMLUICORE_API_INLINE bool StringBase< char >::operator!=(const char * compare) const
-{
-	return strcmp( value, compare ) != 0;
-}
-
-// Redefine Windows APIs as their STDC counterparts.
-#ifdef RMLUI_PLATFORM_WIN32
-	#define strcasecmp stricmp
-	#define strncasecmp strnicmp
-#endif
-
-struct hash_str_lowercase {
-	std::size_t operator()(const ::Rml::Core::String& string) const
-	{
-		auto str_lower = string.ToLower();
-		return str_lower.Hash();
-	}
-};
-}
-}
-
-namespace std {
-	template <> struct hash<::Rml::Core::String> {
-		std::size_t operator()(const ::Rml::Core::String& string) const
-		{
-			return string.Hash();
-		}
-	};
-}
-
-#endif

+ 0 - 220
Include/RmlUi/Core/StringBase.h

@@ -1,220 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-#ifndef RMLUICORESTRINGBASE_H
-#define RMLUICORESTRINGBASE_H
-
-#include "Debug.h"
-#include <stdlib.h>
-
-namespace Rml {
-namespace Core {
-
-/**
-	Base String Implementation
-	@author Lloyd Weehuizen
- */
-template< typename T >
-class StringBase
-{
-public:
-	typedef size_t size_type;
-	static const size_type npos = (size_type)-1;
-
-	StringBase();
-	StringBase(const StringBase& copy);
-	StringBase(const T* string);
-	StringBase(const T* string_start, const T* string_end);
-	StringBase(size_type length, const T character);
-	StringBase(size_type max_length, const T* fmt, ...);
-
-	~StringBase();
-
-	/// Is the string currently empty
-	inline bool Empty() const;
-	/// Clear the string to empty
-	void Clear();
-
-	/// The length of the string
-	inline size_type Length() const;
-	/// Get the hash value of this string
-	inline unsigned int Hash() const;
-	/// Access the string as a standard C string
-	inline const T* CString() const;
-	
-	/// Reserve space for at least this much data
-	inline void Reserve(size_type size);
-
-	/// Find the given string within this string
-	/// @param find The string to search for
-	/// @param offset Starting location of the search
-	size_type Find(const T* find, size_type offset = 0) const;
-	/// Find the given string within this string
-	/// @param find The string to search for
-	/// @param offset Starting location of the search
-	size_type Find(const StringBase<T>& find, size_type offset = 0) const;
-	/// Reverse find the given string within this string
-	/// @param find The string to search for
-	/// @param offset Starting location of the search
-	size_type RFind(const T* find, size_type offset = npos) const;
-	/// Reverse find the given string within this string
-	/// @param find The string to search for
-	/// @param offset Starting location of the search
-	size_type RFind(const StringBase<T>& find, size_type offset = npos) const;
-
-	/// Replace all occurances of the given string with another
-	/// @param find The string to search for
-	/// @param replace The string to replace it with
-	StringBase<T> Replace(const T* find, const T* replace) const;
-	/// Replace all occurances of the given string with another
-	/// @param find The string to search for
-	/// @param replace The string to replace it with
-	StringBase<T> Replace(const StringBase<T>& find, const StringBase<T>& replace) const;
-
-	/// Return a substring of this string
-	/// @param start The starting position
-	/// @param length The number of characters to copy
-	inline StringBase<T> Substring(size_type start, size_type length = StringBase<T>::npos) const;
-
-	/// Append the given string to this string
-	/// @param append The string to appen
-	/// @param count The number of characters to append
-	inline StringBase<T>& Append(const T* append, size_type count = StringBase<T>::npos);
-	/// Assign the given string to this string
-	/// @param assign The string to assign
-	/// @param count The number of characters to assign
-	inline StringBase<T>& Append(const StringBase<T>& append, size_type count = StringBase<T>::npos);
-	/// Append a single character
-	/// @param append The character to append
-	inline StringBase<T>& Append(const T& append);
-
-	/// Assign the given string to this string
-	/// @param assign The string to assign
-	/// @param length The number of characters to assign
-	inline StringBase<T>& Assign(const T* assign, size_type count = StringBase<T>::npos);
-	/// Assign the given string to this string
-	/// @param assign The string to assign
-	/// @param count The number of characters to assign
-	inline StringBase<T>& Assign(const T* assign, const T* end);
-	/// Assign the given string to this string
-	/// @param assign The string to assign
-	/// @param count The number of characters to assign
-	inline StringBase<T>& Assign(const StringBase<T>& assign, size_type count = StringBase<T>::npos);
-
-	/// Insert a string into this string
-	/// @param index Index to insert the characters
-	/// @param insert String to insert
-	/// @param count Number of characters to insert
-	inline void Insert(size_type index, const T* insert, size_type count = StringBase<T>::npos);
-	/// Insert a string into this string
-	/// @param index Index to insert the characters
-	/// @param insert String to insert
-	/// @param count Number of characters to insert
-	inline void Insert(size_type index, const StringBase<T>& insert, size_type count = StringBase<T>::npos);
-	/// Insert a character into this string
-	/// @param index Index to insert the characters
-	/// @param insert Character to insert	
-	inline void Insert(size_type index, const T& insert);
-
-	/// Erase characters from this string
-	/// @param index Index to erase the characters
-	/// @param length Number of characters to erase
-	inline void Erase(size_type index, size_type length = StringBase<T>::npos);
-
-	/// sprsize_typef style string formatting.
-	/// NOTE: This is not implemented in the base layer and requires template
-	/// specialisation of the specific string type
-	/// @param max_length Maximum length of the result
-	/// @param format The sprsize_typef style formatting
-	int FormatString(size_type max_length, const T* format, ...);
-
-	/// Resize the string to the given size, inserts space if the string is getting bigger
-	/// @param size New size
-	void Resize(size_type size);
-
-	/// Create a lowercase version of the string
-	/// @returns The lower case representation of the string
-	StringBase<T> ToLower() const;
-	/// Create a lowercase version of the string
-	/// @returns The lower case representation of the string
-	StringBase<T> ToUpper() const;
-
-	inline bool operator==(const T* compare) const;
-	inline bool operator==(const StringBase<T>& compare) const;
-
-	inline bool operator!=(const T* compare) const;
-	inline bool operator!=(const StringBase<T>& compare) const;
-
-	inline bool operator<(const T* compare) const;
-	inline bool operator<(const StringBase<T>& compare) const;
-
-	inline StringBase<T>& operator=(const T* assign);
-	inline StringBase<T>& operator=(const StringBase<T>& assign);
-
-	inline StringBase<T> operator+(const T* append) const;
-	inline StringBase<T> operator+(const StringBase<T>& append) const;
-
-	inline StringBase<T>& operator+=(const T* append);
-	inline StringBase<T>& operator+=(const StringBase<T>& append);
-	inline StringBase<T>& operator+=(const T& append);
-
-	inline const T& operator[](size_type index) const;
-	inline T& operator[](size_type index);
-
-protected:	
-
-	T* value;
-	size_type buffer_size;
-	size_type length;
-	mutable unsigned int hash;
-	static const size_type LOCAL_BUFFER_SIZE = 8;
-	char local_buffer[LOCAL_BUFFER_SIZE];
-
-	size_type GetLength(const T* string) const;
-
-	// Copies the source string to target string
-	inline void Copy(T* target, const T* src, size_type length, bool terminate = false);
-
-	// Internal implementations of the public interfaces,
-	// all these functions take the length of the const T*'s they're
-	// dealing with which *MUST* be accurate.
-	// Its up to the external interfaces to provide valid values for these functions
-	inline size_type _Find(const T* find, size_type find_length, size_type offset = 0) const;
-	inline size_type _RFind(const T* find, size_type find_length, size_type offset = 0) const;
-	inline StringBase<T> _Replace(const T* find, size_type find_length, const T* replace, size_type replace_length) const;
-	inline StringBase<T>& _Append(const T* append, size_type append_length, size_type count = StringBase<T>::npos);
-	inline StringBase<T>& _Assign(const T* assign, size_type assign_length, size_type count = StringBase<T>::npos);
-	inline void _Insert(size_type index, const T* insert, size_type insert_length, size_type count = StringBase<T>::npos);
-};
-
-}
-}
-
-#include "StringBase.inl"
-
-#endif

+ 0 - 734
Include/RmlUi/Core/StringBase.inl

@@ -1,734 +0,0 @@
-/*
- * This source file is part of RmlUi, the HTML/CSS Interface Middleware
- *
- * For the latest information, see http://github.com/mikke89/RmlUi
- *
- * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
- * Copyright (c) 2019 The RmlUi Team, and contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- */
-
-namespace Rml {
-namespace Core {
-
-template< typename T >
-StringBase< T >::StringBase() : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	value[0] = 0;
-}
-
-template< typename T >
-StringBase< T >::StringBase(const StringBase< T >& copy) : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	value[0] = 0;
-	*this = copy;
-}
-
-template< typename T >
-StringBase< T >::StringBase(const T* string) : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	value[0] = 0;
-	*this = string;
-}
-
-template< typename T >
-StringBase< T >::StringBase(const T* string_start, const T* string_end) : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	value[0] = 0;
-	length = (string_end - string_start);
-
-	if (length > 0)
-	{
-		Reserve(length);
-		Copy(value, string_start, length, true);
-	}
-}
-
-template< typename T >
-StringBase< T >::StringBase(size_type count, const T character) : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	value[0] = 0;
-	length = count;
-
-	if (length > 0)
-	{
-		Reserve(length);
-		for (size_type i = 0; i < length; i++)
-			value[i] = character;
-		value[length] = '\0';
-	}
-}
-
-template< typename T >
-StringBase< T >::StringBase(size_type RMLUI_UNUSED_PARAMETER(max_length), const T * RMLUI_UNUSED_PARAMETER(fmt), ...) : value((T*)local_buffer), buffer_size(LOCAL_BUFFER_SIZE), length(0), hash(0)
-{
-	RMLUI_UNUSED(max_length);
-	RMLUI_UNUSED(fmt);
-
-	value[0] = 0;
-	// Can't implement this at the base level, requires template specialisation
-	RMLUI_ERRORMSG("Not implemented.");
-}
-
-template< typename T >
-StringBase< T >::~StringBase()
-{
-	if (value != (T*)local_buffer)
-		free(value);
-}
-
-template< typename T >
-bool StringBase< T >::Empty() const
-{
-	return length == 0;
-}
-
-template< typename T >
-void StringBase< T >::Clear()
-{
-	if (value != (T*)local_buffer)
-		free(value);
-
-	length = 0;
-	hash = 0;
-	value = (T*)local_buffer;
-	value[0] = 0;
-	buffer_size = LOCAL_BUFFER_SIZE;
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::Length() const
-{
-	return length;
-}
-
-template< typename T >
-unsigned int StringBase< T >::Hash() const
-{
-	if (hash == 0 && length > 0)
-	{
-		// FNV-1 hash algorithm
-		unsigned char* bp = (unsigned char*)value;	// start of buffer
-		unsigned char* be = (unsigned char*)value + (length * sizeof(T));
-
-		// FNV-1a hash each octet in the buffer
-		while (bp < be)
-		{
-			// xor the bottom with the current octet
-			hash ^= *bp++;
-
-			/* multiply by the 32 bit FNV magic prime mod 2^32 */
-#if !defined(__GNUC__)
-			const unsigned int FNV_32_PRIME = ((unsigned int)16777619);
-			hash *= FNV_32_PRIME;
-#else
-			hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
-#endif
-		}
-	}
-	return hash;
-}
-
-template< typename T >
-const T * StringBase< T >::CString() const
-{
-	return value;
-}
-
-template< typename T >
-void StringBase< T >::Reserve(size_type size)
-{
-	size_type new_size = (size + 1) * sizeof(T);
-
-	if (buffer_size >= new_size)
-		return;
-
-	// Pad out to a block of 16 bytes
-	const int BLOCK_SIZE = 16;
-	new_size = (new_size + BLOCK_SIZE - 1) & (~(BLOCK_SIZE - 1));
-
-	if (value == (T*)local_buffer)
-	{
-		T* new_value = (T*)realloc(NULL, new_size);
-		RMLUI_ASSERTMSG(new_value, "Could not reserve memory for String, realloc failed.");
-		if (new_value != NULL)
-		{
-			buffer_size = new_size;
-			Copy(new_value, (T*)local_buffer, LOCAL_BUFFER_SIZE / sizeof(T));
-			value = new_value;
-		}
-	}
-	else
-	{
-		T* new_value = (T*)realloc(value, new_size);
-		RMLUI_ASSERTMSG(new_value, "Could not reserve memory for String, realloc failed.");
-		if (new_value != NULL)
-		{
-			buffer_size = new_size;
-			value = new_value;
-		}
-	}
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::Find(const T * find, size_type offset) const
-{
-	return _Find(find, GetLength(find), offset);
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::Find(const StringBase< T > & find, size_type offset) const
-{
-	return _Find(find.CString(), find.Length(), offset);
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::RFind(const T * find, size_type offset) const
-{
-	return _RFind(find, GetLength(find), offset);
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::RFind(const StringBase< T > & find, size_type offset) const
-{
-	return _RFind(find.CString(), find.Length(), offset);
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::Replace(const T * find, const T * replace) const
-{
-	return _Replace(find, GetLength(find), replace, GetLength(replace));
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::Replace(const StringBase< T > & find, const StringBase< T > & replace) const
-{
-	return _Replace(find.CString(), find.Length(), replace.CString(), replace.Length());
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::Substring(size_type start, size_type count) const
-{
-	// Ensure we're not going of bounds
-	if (count > length - start)
-		count = length - start;
-
-	if (start > length)
-		count = 0;
-
-	return StringBase< T >(&value[start], &value[start + count]);
-}
-
-template< typename T >
-StringBase< T > & StringBase< T >::Append(const T * append, size_type count)
-{
-	return _Append(append, GetLength(append), count);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::Append(const StringBase< T > & append, size_type count)
-{
-	return _Append(append.CString(), append.Length(), count);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::Append(const T & append)
-{
-	T buffer[2] = { append, 0 };
-	return (*this += buffer);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::Assign(const T * assign, size_type count)
-{
-	size_type assign_length = GetLength(assign);
-	return _Assign(assign, count > assign_length ? assign_length : count);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::Assign(const T * assign, const T * end)
-{
-	return _Assign(assign, end - assign);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::Assign(const StringBase< T > & assign, size_type count)
-{
-	return _Assign(assign.CString(), assign.length, count);
-}
-
-// Insert a string into this string
-template< typename T >
-void StringBase< T >::Insert(size_type index, const T * insert, size_type count)
-{
-	return _Insert(index, insert, GetLength(insert), count);
-}
-
-// Insert a string into this string
-template< typename T >
-void StringBase< T >::Insert(size_type index, const StringBase< T > & insert, size_type count)
-{
-	return _Insert(index, insert.value, insert.length, count);
-}
-
-// Insert a character into this string
-template< typename T >
-void StringBase< T >::Insert(size_type index, const T & insert)
-{
-	return _Insert(index, &insert, 1, 1);
-}
-
-/// Erase characters from this string
-template< typename T >
-void StringBase< T >::Erase(size_type index, size_type count)
-{
-	if (index >= length)
-		return;
-
-	if (count == npos)
-	{
-		Resize(index);
-	}
-	else
-	{
-		size_type erase_amount = count < length - index ? count : length - index;
-
-		Copy(&value[index], &value[index + erase_amount], length - index - erase_amount, true);
-
-		length -= erase_amount;
-
-		if (length == 0)
-			Clear();
-	}
-}
-
-template< typename T >
-int StringBase< T >::FormatString(size_type RMLUI_UNUSED_PARAMETER(max_length), const T * RMLUI_UNUSED_PARAMETER(fmt), ...)
-{
-	RMLUI_UNUSED(max_length);
-	RMLUI_UNUSED(fmt);
-
-	RMLUI_ERRORMSG("Not implemented.");
-	return -1;
-}
-
-template< typename T >
-void StringBase< T >::Resize(size_type new_length)
-{
-	Reserve(new_length);
-	length = new_length;
-	value[length] = '\0';
-
-	if (length == 0)
-		Clear();
-}
-
-// Create a lowercase version of the string
-template< typename T >
-StringBase< T > StringBase< T >::ToLower() const
-{
-	// Loop through the string, looking for an uppercase character
-	size_t copy_index = npos;
-	for (size_t i = 0; i < length; i++)
-	{
-		if (value[i] >= 'A' && value[i] <= 'Z')
-		{
-			copy_index = i;
-			break;
-		}
-	}
-
-	// If theres no lowercase letters, simply copy us direct
-	if (copy_index == npos)
-		return StringBase< T >(*this);
-
-	StringBase< T > lowercase(CString(), CString() + copy_index);
-	// Otherwise trawl through the rest of the letters
-	for (size_t i = copy_index; i < length; i++)
-	{
-		if (value[i] >= 'A' && value[i] <= 'Z')
-			lowercase.Append((T)(value[i] + ('a' - 'A')));
-		else
-			lowercase.Append(value[i]);
-	}
-
-	return lowercase;
-}
-
-// Create a lowercase version of the string
-template< typename T >
-StringBase< T > StringBase< T >::ToUpper() const
-{
-	// Loop through the string, looking for an lowercase character
-	size_t copy_index = npos;
-	for (size_t i = 0; i < length; i++)
-	{
-		if (value[i] >= 'a' && value[i] <= 'z')
-		{
-			copy_index = i;
-			break;
-		}
-	}
-
-	// If theres no lowercase letters, simply copy us direct
-	if (copy_index == npos)
-		return StringBase< T >(*this);
-
-	StringBase< T > uppercase(CString(), CString() + copy_index);
-	// Otherwise trawl through the rest of the letters
-	for (size_t i = copy_index; i < length; i++)
-	{
-		if (value[i] >= 'a' && value[i] <= 'z')
-			uppercase.Append((T)(value[i] - ('a' - 'A')));
-		else
-			uppercase.Append(value[i]);
-	}
-
-	return uppercase;
-}
-
-template< typename T >
-bool StringBase< T >::operator==(const T * compare) const
-{
-	size_type index = 0;
-
-	while (index < length && compare[index] == value[index])
-		index++;
-
-	return index == length && compare[index] == '\0';
-}
-
-template< typename T >
-bool StringBase< T >::operator==(const StringBase< T > & compare) const
-{
-	if (length != compare.length)
-		return false;
-
-	if (Hash() != compare.Hash())
-		return false;
-
-	return (*this) == compare.value;
-}
-
-template< typename T >
-bool StringBase< T >::operator!=(const T * compare) const
-{
-	return !(*this == compare);
-}
-
-template< typename T >
-bool StringBase< T >::operator!=(const StringBase< T > & compare) const
-{
-	return !(*this == compare);
-}
-
-template< typename T >
-bool StringBase< T >::operator<(const T * compare) const
-{
-	size_type index = 0;
-	while (index < length && compare[index] == value[index])
-		index++;
-
-	// Check if we reached the end of the string
-	if (index < length)
-	{
-		// If we didn't check if we reached the end of
-		// the string we're comparing against, if so
-		// then we're not less than
-		if (compare[index] == 0)
-			return false;
-
-		// Check the character at index
-		return value[index] < compare[index];
-	}
-	else
-	{
-		// We reached the end of our string,
-		// if the string we're comparing with still
-		// has data, then we're smaller
-		if (compare[index] != 0)
-			return true;
-	}
-
-	return false;
-}
-
-template< typename T >
-bool StringBase< T >::operator<(const StringBase< T > & compare) const
-{
-	return *this < compare.CString();
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::operator=(const T * assign)
-{
-	return Assign(assign);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::operator=(const StringBase< T > & assign)
-{
-	StringBase< T >& out = Assign(assign);
-	out.hash = assign.hash;
-	return out;
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::operator+(const T * add) const
-{
-	StringBase< T > combined(*this);
-	combined.Append(add);
-
-	return combined;
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::operator+(const StringBase< T > & add) const
-{
-	StringBase< T > combined(*this);
-	combined.Append(add);
-
-	return combined;
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::operator+=(const T * add)
-{
-	return Append(add);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::operator+=(const StringBase< T > & add)
-{
-	return _Append(add.CString(), add.length);
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::operator+=(const T & add)
-{
-	return Append(add);
-}
-
-template< typename T >
-const T& StringBase< T >::operator[](size_type index) const
-{
-	RMLUI_ASSERT(index < length);
-	return value[index];
-}
-
-template< typename T >
-T& StringBase< T >::operator[](size_type index)
-{
-	RMLUI_ASSERT(index < length);
-	return value[index];
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::GetLength(const T * string) const
-{
-	const T* ptr = string;
-
-	while (*ptr)
-	{
-		ptr++;
-	}
-
-	return ptr - string;
-}
-
-template< typename T >
-void StringBase< T >::Copy(T * target, const T * src, size_type length, bool terminate)
-{
-	// Copy values
-	for (size_type i = 0; i < length; i++)
-	{
-		*target++ = *src++;
-	}
-
-	if (terminate)
-	{
-		*target++ = 0;
-	}
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::_Find(const T * find, size_type find_length, size_type offset) const
-{
-	size_type needle_index = 0;
-	size_type haystack_index = offset;
-
-	// If the find length is greater than the string we have, it can't be here
-	if (find_length > length)
-		return npos;
-
-	// While there's still data in the haystack loop
-	while (value[haystack_index])
-	{
-		// If the current haystack posize_typeer plus needle offset matches,
-		// advance the needle index
-		if (value[haystack_index + needle_index] == find[needle_index])
-		{
-			needle_index++;
-
-			// If we reach the end of the search term, return the current haystack index
-			if (needle_index == find_length)
-				return haystack_index;
-		}
-		else
-		{
-			// Advance haystack index by one and reset needle index.
-			haystack_index++;
-			needle_index = 0;
-		}
-	}
-
-	return npos;
-}
-
-template< typename T >
-typename StringBase< T >::size_type StringBase< T >::_RFind(const T * find, size_type find_length, size_type offset) const
-{
-	RMLUI_ASSERT(find_length > 0);
-
-	size_type needle_index = 0;
-	size_type haystack_index = (offset < length ? offset : length) - find_length;
-
-	// If the find length is greater than the string we have, it can't be here
-	if (find_length > length)
-		return npos;
-
-	// While theres still data in the haystack loop
-	for (;;)
-	{
-		// If the current haystack index plus needle offset matches,
-		// advance the needle index
-		if (value[haystack_index + needle_index] == find[needle_index])
-		{
-			needle_index++;
-
-			// If we reach the end of the search term, return the current haystack index
-			if (find[needle_index] == 0)
-				return haystack_index;
-		}
-		else
-		{
-			if (haystack_index == 0)
-				return npos;
-
-			// Advance haystack index backwards
-			haystack_index--;
-			needle_index = 0;
-		}
-	}
-}
-
-template< typename T >
-StringBase< T > StringBase< T >::_Replace(const T * find, size_type find_length, const T * replace, size_type replace_length) const
-{
-	StringBase< T > result;
-
-	size_type offset = 0;
-	// Loop until we reach the end of the string
-	while (offset < Length())
-	{
-		// Look for the next search term
-		size_type pos = _Find(find, find_length, offset);
-
-		// Term not found, add remainder and return
-		if (pos == npos)
-			return result + (Substring(offset).CString());
-
-		// Add the unchanged text and replacement after it
-		result += Substring(offset, pos - offset);
-		result._Append(replace, replace_length);
-
-		// Advance the find position
-		offset = pos + find_length;
-	}
-
-	hash = 0;
-
-	return result;
-}
-
-template< typename T >
-StringBase< T >& StringBase< T >::_Append(const T * append, size_type append_length, size_type count)
-{
-	size_type add_length = count < append_length ? count : append_length;
-
-	if (add_length == 0)
-		return *this;
-
-	Reserve(length + add_length);
-	Copy(&value[length], append, add_length, true);
-	length += add_length;
-
-	hash = 0;
-
-	return *this;
-}
-
-template< typename T >
-StringBase< T > & StringBase< T >::_Assign(const T * assign, size_type assign_length, size_type count)
-{
-	size_type new_length = count < assign_length ? count : assign_length;
-
-	if (new_length == 0)
-	{
-		Clear();
-	}
-	else
-	{
-		Reserve(new_length);
-		Copy(value, assign, new_length, true);
-	}
-
-	length = new_length;
-
-	hash = 0;
-
-	return *this;
-}
-
-template< typename T >
-void StringBase< T >::_Insert(size_type index, const T * insert, size_type insert_length, size_type count)
-{
-	if (index >= length)
-	{
-		Append(insert, count);
-		return;
-	}
-
-	size_type add_length = count < insert_length ? count : insert_length;
-
-	Reserve(length + add_length);
-
-	for (size_type i = length + 1; i > index; i--)
-		value[i + add_length - 1] = value[i - 1];
-
-	Copy(&value[index], insert, add_length);
-	length += add_length;
-
-	hash = 0;
-}
-
-}
-}

+ 150 - 37
Include/RmlUi/Core/StringUtilities.h

@@ -29,9 +29,10 @@
 #ifndef RMLUICORESTRINGUTILITIES_H
 #ifndef RMLUICORESTRINGUTILITIES_H
 #define RMLUICORESTRINGUTILITIES_H
 #define RMLUICORESTRINGUTILITIES_H
 
 
+#include <algorithm>
+#include <stddef.h>
 #include "Header.h"
 #include "Header.h"
 #include "Types.h"
 #include "Types.h"
-#include "String.h"
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -41,68 +42,180 @@ namespace Core {
 	@author Lloyd Weehuizen
 	@author Lloyd Weehuizen
  */
  */
 
 
-class RMLUICORE_API StringUtilities
+// Redefine Windows APIs as their STDC counterparts.
+#ifdef RMLUI_PLATFORM_WIN32
+	#define strcasecmp stricmp
+	#define strncasecmp strnicmp
+#endif
+
+class StringView;
+
+/// Construct a string using sprintf-style syntax.
+RMLUICORE_API String CreateString(size_t max_size, const char* format, ...);
+
+/// Format to a string using sprintf-style syntax.
+RMLUICORE_API int FormatString(String& string, size_t max_size, const char* format, ...);
+
+
+namespace StringUtilities
 {
 {
-public:
 	/// Expands character-delimited list of values in a single string to a whitespace-trimmed list
 	/// Expands character-delimited list of values in a single string to a whitespace-trimmed list
 	/// of values.
 	/// of values.
 	/// @param[out] string_list Resulting list of values.
 	/// @param[out] string_list Resulting list of values.
 	/// @param[in] string String to expand.
 	/// @param[in] string String to expand.
 	/// @param[in] delimiter Delimiter found between entries in the string list.
 	/// @param[in] delimiter Delimiter found between entries in the string list.
-	static void ExpandString(StringList& string_list, const String& string, const char delimiter = ',');
+	RMLUICORE_API void ExpandString(StringList& string_list, const String& string, const char delimiter = ',');
+	/// Expands character-delimited list of values with custom quote characters.
+	/// @param[out] string_list Resulting list of values.
+	/// @param[in] string String to expand.
+	/// @param[in] delimiter Delimiter found between entries in the string list.
+	/// @param[in] quote_character Begin quote
+	/// @param[in] unquote_character End quote
+	/// @param[in] ignore_repeated_delimiters If true, repeated values of the delimiter will not add additional entries to the list.
+	RMLUICORE_API void ExpandString(StringList& string_list, const String& string, const char delimiter, char quote_character, char unquote_character, bool ignore_repeated_delimiters = false);
 	/// Joins a list of string values into a single string separated by a character delimiter.
 	/// Joins a list of string values into a single string separated by a character delimiter.
 	/// @param[out] string Resulting concatenated string.
 	/// @param[out] string Resulting concatenated string.
 	/// @param[in] string_list Input list of string values.
 	/// @param[in] string_list Input list of string values.
 	/// @param[in] delimiter Delimiter to insert between the individual values.
 	/// @param[in] delimiter Delimiter to insert between the individual values.
-	static void JoinString(String& string, const StringList& string_list, const char delimiter = ',');
-
-	/// Hashes a string of data to an integer value using the FNV algorithm.
-	/// @param[in] data Data to hash.
-	/// @param[in] length Length of the string to hash. If this is -1, the data will be interpreted as a C string.
-	/// @return Integer hash of the data.
-	static Hash FNVHash(const char* data, int length = -1);
-
-	/// Converts a character array in UTF-8 encoding to a vector of words. The UCS-2 words will be encoded as
-	/// either big- or little-endian, depending on the host processor.
-	/// @param[in] input Input string in UTF-8 encoding.
-	/// @param[out] output Output vector of UCS-2 characters.
-	/// @return True if the conversion went successfully, false if any characters had to be skipped (this will occur if they can't fit into UCS-2).
-	static bool UTF8toUCS2(const String& input, std::vector< word >& output);
-	/// Converts a vector of words in UCS-2 encoding into a character array in UTF-8 encoding. This
-	/// function assumes the endianness of the input words to be the same as the host processor.
-	/// @param[in] input Input vector in UCS-2 encoding.
-	/// @param[out] output Output string in UTF-8 encoding.
-	/// @return True if the conversion went successfully, false if not.
-	static bool UCS2toUTF8(const std::vector< word >& input, String& output);
-	/// Converts an array of words in UCS-2 encoding into a character array in UTF-8 encoding. This
-	/// function assumes the endianness of the input words to be the same as the host processor.
-	/// @param[in] input Input array of words in UCS-2 encoding.
-	/// @param[in] input_size Length of the input array.
-	/// @param[out] output Output string in UTF-8 encoding.
-	/// @return True if the conversion went successfully, false if not.
-	static bool UCS2toUTF8(const word* input, size_t input_size, String& output);
+	RMLUICORE_API void JoinString(String& string, const StringList& string_list, const char delimiter = ',');
+
+	/// Converts upper-case characters in string to lower-case.
+	RMLUICORE_API String ToLower(const String& string);
+
+	// Replaces all occurences of 'search' in 'subject' with 'replace'.
+	RMLUICORE_API String Replace(String subject, const String& search, const String& replace);
+	// Replaces all occurences of 'search' in 'subject' with 'replace'.
+	RMLUICORE_API String Replace(String subject, char search, char replace);
 
 
 	/// Checks if a given value is a whitespace character.
 	/// Checks if a given value is a whitespace character.
-	/// @param[in] x The character to evaluate.
-	/// @return True if the character is whitespace, false otherwise.
 	template < typename CharacterType >
 	template < typename CharacterType >
-	static bool IsWhitespace(CharacterType x)
+	inline bool IsWhitespace(CharacterType x)
 	{
 	{
 		return (x == '\r' || x == '\n' || x == ' ' || x == '\t');
 		return (x == '\r' || x == '\n' || x == ' ' || x == '\t');
 	}
 	}
 
 
 	/// Strip whitespace characters from the beginning and end of a string.
 	/// Strip whitespace characters from the beginning and end of a string.
-	/// @param[in] string The string to trim.
-	/// @return The stripped string.
-	static String StripWhitespace(const String& string);
+	RMLUICORE_API String StripWhitespace(const String& string);
 
 
 	/// Operator for STL containers using strings.
 	/// Operator for STL containers using strings.
 	struct RMLUICORE_API StringComparei
 	struct RMLUICORE_API StringComparei
 	{
 	{
 		bool operator()(const String& lhs, const String& rhs) const;
 		bool operator()(const String& lhs, const String& rhs) const;
 	};
 	};
+
+	// Decode the first code point in a zero-terminated UTF-8 string.
+	RMLUICORE_API Character ToCharacter(const char* p);
+
+	// Encode a single code point as a UTF-8 string.
+	RMLUICORE_API String ToUTF8(Character character);
+
+	// Encode an array of code points as a UTF-8 string.
+	RMLUICORE_API String ToUTF8(const Character* characters, int num_characters);
+
+	/// Returns number of characters in a UTF-8 string.
+	RMLUICORE_API size_t LengthUTF8(StringView string_view);
+
+	// Seek forward in a UTF-8 string, skipping continuation bytes.
+	inline const char* SeekForwardUTF8(const char* p, const char* p_end)
+	{
+		while (p != p_end && (*p & 0b1100'0000) == 0b1000'0000)
+			++p;
+		return p;
+	}
+	// Seek backward in a UTF-8 string, skipping continuation bytes.
+	inline const char* SeekBackwardUTF8(const char* p, const char* p_begin)
+	{
+		while ((p + 1) != p_begin && (*p & 0b1100'0000) == 0b1000'0000)
+			--p;
+		return p;
+	}
+
+
+	/// Converts a string in UTF-8 encoding to a u16string in UTF-16 encoding.
+	/// Reports a warning if some or all characters could not be converted.
+	RMLUICORE_API U16String ToUTF16(const String& str);
+
+	/// Converts a u16string in UTF-16 encoding into a string in UTF-8 encoding.
+	/// Reports a warning if some or all characters could not be converted.
+	RMLUICORE_API String ToUTF8(const U16String& u16str);
+}
+
+
+/*
+	A poor man's string view. 
+	
+	The string view is agnostic to the underlying encoding, any operation will strictly operate on bytes.
+*/
+
+class RMLUICORE_API StringView {
+public:
+	StringView(const char* p_begin, const char* p_end);
+	StringView(const String& string);
+	StringView(const String& string, size_t offset);
+	StringView(const String& string, size_t offset, size_t count);
+
+	// String comparison to another view
+	bool operator==(const StringView& other) const;
+	inline bool operator!=(const StringView& other) const { return !(*this == other); }
+
+	inline const char* begin() const { return p_begin; }
+	inline const char* end() const { return p_end; }
+
+	inline size_t size() const { return p_end - p_begin; }
+
+private:
+	const char* p_begin;
+	const char* p_end;
+};
+
+
+/*
+	An iterator for UTF-8 strings. 
+
+	The increment and decrement operations will move to the beginning of the next or the previous
+	UTF-8 character, respectively. The dereference operator will resolve the current code point.
+
+*/
+
+class RMLUICORE_API StringIteratorU8 {
+public:
+	StringIteratorU8(const char* p_begin, const char* p, const char* p_end);
+	StringIteratorU8(const String& string);
+	StringIteratorU8(const String& string, size_t offset);
+	StringIteratorU8(const String& string, size_t offset, size_t count);
+
+	// Seeks forward to the next UTF-8 character. Iterator must be valid.
+	StringIteratorU8& operator++();
+	// Seeks back to the previous UTF-8 character. Iterator must be valid.
+	StringIteratorU8& operator--();
+
+	// Returns the codepoint at the current position. The iterator must be dereferencable.
+	inline Character operator*() const { return StringUtilities::ToCharacter(p); }
+
+	// Returns false when the iterator is located just outside the valid part of the string.
+	inline operator bool() const { return (p != view.begin() - 1) && (p != view.end()); }
+
+	bool operator==(const StringIteratorU8& other) const { return p == other.p; }
+	bool operator!=(const StringIteratorU8& other) const { return !(*this == other); }
+
+	// Return a pointer to the current position.
+	inline const char* Get() const { return p; }
+
+	// Return offset from the beginning of string. Note: Can return negative if decremented.
+	std::ptrdiff_t Offset() const { return p - view.begin(); }
+
+private:
+	StringView view;
+	// 'p' can be dereferenced if and only if inside [view.begin, view.end)
+	const char* p;
+
+	inline void SeekForward();
+	inline void SeekBack();
 };
 };
 
 
+
+
+
 }
 }
 }
 }
 
 

Some files were not shown because too many files changed in this diff