diff --git a/Backends/RmlUi_Backend_GLFW_GL2.cpp b/Backends/RmlUi_Backend_GLFW_GL2.cpp index 327f6786e..c1bcdc085 100644 --- a/Backends/RmlUi_Backend_GLFW_GL2.cpp +++ b/Backends/RmlUi_Backend_GLFW_GL2.cpp @@ -230,8 +230,8 @@ static void SetupCallbacks(GLFWwindow* window) glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); // Mouse input - glfwSetCursorPosCallback(window, [](GLFWwindow* /*window*/, double xpos, double ypos) { - RmlGLFW::ProcessCursorPosCallback(data->context, xpos, ypos, data->glfw_active_modifiers); + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); }); glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { diff --git a/Backends/RmlUi_Backend_GLFW_GL3.cpp b/Backends/RmlUi_Backend_GLFW_GL3.cpp index 9d6826317..e21f879c7 100644 --- a/Backends/RmlUi_Backend_GLFW_GL3.cpp +++ b/Backends/RmlUi_Backend_GLFW_GL3.cpp @@ -84,6 +84,8 @@ bool Backend::Initialize(const char* name, int width, int height, bool allow_res glfwWindowHint(GLFW_RESIZABLE, allow_resize ? GLFW_TRUE : GLFW_FALSE); glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + GLFWwindow* window = glfwCreateWindow(width, height, name, nullptr, nullptr); if (!window) return false; @@ -242,8 +244,8 @@ static void SetupCallbacks(GLFWwindow* window) glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); // Mouse input - glfwSetCursorPosCallback(window, [](GLFWwindow* /*window*/, double xpos, double ypos) { - RmlGLFW::ProcessCursorPosCallback(data->context, xpos, ypos, data->glfw_active_modifiers); + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); }); glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { diff --git a/Backends/RmlUi_Backend_GLFW_VK.cpp b/Backends/RmlUi_Backend_GLFW_VK.cpp index 5adc83ca0..9af61a700 100644 --- a/Backends/RmlUi_Backend_GLFW_VK.cpp +++ b/Backends/RmlUi_Backend_GLFW_VK.cpp @@ -270,8 +270,8 @@ static void SetupCallbacks(GLFWwindow* window) glfwSetCursorEnterCallback(window, [](GLFWwindow* /*window*/, int entered) { RmlGLFW::ProcessCursorEnterCallback(data->context, entered); }); // Mouse input - glfwSetCursorPosCallback(window, [](GLFWwindow* /*window*/, double xpos, double ypos) { - RmlGLFW::ProcessCursorPosCallback(data->context, xpos, ypos, data->glfw_active_modifiers); + glfwSetCursorPosCallback(window, [](GLFWwindow* window, double xpos, double ypos) { + RmlGLFW::ProcessCursorPosCallback(data->context, window, xpos, ypos, data->glfw_active_modifiers); }); glfwSetMouseButtonCallback(window, [](GLFWwindow* /*window*/, int button, int action, int mods) { diff --git a/Backends/RmlUi_Backend_SDL_GL3.cpp b/Backends/RmlUi_Backend_SDL_GL3.cpp index 0b615b4e9..e0fed6c1d 100644 --- a/Backends/RmlUi_Backend_SDL_GL3.cpp +++ b/Backends/RmlUi_Backend_SDL_GL3.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -321,4 +322,7 @@ void Backend::PresentFrame() data->render_interface.EndFrame(); SDL_GL_SwapWindow(data->window); + + // Optional, used to mark frames during performance profiling. + RMLUI_FrameMark; } diff --git a/Backends/RmlUi_Platform_GLFW.cpp b/Backends/RmlUi_Platform_GLFW.cpp index 5b12cbc7c..ceafc2606 100644 --- a/Backends/RmlUi_Platform_GLFW.cpp +++ b/Backends/RmlUi_Platform_GLFW.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -135,12 +136,23 @@ bool RmlGLFW::ProcessCursorEnterCallback(Rml::Context* context, int entered) return result; } -bool RmlGLFW::ProcessCursorPosCallback(Rml::Context* context, double xpos, double ypos, int mods) +bool RmlGLFW::ProcessCursorPosCallback(Rml::Context* context, GLFWwindow* window, double xpos, double ypos, int mods) { if (!context) return true; - bool result = context->ProcessMouseMove(int(xpos), int(ypos), RmlGLFW::ConvertKeyModifiers(mods)); + using Rml::Vector2i; + using Vector2d = Rml::Vector2; + + Vector2i window_size, framebuffer_size; + glfwGetWindowSize(window, &window_size.x, &window_size.y); + glfwGetFramebufferSize(window, &framebuffer_size.x, &framebuffer_size.y); + + // Convert from mouse position in GLFW screen coordinates to framebuffer coordinates (pixels) used by RmlUi. + const Vector2d mouse_pos = Vector2d(xpos, ypos) * (Vector2d(framebuffer_size) / Vector2d(window_size)); + const Vector2i mouse_pos_round = {int(Rml::Math::Round(mouse_pos.x)), int(Rml::Math::Round(mouse_pos.y))}; + + bool result = context->ProcessMouseMove(mouse_pos_round.x, mouse_pos_round.y, RmlGLFW::ConvertKeyModifiers(mods)); return result; } diff --git a/Backends/RmlUi_Platform_GLFW.h b/Backends/RmlUi_Platform_GLFW.h index fa6457938..546048348 100644 --- a/Backends/RmlUi_Platform_GLFW.h +++ b/Backends/RmlUi_Platform_GLFW.h @@ -70,7 +70,7 @@ namespace RmlGLFW { bool ProcessKeyCallback(Rml::Context* context, int key, int action, int mods); bool ProcessCharCallback(Rml::Context* context, unsigned int codepoint); bool ProcessCursorEnterCallback(Rml::Context* context, int entered); -bool ProcessCursorPosCallback(Rml::Context* context, double xpos, double ypos, int mods); +bool ProcessCursorPosCallback(Rml::Context* context, GLFWwindow* window, double xpos, double ypos, int mods); bool ProcessMouseButtonCallback(Rml::Context* context, int button, int action, int mods); bool ProcessScrollCallback(Rml::Context* context, double yoffset, int mods); void ProcessFramebufferSizeCallback(Rml::Context* context, int width, int height); diff --git a/Backends/RmlUi_Renderer_GL2.cpp b/Backends/RmlUi_Renderer_GL2.cpp index 047b55a7d..3510842da 100644 --- a/Backends/RmlUi_Renderer_GL2.cpp +++ b/Backends/RmlUi_Renderer_GL2.cpp @@ -297,8 +297,8 @@ bool RenderInterface_GL2::GenerateTexture(Rml::TextureHandle& texture_handle, co glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); texture_handle = (Rml::TextureHandle)texture_id; diff --git a/Backends/RmlUi_Renderer_GL3.cpp b/Backends/RmlUi_Renderer_GL3.cpp index cb239cd9e..b143fe3d4 100644 --- a/Backends/RmlUi_Renderer_GL3.cpp +++ b/Backends/RmlUi_Renderer_GL3.cpp @@ -731,8 +731,8 @@ bool RenderInterface_GL3::GenerateTexture(Rml::TextureHandle& texture_handle, co glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); texture_handle = (Rml::TextureHandle)texture_id; diff --git a/CMake/Modules/FindTracy.cmake b/CMake/Modules/FindTracy.cmake deleted file mode 100644 index 314752a48..000000000 --- a/CMake/Modules/FindTracy.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# - Try to find Tracy -# Once done, this will define -# -# TRACY_FOUND - system has Tracy -# TRACY_INCLUDE_DIR - the Tracy include directory - -set(TRACY_DIR "" CACHE PATH "Parent directory of Tracy library") - -find_path(TRACY_INCLUDE_DIR Tracy.hpp PATHS ${TRACY_DIR} $ENV{TRACY_DIR} PATH_SUFFIXES tracy) - -set(TRACY_FOUND "NO") -if(TRACY_INCLUDE_DIR) - message(STATUS "Found Tracy ${TRACY_INCLUDE_DIR}...") - mark_as_advanced(TRACY_DIR TRACY_INCLUDE_DIR) - set(TRACY_FOUND "YES") -elseif(Tracy_FIND_REQUIRED) - message(FATAL_ERROR "Required library Tracy not found! Install the library and try again. If the library is already installed, set the missing variables manually in cmake.") -else() - message(STATUS "Library Tracy not found...") -endif() diff --git a/CMake/ResourceTree.cmake b/CMake/ResourceTree.cmake new file mode 100644 index 000000000..827a4675e --- /dev/null +++ b/CMake/ResourceTree.cmake @@ -0,0 +1,14 @@ +function(resourceTree VAR SOURCE_PATH DESTINATION PATTERN) + file(GLOB_RECURSE _LIST CONFIGURE_DEPENDS ${SOURCE_PATH}/${PATTERN}) + foreach (RESOURCE ${_LIST}) + get_filename_component(_PARENT ${RESOURCE} DIRECTORY) + if (${_PARENT} STREQUAL ${SOURCE_PATH}) + set(_DESTINATION ${DESTINATION}) + else () + file(RELATIVE_PATH _DESTINATION ${SOURCE_PATH} ${_PARENT}) + set(_DESTINATION ${DESTINATION}/${_DESTINATION}) + endif () + set_property(SOURCE ${RESOURCE} PROPERTY MACOSX_PACKAGE_LOCATION ${_DESTINATION}) + endforeach (RESOURCE) + set(${VAR} ${_LIST} PARENT_SCOPE) +endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt index f4020f43a..069bd49c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # Build script for RmlUi =========== #=================================== -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) if(APPLE) # This has to be before most other options so CMake properly handles the @@ -254,29 +254,64 @@ else() endif() endif() -option(ENABLE_TRACY_PROFILING "Enable profiling with Tracy. Source files can be placed in Dependencies/tracy." OFF) -if( ENABLE_TRACY_PROFILING ) - find_package(Tracy REQUIRED) +function(EnableConfigurationType name enable) + if(enable) + list(APPEND CMAKE_CONFIGURATION_TYPES "${name}") + list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES) + else() + list(REMOVE_ITEM CMAKE_CONFIGURATION_TYPES "${name}") + endif() + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "List of configurations to enable" FORCE) +endfunction() - include_directories(${TRACY_INCLUDE_DIR}) +option(RMLUI_TRACY_PROFILING "Enable profiling with Tracy. Source files can be placed in Dependencies/tracy." OFF) +if( RMLUI_TRACY_PROFILING ) + option(RMLUI_TRACY_MEMORY_PROFILING "Overload global operator new/delete to track memory allocations in Tracy." ON) if( CMAKE_CONFIGURATION_TYPES ) - list(APPEND CMAKE_CONFIGURATION_TYPES Tracy) - list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES) - set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING - "Add the configurations that we need" - FORCE) - set(CMAKE_C_FLAGS_TRACY "${CMAKE_CXX_FLAGS_RELEASE} -DRMLUI_ENABLE_PROFILING") - set(CMAKE_CXX_FLAGS_TRACY "${CMAKE_CXX_FLAGS_RELEASE} -DRMLUI_ENABLE_PROFILING") - set(CMAKE_EXE_LINKER_FLAGS_TRACY "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") - set(CMAKE_SHARED_LINKER_FLAGS_TRACY "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + option(RMLUI_TRACY_CONFIGURATION "Enable a separate Tracy configuration type for multi-config generators such as Visual Studio, otherwise enable Tracy in all configurations." ON) + + if( RMLUI_TRACY_CONFIGURATION ) + EnableConfigurationType(Tracy ON) + list(APPEND CMAKE_MAP_IMPORTED_CONFIG_TRACY Release) + set(CMAKE_C_FLAGS_TRACY "${CMAKE_C_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_TRACY "${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_EXE_LINKER_FLAGS_TRACY "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") + set(CMAKE_SHARED_LINKER_FLAGS_TRACY "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") + else() + EnableConfigurationType(Tracy OFF) + endif() + endif() + + if(NOT TARGET Tracy::TracyClient) + find_package(Tracy QUIET) + endif() + + if(NOT TARGET Tracy::TracyClient) + message("Trying to add Tracy from subdirectory 'Dependencies/tracy'.") + add_subdirectory("Dependencies/tracy") + endif() + + if(NOT TARGET Tracy::TracyClient) + message(FATAL_ERROR "Tracy client not found. Either (a) make sure target Tracy::TracyClient is available from parent project," + "(b) Tracy can be found as a config package, or (c) Tracy source files are located in 'Dependencies/Tracy'.") + endif() + + if( CMAKE_CONFIGURATION_TYPES AND RMLUI_TRACY_CONFIGURATION ) message("-- Tracy profiling enabled in configuration 'Tracy'.") + set(RMLUI_TRACY_CONDITION "$") else() message("-- Tracy profiling enabled.") - list(APPEND CORE_PUBLIC_DEFS -DRMLUI_ENABLE_PROFILING) + set(RMLUI_TRACY_CONDITION "1") + endif() + + list(APPEND CORE_PUBLIC_LINK_LIBS "$<${RMLUI_TRACY_CONDITION}:Tracy::TracyClient>") + list(APPEND CORE_PUBLIC_DEFS "$<${RMLUI_TRACY_CONDITION}:RMLUI_TRACY_PROFILING>") + if(RMLUI_TRACY_MEMORY_PROFILING) + list(APPEND CORE_PRIVATE_DEFS "$<${RMLUI_TRACY_CONDITION}:RMLUI_TRACY_MEMORY_PROFILING>") endif() elseif( CMAKE_CONFIGURATION_TYPES ) - list(REMOVE_ITEM CMAKE_CONFIGURATION_TYPES Tracy) + EnableConfigurationType(Tracy OFF) endif() option(ENABLE_LOTTIE_PLUGIN "Enable plugin for Lottie animations. Requires the rlottie library." OFF) @@ -383,7 +418,7 @@ elseif( ENABLE_LOTTIE_PLUGIN ) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Dependencies/rlottie/build) find_package(rlottie CONFIG) - find_path(rlottie_INCLUDE_DIR rlottie.h HINTS ${rlottie_DIR} $ENV{rlottie_DIR} PATH_SUFFIXES inc rlottie/inc ) + find_path(rlottie_INCLUDE_DIR rlottie.h HINTS ${rlottie_DIR} $ENV{rlottie_DIR} PATH_SUFFIXES inc rlottie/inc ../inc) if(rlottie_FOUND AND rlottie_INCLUDE_DIR) message("-- Can Lottie plugin be added to RmlCore - yes - rlottie library found") @@ -600,12 +635,14 @@ if(NOT BUILD_FRAMEWORK) target_include_directories(RmlCore PRIVATE ${CORE_INCLUDE_DIRS}) target_include_directories(RmlCore INTERFACE $ $) target_link_libraries(RmlCore PRIVATE ${CORE_LINK_LIBS}) + target_link_libraries(RmlCore PUBLIC ${CORE_PUBLIC_LINK_LIBS}) target_link_libraries(RmlDebugger RmlCore) target_compile_definitions(RmlCore PRIVATE ${CORE_PRIVATE_DEFS}) target_compile_definitions(RmlCore PUBLIC ${CORE_PUBLIC_DEFS}) else() target_include_directories(RmlUi PRIVATE ${CORE_INCLUDE_DIRS}) target_link_libraries(RmlUi PRIVATE ${CORE_LINK_LIBS}) + target_link_libraries(RmlUi PUBLIC ${CORE_PUBLIC_LINK_LIBS}) target_compile_definitions(RmlUi PRIVATE ${CORE_PRIVATE_DEFS}) target_compile_definitions(RmlUi PUBLIC ${CORE_PUBLIC_DEFS}) endif() @@ -625,12 +662,20 @@ endif() # Build samples ==================== #=================================== +include(ResourceTree) + # Build and link the samples -macro(bl_sample NAME) +macro(bl_sample NAME SAMPLE_SUB_DIR) if (WIN32) add_executable(${NAME} WIN32 ${${NAME}_SRC_FILES} ${${NAME}_HDR_FILES} ) elseif(APPLE) - add_executable(${NAME} MACOSX_BUNDLE ${${NAME}_SRC_FILES} ${${NAME}_HDR_FILES} ) + resourceTree(ASSETS ${SAMPLES_DIR}/assets Resources/assets *) + resourceTree(DATA ${SAMPLES_DIR}/${SAMPLE_SUB_DIR}/data Resources/${SAMPLE_SUB_DIR}/data *) + resourceTree(LUA ${SAMPLES_DIR}/${SAMPLE_SUB_DIR}/lua Resources/${SAMPLE_SUB_DIR}/lua *) + + set(RESOURCE_FILES ${ASSETS} ${DATA} ${LUA}) + + add_executable(${NAME} MACOSX_BUNDLE ${${NAME}_SRC_FILES} ${${NAME}_HDR_FILES} ${RESOURCE_FILES}) # The first rpath is to the proper location where the framework/library SHOULD be, the second is to the location actually seen # in the build environment @@ -668,7 +713,7 @@ if(BUILD_SAMPLES OR BUILD_TESTING) ) endif() - set(SAMPLES_DIR opt/RmlUi/Samples CACHE PATH "Path to samples directory.") + set(SAMPLES_DIR ${PROJECT_SOURCE_DIR}/Samples CACHE PATH "Path to samples directory.") if(WIN32) mark_as_advanced(SAMPLES_DIR) endif() @@ -826,7 +871,7 @@ if(BUILD_SAMPLES) # Build and install the basic samples foreach(sample ${samples}) - bl_sample(${sample} ${sample_LIBRARIES}) + bl_sample(${sample} basic/${sample} ${sample_LIBRARIES} ) # The samples always set this as their current working directory install(DIRECTORY DESTINATION ${SAMPLES_DIR}/basic/${sample}) @@ -838,7 +883,7 @@ if(BUILD_SAMPLES) # Build and install the tutorials foreach(tutorial ${tutorials}) set(tutorial_fullname tutorial_${tutorial}) - bl_sample(${tutorial_fullname} ${sample_LIBRARIES}) + bl_sample(${tutorial_fullname} tutorial/${tutorial} ${sample_LIBRARIES}) # The tutorials always set this as their current working directory install(DIRECTORY DESTINATION ${SAMPLES_DIR}/tutorial/${tutorial}) @@ -848,14 +893,14 @@ if(BUILD_SAMPLES) endforeach() # Build and install invaders sample - bl_sample(invaders ${sample_LIBRARIES}) + bl_sample(invaders invaders ${sample_LIBRARIES}) install(DIRECTORY DESTINATION ${SAMPLES_DIR}/invaders) install(TARGETS invaders RUNTIME DESTINATION ${SAMPLES_DIR}/invaders BUNDLE DESTINATION ${SAMPLES_DIR}) if(BUILD_LUA_BINDINGS) - bl_sample(luainvaders RmlLua ${sample_LIBRARIES} ${LUA_BINDINGS_LINK_LIBS}) + bl_sample(luainvaders luainvaders RmlLua ${sample_LIBRARIES} ${LUA_BINDINGS_LINK_LIBS}) install(DIRECTORY DESTINATION ${SAMPLES_DIR}/luainvaders) install(TARGETS luainvaders RUNTIME DESTINATION ${SAMPLES_DIR}/luainvaders diff --git a/Include/RmlUi/Core/Profiling.h b/Include/RmlUi/Core/Profiling.h index b589ad14c..d7caff058 100644 --- a/Include/RmlUi/Core/Profiling.h +++ b/Include/RmlUi/Core/Profiling.h @@ -29,10 +29,9 @@ #ifndef RMLUI_CORE_PROFILING_H #define RMLUI_CORE_PROFILING_H -#ifdef RMLUI_ENABLE_PROFILING +#ifdef RMLUI_TRACY_PROFILING - #define TRACY_ENABLE - #include + #include #define RMLUI_ZoneNamed(varname, active) ZoneNamed(varname, active) #define RMLUI_ZoneNamedN(varname, name, active) ZoneNamedN(varname, name, active) diff --git a/Include/RmlUi/Core/Traits.h b/Include/RmlUi/Core/Traits.h index 2558a7daf..b3deefb68 100644 --- a/Include/RmlUi/Core/Traits.h +++ b/Include/RmlUi/Core/Traits.h @@ -30,6 +30,7 @@ #define RMLUI_CORE_TRAITS_H #include "../Config/Config.h" +#include "Debug.h" #include "Header.h" #include @@ -136,6 +137,13 @@ Derived rmlui_dynamic_cast(Base base_instance) return nullptr; } +template +Derived rmlui_static_cast(Base base_instance) +{ + static_assert(std::is_pointer::value && std::is_pointer::value, "rmlui_static_cast can only cast pointer types"); + return static_cast(base_instance); +} + template const char* rmlui_type_name(const T& /*var*/) { @@ -162,6 +170,14 @@ Derived rmlui_dynamic_cast(Base base_instance) return dynamic_cast(base_instance); } +template +Derived rmlui_static_cast(Base base_instance) +{ + static_assert(std::is_pointer::value && std::is_pointer::value, "rmlui_static_cast can only cast pointer types"); + RMLUI_ASSERT(dynamic_cast(base_instance)); + return static_cast(base_instance); +} + template const char* rmlui_type_name(const T& var) { diff --git a/Samples/assets/alien_small.tga b/Samples/assets/alien_small.tga new file mode 100644 index 000000000..6d7c53d72 Binary files /dev/null and b/Samples/assets/alien_small.tga differ diff --git a/Samples/assets/invader.rcss b/Samples/assets/invader.rcss index c2e237841..a31b02f9a 100644 --- a/Samples/assets/invader.rcss +++ b/Samples/assets/invader.rcss @@ -302,6 +302,7 @@ input.text, input.password padding: 11dp 10dp 0; decorator: tiled-horizontal( text-l, text-c, auto ); /* Right becomes mirrored left */ cursor: text; + text-align: left; } textarea @@ -309,6 +310,7 @@ textarea padding: 14dp 12dp 10dp; decorator: ninepatch( textarea, textarea-inner, 1.0 ); cursor: text; + text-align: left; } input.text, diff --git a/Samples/basic/demo/data/demo.rml b/Samples/basic/demo/data/demo.rml index 3c6749613..07268e741 100644 --- a/Samples/basic/demo/data/demo.rml +++ b/Samples/basic/demo/data/demo.rml @@ -199,6 +199,9 @@ p.title border: 1dp #777; font-effect: shadow( 1dp 1dp #333 ); } +.image-mode.repeat > div { + height: 120dp; +} #decorators .image-mode > div > p { margin: -2em 0 0 0; @@ -223,6 +226,9 @@ p.title .fit-cover { decorator: image( icon-invader cover ); } .fit-scale-none { decorator: image( icon-invader scale-none ); } .fit-scale-down { decorator: image( icon-invader scale-down ); } +.fit-repeat { decorator: image( /assets/alien_small.tga repeat ); } +.fit-repeat-x { decorator: image( /assets/alien_small.tga repeat-x ); } +.fit-repeat-y { decorator: image( /assets/alien_small.tga repeat-y ); } .orientation-vertical { decorator: image( icon-invader flip-vertical scale-none ); } .orientation-horizontal { decorator: image( icon-invader flip-horizontal scale-none ); } .orientation-rotate { decorator: image( icon-invader rotate-180 scale-none ); } @@ -709,6 +715,11 @@ progress {
+
+

repeat

+

repeat-x

+

repeat-y

+

Image decorator alignment modes

default

diff --git a/Samples/basic/demo/src/main.cpp b/Samples/basic/demo/src/main.cpp index 79ccf6ce7..fb6491838 100644 --- a/Samples/basic/demo/src/main.cpp +++ b/Samples/basic/demo/src/main.cpp @@ -60,7 +60,7 @@ class DemoWindow : public Rml::EventListener { document->GetElementById("title")->SetInnerRML(title); // Add sandbox default text. - if (auto source = static_cast(document->GetElementById("sandbox_rml_source"))) + if (auto source = rmlui_dynamic_cast(document->GetElementById("sandbox_rml_source"))) { auto value = source->GetValue(); value += "

Write your RML here

\n\n"; @@ -100,7 +100,7 @@ class DemoWindow : public Rml::EventListener { } // Add sandbox style sheet text. - if (auto source = static_cast(document->GetElementById("sandbox_rcss_source"))) + if (auto source = rmlui_dynamic_cast(document->GetElementById("sandbox_rcss_source"))) { Rml::String value = "/* Write your RCSS here */\n\n/* body { color: #fea; background: #224; }\nimg { image-color: red; } */"; source->SetValue(value); @@ -332,7 +332,7 @@ class DemoEventListener : public Rml::EventListener { } else if (value == "tween_duration") { - float value = (float)std::atof(static_cast(element)->GetValue().c_str()); + float value = (float)std::atof(rmlui_static_cast(element)->GetValue().c_str()); tweening_parameters.duration = value; if (auto el_duration = element->GetElementById("duration")) el_duration->SetInnerRML(CreateString(20, "%2.2f", value)); @@ -381,7 +381,7 @@ class DemoEventListener : public Rml::EventListener { } else if (value == "set_sandbox_body") { - if (auto source = static_cast(element->GetElementById("sandbox_rml_source"))) + if (auto source = rmlui_dynamic_cast(element->GetElementById("sandbox_rml_source"))) { auto value = source->GetValue(); demo_window->SetSandboxBody(value); @@ -389,7 +389,7 @@ class DemoEventListener : public Rml::EventListener { } else if (value == "set_sandbox_style") { - if (auto source = static_cast(element->GetElementById("sandbox_rcss_source"))) + if (auto source = rmlui_dynamic_cast(element->GetElementById("sandbox_rcss_source"))) { auto value = source->GetValue(); demo_window->SetSandboxStylesheet(value); diff --git a/Samples/basic/transform/src/main.cpp b/Samples/basic/transform/src/main.cpp index b0730cb1a..e7eb7d7ca 100644 --- a/Samples/basic/transform/src/main.cpp +++ b/Samples/basic/transform/src/main.cpp @@ -63,7 +63,7 @@ class DemoWindow : public Rml::EventListener { { std::stringstream s; s << "perspective(" << perspective << "dp) "; - document->SetProperty("transform", s.str().c_str()); + document->SetProperty("transform", s.str()); } } @@ -75,7 +75,7 @@ class DemoWindow : public Rml::EventListener { if (perspective > 0) s << "perspective(" << perspective << "dp) "; s << "rotate3d(0.0, 1.0, 0.0, " << degrees << "deg)"; - document->SetProperty("transform", s.str().c_str()); + document->SetProperty("transform", s.str()); } } diff --git a/Samples/shell/src/PlatformExtensions.cpp b/Samples/shell/src/PlatformExtensions.cpp index 320ba1fc1..05599e50e 100644 --- a/Samples/shell/src/PlatformExtensions.cpp +++ b/Samples/shell/src/PlatformExtensions.cpp @@ -88,7 +88,7 @@ Rml::String PlatformExtensions::FindSamplesRoot() #elif defined RMLUI_PLATFORM_MACOSX - Rml::String path = "../../Samples/"; + Rml::String path = "../Samples/"; // Find the location of the executable. CFBundleRef bundle = CFBundleGetMainBundle(); diff --git a/Source/Core/Context.cpp b/Source/Core/Context.cpp index f86d79a73..04005883c 100644 --- a/Source/Core/Context.cpp +++ b/Source/Core/Context.cpp @@ -282,7 +282,7 @@ ElementDocument* Context::LoadDocument(Stream* stream) if (!element) return nullptr; - ElementDocument* document = static_cast(element.get()); + ElementDocument* document = rmlui_static_cast(element.get()); root->AppendChild(std::move(element)); diff --git a/Source/Core/DataModel.cpp b/Source/Core/DataModel.cpp index 273801fa1..1a6f33b92 100644 --- a/Source/Core/DataModel.cpp +++ b/Source/Core/DataModel.cpp @@ -234,6 +234,21 @@ bool DataModel::EraseAliases(Element* element) return aliases.erase(element) == 1; } +void DataModel::CopyAliases(Element* from_element, Element* to_element) +{ + if (from_element == to_element) + return; + auto existing_map = aliases.find(from_element); + + if (existing_map != aliases.end()) + { + // Need to create a copy to prevent errors during concurrent modification for 3rd party containers + auto copy = existing_map->second; + for (auto const& it : copy) + aliases[to_element][it.first] = std::move(it.second); + } +} + DataAddress DataModel::ResolveAddress(const String& address_str, Element* element) const { DataAddress address = ParseAddress(address_str); diff --git a/Source/Core/DataModel.h b/Source/Core/DataModel.h index c1a3ced4b..332f1f509 100644 --- a/Source/Core/DataModel.h +++ b/Source/Core/DataModel.h @@ -58,6 +58,7 @@ class DataModel : NonCopyMoveable { bool InsertAlias(Element* element, const String& alias_name, DataAddress replace_with_address); bool EraseAliases(Element* element); + void CopyAliases(Element* source_element, Element* target_element); DataAddress ResolveAddress(const String& address_str, Element* element) const; const DataEventFunc* GetEventCallback(const String& name); diff --git a/Source/Core/DataViewDefault.cpp b/Source/Core/DataViewDefault.cpp index d04123683..4c67271fb 100644 --- a/Source/Core/DataViewDefault.cpp +++ b/Source/Core/DataViewDefault.cpp @@ -391,18 +391,12 @@ bool DataViewText::Update(DataModel& model) { if (Element* element = GetElement()) { - RMLUI_ASSERTMSG(rmlui_dynamic_cast(element), - "Somehow the element type was changed from ElementText since construction of the view. Should not be possible?"); + String new_text = BuildText(); + String text; + if (SystemInterface* system_interface = GetSystemInterface()) + system_interface->TranslateString(text, new_text); - if (ElementText* text_element = static_cast(element)) - { - String new_text = BuildText(); - - String text; - if (SystemInterface* system_interface = GetSystemInterface()) - system_interface->TranslateString(text, new_text); - text_element->SetText(text); - } + rmlui_static_cast(element)->SetText(text); } else { diff --git a/Source/Core/DecoratorTiled.cpp b/Source/Core/DecoratorTiled.cpp index 24d67a4fd..1fdc2f5db 100644 --- a/Source/Core/DecoratorTiled.cpp +++ b/Source/Core/DecoratorTiled.cpp @@ -120,6 +120,7 @@ void DecoratorTiled::Tile::GenerateGeometry(Vector& vertices, Vector& vertices, Vector& vertices, Vectorrectangle.Position(); tile.size = sprite->rectangle.Size(); @@ -134,6 +135,14 @@ bool DecoratorTiledInstancer::GetTileProperties(DecoratorTiled::Tile* tiles, Tex const Property& fit_property = *properties.GetProperty(ids.fit); tile.fit_mode = (DecoratorTiled::TileFitMode)fit_property.value.Get(); + if (sprite && (tile.fit_mode == DecoratorTiled::TileFitMode::REPEAT || + tile.fit_mode == DecoratorTiled::TileFitMode::REPEAT_X || + tile.fit_mode == DecoratorTiled::TileFitMode::REPEAT_Y)) { + Log::Message(Log::LT_WARNING, "Decorator fit value is '%s', which is incompatible with a spritesheet", + fit_property.ToString().c_str()); + return false; + } + const Property* align_properties[2] = {properties.GetProperty(ids.align_x), properties.GetProperty(ids.align_y)}; for (int dimension = 0; dimension < 2; dimension++) diff --git a/Source/Core/Element.cpp b/Source/Core/Element.cpp index aa6e8b474..568326525 100644 --- a/Source/Core/Element.cpp +++ b/Source/Core/Element.cpp @@ -153,7 +153,7 @@ Element::~Element() void Element::Update(float dp_ratio, Vector2f vp_dimensions) { -#ifdef RMLUI_ENABLE_PROFILING +#ifdef RMLUI_TRACY_PROFILING auto name = GetAddress(false, false); RMLUI_ZoneScoped; RMLUI_ZoneText(name.c_str(), name.size()); @@ -213,7 +213,7 @@ void Element::UpdateProperties(const float dp_ratio, const Vector2f vp_dimension void Element::Render() { -#ifdef RMLUI_ENABLE_PROFILING +#ifdef RMLUI_TRACY_PROFILING auto name = GetAddress(false, false); RMLUI_ZoneScoped; RMLUI_ZoneText(name.c_str(), name.size()); diff --git a/Source/Core/ElementDocument.cpp b/Source/Core/ElementDocument.cpp index 04ccf8f6b..c207f7817 100644 --- a/Source/Core/ElementDocument.cpp +++ b/Source/Core/ElementDocument.cpp @@ -221,7 +221,7 @@ void ElementDocument::ReloadStyleSheet() return; } - SetStyleSheetContainer(static_cast(temp_doc.get())->style_sheet_container); + SetStyleSheetContainer(rmlui_static_cast(temp_doc.get())->style_sheet_container); } void ElementDocument::DirtyMediaQueries() @@ -277,11 +277,12 @@ void ElementDocument::Show(ModalFlag modal_flag, FocusFlag focus_flag) break; } - // Set to visible and switch focus if necessary + // Set to visible and switch focus if necessary. SetProperty(PropertyId::Visibility, Property(Style::Visibility::Visible)); - // We should update the document now, otherwise the focusing methods below do not think we are visible - // If this turns out to be slow, the more performant approach is just to compute the new visibility property + // Update the document now, otherwise the focusing methods below do not think we are visible. This is also important + // to ensure correct layout for any event handlers, such as for focused input fields to submit the proper caret + // position. UpdateDocument(); if (focus) diff --git a/Source/Core/ElementInstancer.cpp b/Source/Core/ElementInstancer.cpp index 30d65aa35..46fda3961 100644 --- a/Source/Core/ElementInstancer.cpp +++ b/Source/Core/ElementInstancer.cpp @@ -71,7 +71,7 @@ ElementPtr ElementInstancerText::InstanceElement(Element* /*parent*/, const Stri void ElementInstancerText::ReleaseElement(Element* element) { - pool_text_default.DestroyAndDeallocate(static_cast(element)); + pool_text_default.DestroyAndDeallocate(rmlui_static_cast(element)); } } // namespace Rml diff --git a/Source/Core/Elements/WidgetDropDown.cpp b/Source/Core/Elements/WidgetDropDown.cpp index bf2f36d7b..e13673c1d 100644 --- a/Source/Core/Elements/WidgetDropDown.cpp +++ b/Source/Core/Elements/WidgetDropDown.cpp @@ -38,6 +38,7 @@ #include "../../../Include/RmlUi/Core/Math.h" #include "../../../Include/RmlUi/Core/Profiling.h" #include "../../../Include/RmlUi/Core/Property.h" +#include "../DataModel.h" namespace Rml { @@ -129,9 +130,17 @@ void WidgetDropDown::OnUpdate() const int selection = GetSelection(); if (Element* option = selection_element->GetChild(selection)) + { option->GetInnerRML(value_rml); + if (auto model = value_element->GetDataModel()) + model->CopyAliases(option, value_element); + } else + { + if (auto model = value_element->GetDataModel()) + model->EraseAliases(value_element); value_rml = parent_element->GetValue(); + } value_element->SetInnerRML(value_rml); diff --git a/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp b/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp index fc8fa5ba0..e103fa9e8 100644 --- a/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp +++ b/Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp @@ -27,6 +27,7 @@ */ #include "FontFaceHandleDefault.h" +#include "../../../Include/RmlUi/Core/Profiling.h" #include "../../../Include/RmlUi/Core/StringUtilities.h" #include "../TextureLayout.h" #include "FontFaceLayer.h" @@ -83,6 +84,8 @@ const FontGlyphMap& FontFaceHandleDefault::GetGlyphs() const int FontFaceHandleDefault::GetStringWidth(const String& string, float letter_spacing, Character prior_character) { + RMLUI_ZoneScoped; + int width = 0; for (auto it_string = StringIteratorU8(string); it_string; ++it_string) { diff --git a/Source/Core/Layout/BlockContainer.cpp b/Source/Core/Layout/BlockContainer.cpp index bbbfce5dc..aa4a784be 100644 --- a/Source/Core/Layout/BlockContainer.cpp +++ b/Source/Core/Layout/BlockContainer.cpp @@ -362,7 +362,7 @@ float BlockContainer::GetShrinkToFitWidth() const if (computed.width().type == Style::Width::Length) { // We have a definite width, so use that size. - content_width = computed.width().value; + content_width = box.GetSize().x; } else { @@ -460,7 +460,7 @@ InlineContainer* BlockContainer::GetOpenInlineContainer() const InlineContainer* BlockContainer::GetOpenInlineContainer() const { if (!child_boxes.empty() && child_boxes.back()->GetType() == Type::InlineContainer) - return static_cast(child_boxes.back().get()); + return rmlui_static_cast(child_boxes.back().get()); return nullptr; } diff --git a/Source/Core/Layout/BlockFormattingContext.cpp b/Source/Core/Layout/BlockFormattingContext.cpp index b52b36dc9..40783976f 100644 --- a/Source/Core/Layout/BlockFormattingContext.cpp +++ b/Source/Core/Layout/BlockFormattingContext.cpp @@ -116,7 +116,7 @@ UniquePtr BlockFormattingContext::Format(ContainerBox* parent_contain { RMLUI_ASSERT(parent_container && element); -#ifdef RMLUI_ENABLE_PROFILING +#ifdef RMLUI_TRACY_PROFILING RMLUI_ZoneScopedC(0xB22222); auto name = CreateString(80, "%s %x", element->GetAddress(false, false).c_str(), element); RMLUI_ZoneName(name.c_str(), name.size()); @@ -212,7 +212,7 @@ bool BlockFormattingContext::FormatInlineBox(BlockContainer* parent_container, E bool BlockFormattingContext::FormatBlockContainerChild(BlockContainer* parent_container, Element* element) { -#ifdef RMLUI_ENABLE_PROFILING +#ifdef RMLUI_TRACY_PROFILING RMLUI_ZoneScoped; auto name = CreateString(80, ">%s %x", element->GetAddress(false, false).c_str(), element); RMLUI_ZoneName(name.c_str(), name.size()); diff --git a/Source/Core/Layout/ContainerBox.cpp b/Source/Core/Layout/ContainerBox.cpp index 34c67e6fc..7db89adfb 100644 --- a/Source/Core/Layout/ContainerBox.cpp +++ b/Source/Core/Layout/ContainerBox.cpp @@ -267,6 +267,16 @@ bool FlexContainer::Close(const Vector2f content_overflow_size, const Box& box, return true; } +float FlexContainer::GetShrinkToFitWidth() const +{ + // We don't currently support shrink-to-fit layout of flex containers. However, for the trivial case of a fixed + // width, we simply return that. + if (element->GetComputedValues().width().type == Style::Width::Type::Length) + return box.GetSize().x; + + return 0.0f; +} + String FlexContainer::DebugDumpTree(int depth) const { return String(depth * 2, ' ') + "FlexContainer" + " | " + LayoutDetails::GetDebugElementName(element); @@ -291,6 +301,16 @@ void TableWrapper::Close(const Vector2f content_overflow_size, const Box& box, f SetElementBaseline(element_baseline); } +float TableWrapper::GetShrinkToFitWidth() const +{ + // We don't currently support shrink-to-fit layout of tables. However, for the trivial case of a fixed width, we + // simply return that. + if (element->GetComputedValues().width().type == Style::Width::Type::Length) + return box.GetSize().x; + + return 0.0f; +} + String TableWrapper::DebugDumpTree(int depth) const { return String(depth * 2, ' ') + "TableWrapper" + " | " + LayoutDetails::GetDebugElementName(element); diff --git a/Source/Core/Layout/ContainerBox.h b/Source/Core/Layout/ContainerBox.h index 3fe0e9fd6..3fe3efd1e 100644 --- a/Source/Core/Layout/ContainerBox.h +++ b/Source/Core/Layout/ContainerBox.h @@ -131,6 +131,8 @@ class FlexContainer final : public ContainerBox { // @returns True if it succeeds, otherwise false if it needs to be formatted again because scrollbars were enabled. bool Close(const Vector2f content_overflow_size, const Box& box, float element_baseline); + float GetShrinkToFitWidth() const override; + const Box* GetIfBox() const override { return &box; } String DebugDumpTree(int depth) const override; @@ -152,6 +154,8 @@ class TableWrapper final : public ContainerBox { // Submits the formatted box to the table element, and propagates any uncaught overflow to this box. void Close(const Vector2f content_overflow_size, const Box& box, float element_baseline); + float GetShrinkToFitWidth() const override; + const Box* GetIfBox() const override { return &box; } String DebugDumpTree(int depth) const override; diff --git a/Source/Core/Layout/FlexFormattingContext.cpp b/Source/Core/Layout/FlexFormattingContext.cpp index 2077201ac..8187791eb 100644 --- a/Source/Core/Layout/FlexFormattingContext.cpp +++ b/Source/Core/Layout/FlexFormattingContext.cpp @@ -30,6 +30,7 @@ #include "../../../Include/RmlUi/Core/ComputedValues.h" #include "../../../Include/RmlUi/Core/Element.h" #include "../../../Include/RmlUi/Core/ElementScroll.h" +#include "../../../Include/RmlUi/Core/Profiling.h" #include "../../../Include/RmlUi/Core/Types.h" #include "ContainerBox.h" #include "LayoutDetails.h" @@ -42,6 +43,7 @@ namespace Rml { UniquePtr FlexFormattingContext::Format(ContainerBox* parent_container, Element* element, const Box* override_initial_box) { + RMLUI_ZoneScopedC(0xAFAF4F); auto flex_container_box = MakeUnique(element, parent_container); ElementScroll* element_scroll = element->GetElementScroll(); @@ -135,7 +137,7 @@ struct FlexItem { Size cross; float flex_shrink_factor; float flex_grow_factor; - Style::AlignSelf align_self; // 'Auto' is replaced by container's 'align-items' value + Style::AlignSelf align_self; // 'Auto' is replaced by container's 'align-items' value float inner_flex_base_size; // Inner size float flex_base_size; // Outer size @@ -234,9 +236,10 @@ void FlexFormattingContext::Format(Vector2f& flex_resulting_content_size, Vector const float cross_size_base_value = (cross_available_size < 0.0f ? 0.0f : cross_available_size); // -- Build a list of all flex items with base size information -- + const int num_flex_children = element_flex->GetNumChildren(); Vector items; + items.reserve(num_flex_children); - const int num_flex_children = element_flex->GetNumChildren(); for (int i = 0; i < num_flex_children; i++) { Element* element = element_flex->GetChild(i); diff --git a/Source/Core/Layout/FormattingContext.cpp b/Source/Core/Layout/FormattingContext.cpp index 990339bd6..8a41376b5 100644 --- a/Source/Core/Layout/FormattingContext.cpp +++ b/Source/Core/Layout/FormattingContext.cpp @@ -29,6 +29,7 @@ #include "FormattingContext.h" #include "../../../Include/RmlUi/Core/ComputedValues.h" #include "../../../Include/RmlUi/Core/Element.h" +#include "../../../Include/RmlUi/Core/Profiling.h" #include "BlockFormattingContext.h" #include "FlexFormattingContext.h" #include "LayoutBox.h" @@ -40,6 +41,7 @@ namespace Rml { UniquePtr FormattingContext::FormatIndependent(ContainerBox* parent_container, Element* element, const Box* override_initial_box, FormattingContextType backup_context) { + RMLUI_ZoneScopedC(0xAFAFAF); using namespace Style; if (element->IsReplaced()) diff --git a/Source/Core/Layout/InlineLevelBox.cpp b/Source/Core/Layout/InlineLevelBox.cpp index 18f3b441d..449c83101 100644 --- a/Source/Core/Layout/InlineLevelBox.cpp +++ b/Source/Core/Layout/InlineLevelBox.cpp @@ -227,8 +227,6 @@ String InlineLevelBox_Text::DebugDumpNameValue() const ElementText* InlineLevelBox_Text::GetTextElement() { - RMLUI_ASSERT(rmlui_dynamic_cast(GetElement())); - - return static_cast(GetElement()); + return rmlui_static_cast(GetElement()); } } // namespace Rml diff --git a/Source/Core/Layout/LayoutDetails.cpp b/Source/Core/Layout/LayoutDetails.cpp index b4f960720..ec3e33816 100644 --- a/Source/Core/Layout/LayoutDetails.cpp +++ b/Source/Core/Layout/LayoutDetails.cpp @@ -186,10 +186,11 @@ ContainingBlock LayoutDetails::GetContainingBlock(ContainerBox* parent_container { area = BoxArea::Padding; - auto EstablishesAbsoluteContainingBlock = [](ContainerBox* container) -> bool { + auto EstablishesAbsoluteContainingBlock = [](const ContainerBox* container) -> bool { return container->GetPositionProperty() != Position::Static || container->HasLocalTransformOrPerspective(); }; - while (container && container->GetParent() && !EstablishesAbsoluteContainingBlock(container)) + + while (!EstablishesAbsoluteContainingBlock(container) && container->GetParent()) container = container->GetParent(); } @@ -530,7 +531,7 @@ void LayoutDetails::BuildBoxHeight(Box& box, const ComputedValues& computed, flo // If the height is set to auto, we need to calculate the height. if (height_auto) { - // If the height is set to auto for a box in normal flow, the height is set to -1. + // If the height is set to auto for a box in normal flow, the height is set to -1, representing indefinite height. content_area.y = -1; // But if we are dealing with an absolutely positioned element we need to consider if the top and bottom diff --git a/Source/Core/Layout/LayoutDetails.h b/Source/Core/Layout/LayoutDetails.h index c022c8132..3adc17c37 100644 --- a/Source/Core/Layout/LayoutDetails.h +++ b/Source/Core/Layout/LayoutDetails.h @@ -89,8 +89,8 @@ class LayoutDetails { /// Returns the containing block for a box. /// @param[in] parent_container The parent container of the current box. - /// @param[in] child_position The position property of the current box. - /// @return The containing block box and size, possibly indefinite along one or both axes. + /// @param[in] position The position property of the current box. + /// @return The containing block box and size, possibly indefinite (represented by negative size) along one or both axes. static ContainingBlock GetContainingBlock(ContainerBox* parent_container, Style::Position position); /// Builds margins of a Box, and resolves any auto width or height for non-inline elements. The height may be left unresolved if it depends on the diff --git a/Source/Core/Layout/LineBox.cpp b/Source/Core/Layout/LineBox.cpp index ca28ce416..acc21d59b 100644 --- a/Source/Core/Layout/LineBox.cpp +++ b/Source/Core/Layout/LineBox.cpp @@ -92,7 +92,7 @@ bool LineBox::AddBox(InlineLevelBox* box, InlineLayoutMode layout_mode, LayoutOv case FragmentType::InlineBox: { RMLUI_ASSERT(constructor.layout_width < 0.f); - RMLUI_ASSERT(rmlui_dynamic_cast(box)); + RMLUI_ASSERT(rmlui_static_cast(box)); open_fragments_leaf = fragment_index; open_spacing_left += box->GetSpacingLeft(); @@ -389,7 +389,7 @@ InlineBox* LineBox::GetOpenInlineBox() if (open_fragments_leaf == RootFragmentIndex) return nullptr; - return static_cast(fragments[open_fragments_leaf].box); + return rmlui_static_cast(fragments[open_fragments_leaf].box); } bool LineBox::CanCollapseLine() const diff --git a/Source/Core/Profiling.cpp b/Source/Core/Profiling.cpp index ed4933f7a..741cb080a 100644 --- a/Source/Core/Profiling.cpp +++ b/Source/Core/Profiling.cpp @@ -28,8 +28,7 @@ #include "../../Include/RmlUi/Core/Profiling.h" -#ifdef RMLUI_ENABLE_PROFILING - #include +#ifdef RMLUI_TRACY_MEMORY_PROFILING #include void* operator new(std::size_t n) diff --git a/Source/Core/PropertyParserTransform.cpp b/Source/Core/PropertyParserTransform.cpp index 97e250020..4dfe04130 100644 --- a/Source/Core/PropertyParserTransform.cpp +++ b/Source/Core/PropertyParserTransform.cpp @@ -34,7 +34,9 @@ namespace Rml { -PropertyParserTransform::PropertyParserTransform() : number(Unit::NUMBER), length(Unit::LENGTH_PERCENT, Unit::PX), angle(Unit::ANGLE, Unit::RAD) {} +PropertyParserTransform::PropertyParserTransform() : + number(Unit::NUMBER), length(Unit::LENGTH, Unit::PX), length_pct(Unit::LENGTH_PERCENT, Unit::PX), angle(Unit::ANGLE, Unit::RAD) +{} PropertyParserTransform::~PropertyParserTransform() {} @@ -53,18 +55,21 @@ bool PropertyParserTransform::ParseValue(Property& property, const String& value NumericValue args[16]; - const PropertyParser* angle1[] = {&angle}; - const PropertyParser* angle2[] = {&angle, &angle}; - const PropertyParser* length1[] = {&length}; - const PropertyParser* length2[] = {&length, &length}; - const PropertyParser* length3[] = {&length, &length, &length}; - const PropertyParser* number3angle1[] = {&number, &number, &number, &angle}; - const PropertyParser* number1[] = {&number}; - const PropertyParser* number2[] = {&number, &number}; - const PropertyParser* number3[] = {&number, &number, &number}; - const PropertyParser* number6[] = {&number, &number, &number, &number, &number, &number}; const PropertyParser* number16[] = {&number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number, &number}; + const PropertyParser* lengthpct2_length1[] = {&length_pct, &length_pct, &length}; + const PropertyParser* number3angle1[] = {&number, &number, &number, &angle}; + const PropertyParser* angle2[] = {&angle, &angle}; + const PropertyParser* length1[] = {&length}; + + // For semantic purposes, define subsets of the above parsers when scanning primitives below. + auto lengthpct1 = lengthpct2_length1; + auto lengthpct2 = lengthpct2_length1; + auto angle1 = angle2; + auto number1 = number16; + auto number2 = number16; + auto number3 = number16; + auto number6 = number16; while (*next) { @@ -83,11 +88,11 @@ bool PropertyParserTransform::ParseValue(Property& property, const String& value { transform->AddPrimitive({Matrix3D(args)}); } - else if (Scan(bytes_read, next, "translateX", length1, args, 1)) + else if (Scan(bytes_read, next, "translateX", lengthpct1, args, 1)) { transform->AddPrimitive({TranslateX(args)}); } - else if (Scan(bytes_read, next, "translateY", length1, args, 1)) + else if (Scan(bytes_read, next, "translateY", lengthpct1, args, 1)) { transform->AddPrimitive({TranslateY(args)}); } @@ -95,11 +100,11 @@ bool PropertyParserTransform::ParseValue(Property& property, const String& value { transform->AddPrimitive({TranslateZ(args)}); } - else if (Scan(bytes_read, next, "translate", length2, args, 2)) + else if (Scan(bytes_read, next, "translate", lengthpct2, args, 2)) { transform->AddPrimitive({Translate2D(args)}); } - else if (Scan(bytes_read, next, "translate3d", length3, args, 3)) + else if (Scan(bytes_read, next, "translate3d", lengthpct2_length1, args, 3)) { transform->AddPrimitive({Translate3D(args)}); } @@ -183,20 +188,6 @@ bool PropertyParserTransform::Scan(int& out_bytes_read, const char* str, const c out_bytes_read = 0; int total_bytes_read = 0, bytes_read = 0; - /* use the quicker stack-based argument buffer, if possible */ - char* arg = nullptr; - char arg_stack[1024]; - String arg_heap; - if (strlen(str) < sizeof(arg_stack)) - { - arg = arg_stack; - } - else - { - arg_heap = str; - arg = &arg_heap[0]; - } - /* skip leading white space */ bytes_read = 0; sscanf(str, " %n", &bytes_read); @@ -233,6 +224,20 @@ bool PropertyParserTransform::Scan(int& out_bytes_read, const char* str, const c return false; } + /* use the quicker stack-based argument buffer, if possible */ + char* arg = nullptr; + char arg_stack[1024]; + String arg_heap; + if (strlen(str) < sizeof(arg_stack)) + { + arg = arg_stack; + } + else + { + arg_heap = str; + arg = &arg_heap[0]; + } + /* parse the arguments */ for (int i = 0; i < nargs; ++i) { diff --git a/Source/Core/PropertyParserTransform.h b/Source/Core/PropertyParserTransform.h index bd29b82e3..2d13762bc 100644 --- a/Source/Core/PropertyParserTransform.h +++ b/Source/Core/PropertyParserTransform.h @@ -62,7 +62,7 @@ class PropertyParserTransform : public PropertyParser { /// @return True if parsed successfully, false otherwise. bool Scan(int& out_bytes_read, const char* str, const char* keyword, const PropertyParser** parsers, NumericValue* args, int nargs) const; - PropertyParserNumber number, length, angle; + PropertyParserNumber number, length, length_pct, angle; }; } // namespace Rml diff --git a/Source/Core/TransformUtilities.cpp b/Source/Core/TransformUtilities.cpp index f4c524134..da41c8ff5 100644 --- a/Source/Core/TransformUtilities.cpp +++ b/Source/Core/TransformUtilities.cpp @@ -85,13 +85,12 @@ static inline float ResolveHeight(NumericValue value, Element& e) noexcept return e.ResolveNumericValue(value, e.GetBox().GetSize(BoxArea::Border).y); } -// Resolve a numeric property value with the element's depth as relative base value. -static inline float ResolveDepth(NumericValue value, Element& e) noexcept +// Resolve a length numeric property value for the given element. +static inline float ResolveLength(NumericValue value, Element& e) noexcept { if (value.unit == Unit::PX || value.unit == Unit::NUMBER) return value.number; - Vector2f size = e.GetBox().GetSize(BoxArea::Border); - return e.ResolveNumericValue(value, Math::Max(size.x, size.y)); + return e.ResolveLength(value); } static inline String ToString(NumericValue value) noexcept @@ -192,11 +191,11 @@ struct ResolveTransformVisitor { void operator()(const Transforms::TranslateX& p) { m = Matrix4f::TranslateX(ResolveWidth(p.values[0], e)); } void operator()(const Transforms::TranslateY& p) { m = Matrix4f::TranslateY(ResolveHeight(p.values[0], e)); } - void operator()(const Transforms::TranslateZ& p) { m = Matrix4f::TranslateZ(ResolveDepth(p.values[0], e)); } + void operator()(const Transforms::TranslateZ& p) { m = Matrix4f::TranslateZ(ResolveLength(p.values[0], e)); } void operator()(const Transforms::Translate2D& p) { m = Matrix4f::Translate(ResolveWidth(p.values[0], e), ResolveHeight(p.values[1], e), 0); } void operator()(const Transforms::Translate3D& p) { - m = Matrix4f::Translate(ResolveWidth(p.values[0], e), ResolveHeight(p.values[1], e), ResolveDepth(p.values[2], e)); + m = Matrix4f::Translate(ResolveWidth(p.values[0], e), ResolveHeight(p.values[1], e), ResolveLength(p.values[2], e)); } void operator()(const Transforms::ScaleX& p) { m = Matrix4f::ScaleX(p.values[0]); } @@ -216,7 +215,7 @@ struct ResolveTransformVisitor { void operator()(const Transforms::Skew2D& p) { m = Matrix4f::Skew(p.values[0], p.values[1]); } void operator()(const Transforms::DecomposedMatrix4& p) { m = Matrix4f::Compose(p.translation, p.scale, p.skew, p.perspective, p.quaternion); } - void operator()(const Transforms::Perspective& p) { m = Matrix4f::Perspective(ResolveDepth(p.values[0], e)); } + void operator()(const Transforms::Perspective& p) { m = Matrix4f::Perspective(ResolveLength(p.values[0], e)); } void run(const TransformPrimitive& primitive) { @@ -271,7 +270,7 @@ struct PrepareVisitor { } bool operator()(TranslateZ& p) { - p.values[0] = NumericValue{ResolveDepth(p.values[0], e), Unit::PX}; + p.values[0] = NumericValue{ResolveLength(p.values[0], e), Unit::PX}; return true; } bool operator()(Translate2D& p) @@ -284,7 +283,7 @@ struct PrepareVisitor { { p.values[0] = NumericValue{ResolveWidth(p.values[0], e), Unit::PX}; p.values[1] = NumericValue{ResolveHeight(p.values[1], e), Unit::PX}; - p.values[2] = NumericValue{ResolveDepth(p.values[2], e), Unit::PX}; + p.values[2] = NumericValue{ResolveLength(p.values[2], e), Unit::PX}; return true; } template diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index bc17d4aaa..ab076435a 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -23,6 +23,13 @@ include_dependency("nanobench") include_dependency("lodepng") include_dependency("trompeloeil") +if(MSVC) + target_compile_definitions(doctest::doctest INTERFACE DOCTEST_CONFIG_USE_STD_HEADERS) +endif() +if(DISABLE_RTTI_AND_EXCEPTIONS) + target_compile_definitions(doctest::doctest INTERFACE DOCTEST_CONFIG_NO_EXCEPTIONS_BUT_WITH_ALL_ASSERTS) +endif() + #=================================== # Common source files ============== #=================================== @@ -41,10 +48,6 @@ add_executable(UnitTests ${UnitTests_HDR_FILES} ${UnitTests_SRC_FILES}) target_link_libraries(UnitTests RmlCore RmlDebugger doctest::doctest trompeloeil::trompeloeil ${sample_LIBRARIES}) add_common_target_options(UnitTests) -if(MSVC) - target_compile_definitions(UnitTests PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) -endif() - doctest_discover_tests(UnitTests) @@ -75,10 +78,6 @@ if(VISUAL_TESTS_CAPTURE_DIRECTORY) target_compile_definitions(VisualTests PRIVATE RMLUI_VISUAL_TESTS_CAPTURE_DIRECTORY="${VISUAL_TESTS_CAPTURE_DIRECTORY}") endif() -if(MSVC) - target_compile_definitions(VisualTests PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) -endif() - #=================================== @@ -92,10 +91,6 @@ add_executable(Benchmarks ${Benchmarks_HDR_FILES} ${Benchmarks_SRC_FILES} ${Test target_link_libraries(Benchmarks RmlCore RmlDebugger doctest::doctest nanobench::nanobench ${sample_LIBRARIES}) add_common_target_options(Benchmarks) -if(MSVC) - target_compile_definitions(Benchmarks PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) -endif() - #=================================== diff --git a/Tests/Data/VisualTests/shrink_to_fit_04.rml b/Tests/Data/VisualTests/shrink_to_fit_04.rml new file mode 100644 index 000000000..a19c9cee8 --- /dev/null +++ b/Tests/Data/VisualTests/shrink_to_fit_04.rml @@ -0,0 +1,67 @@ + + + Shrink-to-fit 4 + + + + + + + +

The following boxes should all appear the same, with a border wrapped all the way around the background and no red visible.

+ +
+
+
Hello
+
+
+
+
+
+
Hello
+
+
+
+
+
+
Hello
+
+
+
+
+
+
Hello
+
+
+
+
+
+
Hello
+
+
+ +
diff --git a/Tests/Data/style.rcss b/Tests/Data/style.rcss index 5d88aeb11..d6c6c9eb3 100644 --- a/Tests/Data/style.rcss +++ b/Tests/Data/style.rcss @@ -147,4 +147,8 @@ input.text { tab-index: auto; cursor: text; box-sizing: border-box; + text-align: left; +} +input.text, textarea { + text-align: left; } diff --git a/Tests/Source/Benchmarks/Flexbox.cpp b/Tests/Source/Benchmarks/Flexbox.cpp index efd607e10..08a0d0cba 100644 --- a/Tests/Source/Benchmarks/Flexbox.cpp +++ b/Tests/Source/Benchmarks/Flexbox.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -491,3 +492,82 @@ TEST_CASE("flexbox") document->Close(); } } + +static const String rml_flexbox_chatbox = R"( + + + Chat + + + + + +
+ + +)"; + +TEST_CASE("flexbox.chat") +{ + Context* context = TestsShell::GetContext(); + REQUIRE(context); + + nanobench::Bench bench; + bench.title("Flexbox chat"); + bench.relative(true); + // bench.epochs(100); + + auto MakeFlexItemsRml = [](int number_items, const String& item_text) { + String rml; + for (int i = 0; i < number_items; i++) + rml += "
" + item_text + "
"; + return rml; + }; + + const String short_words = + MakeFlexItemsRml(10, "aaaaaaaaaaaaaaa aaaaaaaaaaaaaa aaaaaaaaa aaaaaaaaaaaa aaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaa"); + const String long_words = + MakeFlexItemsRml(10, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + // The flex items will essentially be formatted four times each: + // - Two times during flex formatting, first to get their height, then to do their actual formatting. + // - Then flex formatting is itself done twice, since the body adds a scrollbar, thereby modifying the available width. + // The long words take longer to format, since we do a naive approach to breaking up words in ElementText::GenerateLine, making us calculate + // string widths repeatedly. Removing the 'word-break' property should make the long word-case much faster. + ElementDocument* document = context->LoadDocumentFromMemory(rml_flexbox_chatbox); + Element* chat = document->GetElementById("chat"); + chat->SetInnerRML(short_words + long_words); + document->Show(); + TestsShell::RenderLoop(); + + bench.run("Short words", [&] { + chat->SetInnerRML(short_words); + context->Update(); + context->Render(); + RMLUI_FrameMark; + }); + bench.run("Long words", [&] { + chat->SetInnerRML(long_words); + context->Update(); + context->Render(); + RMLUI_FrameMark; + }); + + document->Close(); +} diff --git a/Tests/Source/UnitTests/ElementFormControlSelect.cpp b/Tests/Source/UnitTests/ElementFormControlSelect.cpp index 883ff9bc8..27c211893 100644 --- a/Tests/Source/UnitTests/ElementFormControlSelect.cpp +++ b/Tests/Source/UnitTests/ElementFormControlSelect.cpp @@ -296,6 +296,14 @@ TEST_CASE("form.select.databinding") )", "2", "C"}, + + { + R"( + + )", + "2", "

C

"}, }; DataModelConstructor constructor = context->CreateDataModel("select-test"); diff --git a/readme.md b/readme.md index c3ea15b5c..476ed37d6 100644 --- a/readme.md +++ b/readme.md @@ -105,7 +105,7 @@ Make sure to replace the path to vcpkg. When this completes, feel free to test t #### Conan -RmlUi is readily available from [ConanCenter](https://conan.io/center/rmlui). +RmlUi is readily available from [ConanCenter](https://conan.io/center/recipes/rmlui). ## Integrating RmlUi @@ -125,7 +125,7 @@ Several [samples](Samples/) demonstrate everything from basic integration to mor To ease the integration of RmlUi, the library includes [many backends](Backends/) adding support for common renderers and platforms. The following terms are used here: -- ***Renderer***: Implements the [render interface](https://mikke89.github.io/RmlUiDoc/pages/cpp_manual/interfaces/render) for a given rendering API, and provides initialization code when necessary. +- ***Renderer***: Implements the [render interface](https://mikke89.github.io/RmlUiDoc/pages/cpp_manual/interfaces/render.html) for a given rendering API, and provides initialization code when necessary. - ***Platform***: Implements the [system interface](https://mikke89.github.io/RmlUiDoc/pages/cpp_manual/interfaces/system.html) for a given platform (operating system or window library), and adds procedures for providing input to RmlUi contexts. - ***Backend***: Combines a renderer and a platform for a complete windowing framework sample, implementing the basic [Backend interface](Backends/RmlUi_Backend.h). @@ -366,6 +366,9 @@ Users can now edit the text field to change the animal. The data bindings ensure **Installer software by [@xland](https://github.com/xland)**\ ![xland installer collage](https://user-images.githubusercontent.com/5490330/230487763-ec4d28e7-7ec6-44af-89f2-d2cbad8f44c1.png) +**[TruckersMP](https://truckersmp.com/) - a multiplayer mod for truck simulators - chat box in RmlUi**\ +![TruckersMP](https://raw.githubusercontent.com/mikke89/RmlUiDoc/8ce505124daec1a9fdff0327be495fc2e43a37cf/assets/gallery/truckers_mp.webp) + **Form controls from the 'demo' sample**\ ![Form controls](https://github.com/mikke89/RmlUiDoc/blob/3f319d8464e73b821179ff8d20537013af5b9810/assets/gallery/forms.png)