From f6bce5d044fa62c3acf3c1dd54c9c720809ac3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 11 Jul 2026 00:10:56 -0300 Subject: [PATCH] Optimize hot UI accessors and CSS length parsing: Inline frequently used scene and UI state accessors to reduce function call overhead during layout, rendering, and rich-text traversal. Add string-view numeric conversion support and parse CSS length values without temporary number or unit strings. Preserve existing string overloads for compatibility and keep CSS function parsing unchanged. Add focused parsing coverage and benchmarks. Full-suite profiling showed substantial reductions in layout invalidation, whitespace traversal, and CSS length parsing costs. --- ...l_gui_html_optimization_plan_2026_07_09.md | 61 ++++++++ include/eepp/core/string.hpp | 13 ++ include/eepp/scene/node.hpp | 64 ++++---- include/eepp/ui/uihtmlwidget.hpp | 18 ++- include/eepp/ui/uilayout.hpp | 6 +- include/eepp/ui/uinode.hpp | 6 +- include/eepp/ui/uirichtext.hpp | 6 +- include/eepp/ui/uiwidget.hpp | 6 +- src/benchmarks/inline_layout_benchmark.cpp | 23 +++ src/eepp/core/string.cpp | 80 +++++++--- src/eepp/scene/node.cpp | 116 -------------- src/eepp/ui/css/stylesheetlength.cpp | 146 +++++++++--------- src/eepp/ui/uihtmltable.cpp | 6 - src/eepp/ui/uihtmlwidget.cpp | 20 --- src/eepp/ui/uilayout.cpp | 8 - src/eepp/ui/uilinearlayout.cpp | 10 +- src/eepp/ui/uinode.cpp | 8 - src/eepp/ui/uirichtext.cpp | 8 - src/eepp/ui/uiwidget.cpp | 8 - .../unit_tests/stringsoperations_tests.cpp | 25 ++- .../unit_tests/uicss_inheritance_tests.cpp | 27 ++++ 21 files changed, 344 insertions(+), 321 deletions(-) diff --git a/.agent/plans/eepp_superluminal_gui_html_optimization_plan_2026_07_09.md b/.agent/plans/eepp_superluminal_gui_html_optimization_plan_2026_07_09.md index 22f5f205d..acdc41121 100644 --- a/.agent/plans/eepp_superluminal_gui_html_optimization_plan_2026_07_09.md +++ b/.agent/plans/eepp_superluminal_gui_html_optimization_plan_2026_07_09.md @@ -10,6 +10,21 @@ Session analyzed: /home/downloads/2026-07-09_19-52-19_eepp-unit_tests.slp ``` +Current baseline capture after CSS selector phases 1-8: + +```text +/tmp/eepp-unit-tests-current-final-2026-07-10.linux +``` + +Accessor-inlining comparison capture: + +```text +/tmp/eepp-unit-tests-inline-accessors-2026-07-10.linux +``` + +Both current captures ran all 737 release tests at 500 Hz. The post-change run passed 736 tests +with one skipped test. + MCP could not open the packaged `.slp` directly, so it was extracted to `/tmp/superluminal-eepp-unit-tests` and the contained `.session` was queried. ## Capture Notes @@ -108,6 +123,33 @@ Validation: ### 3. Fast-parse common CSS lengths without allocations +**Status: Implemented and measured** + +`StyleSheetLength::fromString()` now trims once, scans scalar numeric prefixes directly from a +`std::string_view`, and converts the numeric subview through `String::fromString()`. The core string +API exposes `std::string_view` numeric overloads while retaining exact `std::string` forwarding +overloads for source and ABI compatibility with the implicitly constructible `EE::String` type. +The CSS parser no longer builds separate number/unit strings or retries parsing by removing +characters. Function expressions retain the existing parser, and position keywords map directly +to percentage lengths without recursive string construction. + +Validation includes signed values, leading-dot decimals, scientific notation, surrounding +whitespace, position keywords, unitless and unknown units, `pxAsDp`, all existing CSS function +tests, and a dedicated release benchmark. + +- Final focused benchmark median for 320,000 mixed values: 13.67 ms to 10.10 ms, a 26.1% + reduction. +- Before numeric conversion was centralized in `String`, the full-suite capture measured 105.4 ms + inclusive / 44.6 ms exclusive to 46.5 ms / 12.4 ms. A future full capture should refresh those + aggregate numbers for the final implementation. +- Post-change full release suite: 737 passed, one skipped. + +Comparison capture: + +```text +/tmp/eepp-unit-tests-length-fast-path-2026-07-10.linux +``` + Files: ```text @@ -305,6 +347,25 @@ Validation: ### 10. Broaden hot accessor/type-check inlining beyond selector code +**Status: Core hierarchy implemented and measured** + +The trivial `getType()` / `isType()` implementations for `Node`, `UINode`, `UIWidget`, +`UILayout`, `UIHTMLWidget`, and `UIRichText` are now inline, along with `Node::isLayout()`. + +In the comparable full-suite captures: + +- `UISceneNode::invalidateLayout()` decreased from 86.1 ms inclusive / 41.5 ms exclusive to + 26.3 ms total. +- Base type accessor symbols largely disappeared from the hot function list. +- `getEffectiveWhiteSpaceCollapse()` decreased from 344.0 ms to 162.9 ms inclusive, partly + because its repeated type checks became cheaper. +- Unit-test process CPU time decreased from 20.12 s to 19.88 s, approximately 1.2%. Treat this + whole-process result as directional because the full suite contains rendering and image-diff + noise. + +Further subclass inlining remains possible, especially table widgets, but the next independent +high-value target is CSS length parsing. + Files: ```text diff --git a/include/eepp/core/string.hpp b/include/eepp/core/string.hpp index 42b93bd04..b286a0402 100644 --- a/include/eepp/core/string.hpp +++ b/include/eepp/core/string.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -539,6 +540,18 @@ class EE_API String { static bool fromString( float& t, const std::string& s ); static bool fromString( double& t, const std::string& s ); + /** Converts from a string view to type */ + static bool fromString( Int8& t, std::string_view s, int base = 10 ); + static bool fromString( Int16& t, std::string_view s, int base = 10 ); + static bool fromString( Int32& t, std::string_view s, int base = 10 ); + static bool fromString( Int64& t, std::string_view s, int base = 10 ); + static bool fromString( Uint8& t, std::string_view s, int base = 10 ); + static bool fromString( Uint16& t, std::string_view s, int base = 10 ); + static bool fromString( Uint32& t, std::string_view s, int base = 10 ); + static bool fromString( Uint64& t, std::string_view s, int base = 10 ); + static bool fromString( float& t, std::string_view s ); + static bool fromString( double& t, std::string_view s ); + /** Converts from a String to type */ static bool fromString( Int8& t, const String& s, int base = 10 ); static bool fromString( Int16& t, const String& s, int base = 10 ); diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index 56ac4a219..03488f2fb 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -202,7 +202,7 @@ class EE_API Node : public Transformable { * * @return The node type as a Uint32. */ - virtual Uint32 getType() const; + virtual Uint32 getType() const { return 0; } /** * @brief Checks if the node is of a specific type. @@ -212,7 +212,7 @@ class EE_API Node : public Transformable { * @param type The type identifier to check. * @return True if the node is of the specified type, false otherwise. */ - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { return Node::getType() == type; } /** @return True if this node is a UITextNode, false otherwise. */ inline bool isTextNode() const { return 0 != ( mNodeFlags & NODE_FLAG_TEXTNODE ); } @@ -279,7 +279,7 @@ class EE_API Node : public Transformable { * * @return The size as a Sizef. */ - virtual const Sizef& getSize() const; + virtual const Sizef& getSize() const { return mSize; } /** * @brief Gets the node size in actual screen pixels. @@ -289,7 +289,7 @@ class EE_API Node : public Transformable { * * @return The pixel size as a Sizef. */ - const Sizef& getPixelsSize() const; + inline const Sizef& getPixelsSize() const { return mSize; } /** * @brief Sets the visibility of the node. @@ -322,7 +322,7 @@ class EE_API Node : public Transformable { * * @return True if the node is visible, false otherwise. */ - bool isVisible() const; + inline bool isVisible() const { return mVisible; } /** * @brief Checks if the node and all its parents are visible. @@ -350,7 +350,7 @@ class EE_API Node : public Transformable { * * @return True if the node is enabled, false if disabled. */ - bool isEnabled() const; + inline bool isEnabled() const { return mEnabled; } /** * @brief Checks if the node is disabled. @@ -359,7 +359,7 @@ class EE_API Node : public Transformable { * * @return True if the node is disabled, false otherwise. */ - bool isDisabled() const; + inline bool isDisabled() const { return !mEnabled; } /** * @brief Gets the parent node. @@ -468,7 +468,7 @@ class EE_API Node : public Transformable { * * @return The stored user data pointer. */ - const UintPtr& getData() const; + inline const UintPtr& getData() const { return mData; } /** * @brief Sets the blend mode for this node. @@ -544,7 +544,7 @@ class EE_API Node : public Transformable { * * @return True if this node is a SceneNode, false otherwise. */ - bool isSceneNode() const; + inline bool isSceneNode() const { return 0 != ( mNodeFlags & NODE_FLAG_SCENENODE ); } /** * @brief Checks if this node is a UISceneNode. @@ -553,7 +553,7 @@ class EE_API Node : public Transformable { * * @return True if this node is a UISceneNode, false otherwise. */ - bool isUISceneNode() const; + inline bool isUISceneNode() const { return 0 != ( mNodeFlags & NODE_FLAG_UISCENENODE ); } /** * @brief Checks if this node is a UINode. @@ -562,7 +562,7 @@ class EE_API Node : public Transformable { * * @return True if this node is a UINode, false otherwise. */ - bool isUINode() const; + inline bool isUINode() const { return 0 != ( mNodeFlags & NODE_FLAG_UINODE ); } /** * @brief Checks if this node is a UIWidget. @@ -580,7 +580,7 @@ class EE_API Node : public Transformable { * * @return True if this node is a Window, false otherwise. */ - bool isWindow() const; + inline bool isWindow() const { return 0 != ( mNodeFlags & NODE_FLAG_WINDOW ); } /** * @brief Checks if this node is a Layout. @@ -590,7 +590,7 @@ class EE_API Node : public Transformable { * * @return True if this node is a Layout, false otherwise. */ - bool isLayout() const; + inline bool isLayout() const { return 0 != ( mNodeFlags & NODE_FLAG_LAYOUT ); } /** * @brief Checks if clipping is enabled for this node. @@ -599,21 +599,21 @@ class EE_API Node : public Transformable { * * @return True if clipping is enabled, false otherwise. */ - bool isClipped() const; + inline bool isClipped() const { return 0 != ( mNodeFlags & NODE_FLAG_CLIP_ENABLE ); } /** * @brief Checks if this node has rotation applied. * * @return True if the node's rotation is non-zero, false otherwise. */ - bool isRotated() const; + inline bool isRotated() const { return 0 != ( mNodeFlags & NODE_FLAG_ROTATED ); } /** * @brief Checks if this node has scaling applied. * * @return True if the node's scale is not (1,1), false otherwise. */ - bool isScaled() const; + inline bool isScaled() const { return 0 != ( mNodeFlags & NODE_FLAG_SCALED ); } /** * @brief Checks if this node uses a frame buffer. @@ -622,21 +622,23 @@ class EE_API Node : public Transformable { * * @return True if using frame buffer rendering, false otherwise. */ - bool isFrameBuffer() const; + inline bool isFrameBuffer() const { return 0 != ( mNodeFlags & NODE_FLAG_FRAME_BUFFER ); } /** * @brief Checks if the mouse is currently over this node. * * @return True if the mouse cursor is over this node, false otherwise. */ - bool isMouseOver() const; + inline bool isMouseOver() const { return 0 != ( mNodeFlags & NODE_FLAG_MOUSEOVER ); } /** * @brief Checks if the mouse is over this node or any of its children. * * @return True if the mouse is over this node or any descendant, false otherwise. */ - bool isMouseOverMeOrChildren() const; + inline bool isMouseOverMeOrChildren() const { + return 0 != ( mNodeFlags & NODE_FLAG_MOUSEOVER_ME_OR_CHILD ); + } /** * @brief Checks if this node and all its parents are visible in the tree. @@ -756,14 +758,14 @@ class EE_API Node : public Transformable { * * @return Pointer to the first child or nullptr if no children exist. */ - Node* getFirstChild() const; + inline Node* getFirstChild() const { return mChild; } /** * @brief Gets the last child node. * * @return Pointer to the last child or nullptr if no children exist. */ - Node* getLastChild() const; + inline Node* getLastChild() const { return mChildLast; } /** * @brief Gets the world polygon of this node. @@ -873,7 +875,7 @@ class EE_API Node : public Transformable { * * @return The ID hash value. */ - const String::HashType& getIdHash() const; + inline const String::HashType& getIdHash() const { return mIdHash; } /** * @brief Finds a descendant node by its ID string. @@ -1030,7 +1032,7 @@ class EE_API Node : public Transformable { * * @return True if reverse drawing is enabled, false otherwise. */ - bool isReverseDraw() const; + inline bool isReverseDraw() const { return 0 != ( mNodeFlags & NODE_FLAG_REVERSE_DRAW ); } /** * @brief Enables or disables reverse drawing order. @@ -1428,7 +1430,7 @@ class EE_API Node : public Transformable { * * @return True if this node has focus, false otherwise. */ - bool hasFocus() const; + inline bool hasFocus() const { return 0 != ( mNodeFlags & NODE_FLAG_HAS_FOCUS ); } /** * @brief Checks if this node or any descendant has focus. @@ -1553,7 +1555,7 @@ class EE_API Node : public Transformable { * * @return Pointer to the SceneNode or nullptr if not found. */ - SceneNode* getSceneNode() const; + inline SceneNode* getSceneNode() const { return mSceneNode; } /** * @brief Gets the event dispatcher associated with this node. @@ -1764,7 +1766,7 @@ class EE_API Node : public Transformable { * * @return True if the node's loading flag is set, false otherwise. */ - bool isLoadingState() const; + inline bool isLoadingState() const { return 0 != ( mNodeFlags & NODE_FLAG_LOADING ); } /** * @brief Called when the node's ID changes. @@ -1779,7 +1781,7 @@ class EE_API Node : public Transformable { * * @return True if the close flag is set (node will be removed), false otherwise. */ - bool isClosing() const; + inline bool isClosing() const { return 0 != ( mNodeFlags & NODE_FLAG_CLOSE ); } /** * @brief Checks if the node is marked for closure or any node in its parent tree. @@ -1793,7 +1795,9 @@ class EE_API Node : public Transformable { * * @return True if the closing children flag is set, false otherwise. */ - bool isClosingChildren() const; + inline bool isClosingChildren() const { + return 0 != ( mNodeFlags & NODE_FLAG_CLOSING_CHILDREN ); + } /** * @brief Finds the node under a point, considering hit testing. @@ -2554,7 +2558,9 @@ class EE_API Node : public Transformable { * * @return True if NODE_FLAG_SCHEDULED_UPDATE is set. */ - bool isSubscribedForScheduledUpdate(); + inline bool isSubscribedForScheduledUpdate() { + return 0 != ( mNodeFlags & NODE_FLAG_SCHEDULED_UPDATE ); + } }; }} // namespace EE::Scene diff --git a/include/eepp/ui/uihtmlwidget.hpp b/include/eepp/ui/uihtmlwidget.hpp index d71bfba39..a4ccb8a81 100644 --- a/include/eepp/ui/uihtmlwidget.hpp +++ b/include/eepp/ui/uihtmlwidget.hpp @@ -53,9 +53,11 @@ class EE_API UIHTMLWidget : public UILayout { virtual ~UIHTMLWidget(); - virtual Uint32 getType() const; + virtual Uint32 getType() const { return UI_TYPE_HTML_WIDGET; } - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { + return UIHTMLWidget::getType() == type || UILayout::isType( type ); + } UILayouter* getLayouter(); @@ -67,9 +69,13 @@ class EE_API UIHTMLWidget : public UILayout { void setDisplay( CSSDisplay display ); - bool isFlex() const; + inline bool isFlex() const { + return mDisplay == CSSDisplay::Flex || mDisplay == CSSDisplay::InlineFlex; + } - bool isGrid() const; + inline bool isGrid() const { + return mDisplay == CSSDisplay::Grid || mDisplay == CSSDisplay::InlineGrid; + } CSSPosition getCSSPosition() const { return mPosition; } @@ -317,7 +323,9 @@ class EE_API UIHTMLWidget : public UILayout { virtual void invalidateIntrinsicSize(); - bool isOutOfFlow() const; + inline bool isOutOfFlow() const { + return mPosition == CSSPosition::Absolute || mPosition == CSSPosition::Fixed; + } bool establishesBlockFormattingContext() const; diff --git a/include/eepp/ui/uilayout.hpp b/include/eepp/ui/uilayout.hpp index 4989746c2..d6d36081c 100644 --- a/include/eepp/ui/uilayout.hpp +++ b/include/eepp/ui/uilayout.hpp @@ -18,9 +18,11 @@ class EE_API UILayout : public UIWidget { Uint64 treeUpdates{ 0 }; }; - virtual Uint32 getType() const; + virtual Uint32 getType() const { return UI_TYPE_LAYOUT; } - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { + return UILayout::getType() == type || UIWidget::isType( type ); + } virtual const Sizef& getSize() const; diff --git a/include/eepp/ui/uinode.hpp b/include/eepp/ui/uinode.hpp index a0ae5e8d9..f78228dd3 100644 --- a/include/eepp/ui/uinode.hpp +++ b/include/eepp/ui/uinode.hpp @@ -117,7 +117,7 @@ class EE_API UINode : public Node { * * @return The node type as a Uint32. */ - virtual Uint32 getType() const; + virtual Uint32 getType() const { return UI_TYPE_UINODE; } /** * @brief Checks if the node is of a specific type. @@ -127,7 +127,9 @@ class EE_API UINode : public Node { * @param type The type identifier to check. * @return True if the node is of the specified type, false otherwise. */ - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { + return UINode::getType() == type || Node::isType( type ); + } /** * @brief Sets the node position in density-independent pixels (dp). diff --git a/include/eepp/ui/uirichtext.hpp b/include/eepp/ui/uirichtext.hpp index 05071a27e..4d16b809a 100644 --- a/include/eepp/ui/uirichtext.hpp +++ b/include/eepp/ui/uirichtext.hpp @@ -68,9 +68,11 @@ class EE_API UIRichText : public UIHTMLWidget { static UIRichText* NewBlockquote() { return UIRichText::NewWithTag( "blockquote" ); }; - virtual Uint32 getType() const; + virtual Uint32 getType() const { return UI_TYPE_RICHTEXT; } - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { + return UIRichText::getType() == type || UIHTMLWidget::isType( type ); + } virtual void draw(); diff --git a/include/eepp/ui/uiwidget.hpp b/include/eepp/ui/uiwidget.hpp index 24d105537..fb9db77ff 100644 --- a/include/eepp/ui/uiwidget.hpp +++ b/include/eepp/ui/uiwidget.hpp @@ -80,7 +80,7 @@ class EE_API UIWidget : public UINode { * * @return The widget type as a Uint32. */ - virtual Uint32 getType() const; + virtual Uint32 getType() const { return UI_TYPE_WIDGET; } /** * @brief Checks if the widget is of a specific type. @@ -90,7 +90,9 @@ class EE_API UIWidget : public UINode { * @param type The type identifier to check. * @return True if the widget is of the specified type, false otherwise. */ - virtual bool isType( const Uint32& type ) const; + virtual bool isType( const Uint32& type ) const { + return UIWidget::getType() == type || UINode::isType( type ); + } /** * @brief Sets multiple flags on the widget. diff --git a/src/benchmarks/inline_layout_benchmark.cpp b/src/benchmarks/inline_layout_benchmark.cpp index 8e79383e8..97952c155 100644 --- a/src/benchmarks/inline_layout_benchmark.cpp +++ b/src/benchmarks/inline_layout_benchmark.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -199,6 +200,28 @@ UTEST( Benchmark, CSSAttributeSelectorMatching ) { Engine::destroySingleton(); } +UTEST( Benchmark, CSSLengthParsing ) { + const std::array values = { + "0", "12px", "-1.5em", "+24dp", "100%", ".75rem", "50vw", "25vh", + "2.54cm", "12pt", "1e3px", "center", "left", "10vmin", "4ch", "8dpr" }; + const int iterations = getSelectorMatchingIterations(); + Float parsedValueSum = 0; + Clock parsingClock; + for ( int iteration = 0; iteration < iterations; ++iteration ) { + for ( const auto& value : values ) { + const auto length = StyleSheetLength::fromString( value ); + parsedValueSum += length.getValue(); + } + } + const Time parsingElapsed = parsingClock.getElapsedTime(); + + EXPECT_TRUE( parsedValueSum > 0 ); + UTEST_PRINT_INFO( + String::format( "CSS length parsing: %lld us", parsingElapsed.asMicroseconds() ).c_str() ); + UTEST_PRINT_INFO( + String::format( "CSS length values: %d", iterations * values.size() ).c_str() ); +} + static int getMarkdownFlushIterations() { if ( const char* env = std::getenv( "EE_MARKDOWN_BENCH_FLUSH_ITERATIONS" ) ) { Int32 val = markdownFlushIterations; diff --git a/src/eepp/core/string.cpp b/src/eepp/core/string.cpp index 0cf5f6d9e..e11573d61 100644 --- a/src/eepp/core/string.cpp +++ b/src/eepp/core/string.cpp @@ -54,7 +54,7 @@ namespace EE { -template static bool _fromString( T& t, const std::string& s, int base = 10 ) { +template static bool _fromString( T& t, std::string_view s, int base = 10 ) { const char* begin = s.data(); const char* end = s.data() + s.size(); @@ -109,83 +109,123 @@ template static std::string _toString( const T& value, size_t digitsAf } bool String::fromString( Int8& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Int16& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Int32& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Int64& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Uint8& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Uint16& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Uint32& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( Uint64& t, const std::string& s, int base ) { - return _fromString<>( t, s, base ); + return fromString( t, std::string_view( s ), base ); } bool String::fromString( float& t, const std::string& s ) { - return _fromString<>( t, s ); + return fromString( t, std::string_view( s ) ); } bool String::fromString( double& t, const std::string& s ) { + return fromString( t, std::string_view( s ) ); +} + +bool String::fromString( Int8& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Int16& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Int32& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Int64& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Uint8& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Uint16& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Uint32& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( Uint64& t, std::string_view s, int base ) { + return _fromString<>( t, s, base ); +} + +bool String::fromString( float& t, std::string_view s ) { + return _fromString<>( t, s ); +} + +bool String::fromString( double& t, std::string_view s ) { return _fromString<>( t, s ); } bool String::fromString( Int8& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Int16& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Int32& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Int64& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Uint8& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Uint16& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Uint32& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( Uint64& t, const String& s, int base ) { - return _fromString<>( t, s, base ); + return _fromString<>( t, s.toUtf8(), base ); } bool String::fromString( float& t, const String& s ) { - return _fromString<>( t, s ); + return _fromString<>( t, s.toUtf8() ); } bool String::fromString( double& t, const String& s ) { - return _fromString<>( t, s ); + return _fromString<>( t, s.toUtf8() ); } std::string String::toString( const Int8& i ) { diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index bddc1ad30..7e471cf9b 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -96,14 +96,6 @@ void Node::nodeToWorldTranslation( Vector2f& Pos ) const { } } -Uint32 Node::getType() const { - return 0; -} - -bool Node::isType( const Uint32& type ) const { - return Node::getType() == type; -} - void Node::messagePost( const NodeMessage* Msg ) { Node* node = this; while ( NULL != node ) { @@ -164,14 +156,6 @@ void Node::setInternalHeight( const Float& height ) { setInternalSize( Sizef( getSize().getWidth(), height ) ); } -const Sizef& Node::getSize() const { - return mSize; -} - -const Sizef& Node::getPixelsSize() const { - return mSize; -} - Node* Node::setVisible( const bool& visible, bool emitEventNotification ) { if ( mVisible != visible ) { mVisible = visible; @@ -197,10 +181,6 @@ Node* Node::setChildrenVisibility( bool visible, bool emitEventNotification ) { return this; } -bool Node::isVisible() const { - return mVisible; -} - bool Node::hasVisibility() const { const Node* cur = this; while ( cur ) { @@ -219,14 +199,6 @@ Node* Node::setEnabled( const bool& enabled ) { return this; } -bool Node::isEnabled() const { - return mEnabled; -} - -bool Node::isDisabled() const { - return !mEnabled; -} - void Node::updateDrawInvalidator( bool force ) { mNodeDrawInvalidator = getDrawInvalidator(); @@ -253,10 +225,6 @@ void Node::unsubscribeScheduledUpdate() { } } -bool Node::isSubscribedForScheduledUpdate() { - return 0 != ( mNodeFlags & NODE_FLAG_SCHEDULED_UPDATE ); -} - Node* Node::setParent( Node* parent ) { eeASSERT( NULL != parent ); @@ -370,14 +338,6 @@ Uint32 Node::onMouseClick( const Vector2i& Pos, const Uint32& Flags ) { return 1; } -bool Node::isMouseOver() const { - return 0 != ( mNodeFlags & NODE_FLAG_MOUSEOVER ); -} - -bool Node::isMouseOverMeOrChildren() const { - return 0 != ( mNodeFlags & NODE_FLAG_MOUSEOVER_ME_OR_CHILD ); -} - Uint32 Node::onMouseDoubleClick( const Vector2i& Pos, const Uint32& Flags ) { sendMouseEvent( Event::MouseDoubleClick, Pos, Flags ); return 1; @@ -442,10 +402,6 @@ Node* Node::setData( const UintPtr& data ) { return this; } -const UintPtr& Node::getData() const { - return mData; -} - Node* Node::setBlendMode( const BlendMode& blend ) { mBlend = blend; invalidateDraw(); @@ -755,10 +711,6 @@ void Node::onIdChange() { sendCommonEvent( Event::OnIdChange ); } -bool Node::isClosing() const { - return 0 != ( mNodeFlags & NODE_FLAG_CLOSE ); -} - bool Node::inClosingTree() const { if ( isClosing() ) return true; @@ -771,14 +723,6 @@ bool Node::inClosingTree() const { return false; } -bool Node::isClosingChildren() const { - return 0 != ( mNodeFlags & NODE_FLAG_CLOSING_CHILDREN ); -} - -const String::HashType& Node::getIdHash() const { - return mIdHash; -} - static bool isNestedSceneBoundary( const Node* child ) { return child->isSceneNode() && child->getSceneNode() == child; } @@ -933,10 +877,6 @@ void Node::setLoadingState( bool loading ) { writeNodeFlag( NODE_FLAG_LOADING, loading ? 1 : 0 ); } -bool Node::isLoadingState() const { - return 0 != ( mNodeFlags & NODE_FLAG_LOADING ); -} - Uint32 Node::getChildCount() const { Node* child = mChild; Uint32 count = 0; @@ -1002,14 +942,6 @@ Uint32 Node::getNodeOfTypeIndex() const { return nodeIndex; } -Node* Node::getFirstChild() const { - return mChild; -} - -Node* Node::getLastChild() const { - return mChildLast; -} - Node* Node::overFind( const Vector2f& point ) { Node* pOver = NULL; @@ -1078,42 +1010,6 @@ void Node::onSceneChange() { } } -bool Node::isWindow() const { - return 0 != ( mNodeFlags & NODE_FLAG_WINDOW ); -} - -bool Node::isLayout() const { - return 0 != ( mNodeFlags & NODE_FLAG_LAYOUT ); -} - -bool Node::isClipped() const { - return 0 != ( mNodeFlags & NODE_FLAG_CLIP_ENABLE ); -} - -bool Node::isRotated() const { - return 0 != ( mNodeFlags & NODE_FLAG_ROTATED ); -} - -bool Node::isScaled() const { - return 0 != ( mNodeFlags & NODE_FLAG_SCALED ); -} - -bool Node::isFrameBuffer() const { - return 0 != ( mNodeFlags & NODE_FLAG_FRAME_BUFFER ); -} - -bool Node::isSceneNode() const { - return 0 != ( mNodeFlags & NODE_FLAG_SCENENODE ); -} - -bool Node::isUISceneNode() const { - return 0 != ( mNodeFlags & NODE_FLAG_UISCENENODE ); -} - -bool Node::isUINode() const { - return 0 != ( mNodeFlags & NODE_FLAG_UINODE ); -} - bool Node::isMeOrParentTreeVisible() const { const Node* node = this; while ( NULL != node ) { @@ -1342,10 +1238,6 @@ void Node::nodeToWorld( Vector2f& pos ) const { pos = Vector2f( toPos.x, toPos.y ); } -bool Node::isReverseDraw() const { - return 0 != ( mNodeFlags & NODE_FLAG_REVERSE_DRAW ); -} - void Node::setReverseDraw( bool reverseDraw ) { writeNodeFlag( NODE_FLAG_REVERSE_DRAW, reverseDraw ? 1 : 0 ); invalidateDraw(); @@ -1357,10 +1249,6 @@ void Node::invalidateDraw() { } } -SceneNode* Node::getSceneNode() const { - return mSceneNode; -} - SceneNode* Node::findSceneNode() { Node* node = mParentNode; while ( node != NULL ) { @@ -1747,10 +1635,6 @@ Uint32 Node::onFocusLoss() { return 1; } -bool Node::hasFocus() const { - return 0 != ( mNodeFlags & NODE_FLAG_HAS_FOCUS ); -} - bool Node::hasFocusWithin() const { return hasFocus() || inParentTreeOf( getEventDispatcher()->getFocusNode() ); } diff --git a/src/eepp/ui/css/stylesheetlength.cpp b/src/eepp/ui/css/stylesheetlength.cpp index f3fb975af..8413130c0 100644 --- a/src/eepp/ui/css/stylesheetlength.cpp +++ b/src/eepp/ui/css/stylesheetlength.cpp @@ -44,22 +44,6 @@ enum PercentagePositions : String::HashType { None = 0, }; -static std::string positionToPercentage( const PercentagePositions& pos ) { - switch ( pos ) { - case Center: - return "50%"; - case Left: - case Top: - return "0%"; - case Right: - case Bottom: - return "100%"; - default: - case None: - return ""; - } -} - static PercentagePositions isPercentagePosition( const String::HashType& strHash ) { switch ( strHash ) { case PercentagePositions::Center: @@ -76,6 +60,45 @@ static PercentagePositions isPercentagePosition( const String::HashType& strHash return PercentagePositions::None; } +static size_t numericPrefixLength( std::string_view value ) { + if ( value.empty() ) + return 0; + size_t pos = 0; + if ( value[pos] == '-' || value[pos] == '+' ) + pos++; + + bool hasDigit = false; + bool hasDot = false; + while ( pos < value.size() ) { + const char c = value[pos]; + if ( c >= '0' && c <= '9' ) { + hasDigit = true; + pos++; + } else if ( c == '.' && !hasDot ) { + hasDot = true; + pos++; + } else { + break; + } + } + + if ( !hasDigit ) + return 0; + + if ( pos < value.size() && ( value[pos] == 'e' || value[pos] == 'E' ) ) { + size_t expPos = pos + 1; + if ( expPos < value.size() && ( value[expPos] == '-' || value[expPos] == '+' ) ) + expPos++; + const size_t expDigits = expPos; + while ( expPos < value.size() && value[expPos] >= '0' && value[expPos] <= '9' ) + expPos++; + if ( expPos != expDigits ) + pos = expPos; + } + + return pos; +} + StyleSheetLength::Unit StyleSheetLength::unitFromString( std::string_view unitStr ) { switch ( String::hashToLower( unitStr ) ) { case UnitHashes::Percentage: @@ -187,41 +210,10 @@ bool StyleSheetLength::isLength( std::string_view unitStr ) { if ( isFunctionString( unitStr ) ) return true; - size_t pos = 0; - if ( unitStr[pos] == '-' || unitStr[pos] == '+' ) - pos++; - - bool hasDigit = false; - bool hasDot = false; - while ( pos < unitStr.size() ) { - char c = unitStr[pos]; - if ( c >= '0' && c <= '9' ) { - hasDigit = true; - pos++; - } else if ( c == '.' && !hasDot ) { - hasDot = true; - pos++; - } else { - break; - } - } - - if ( !hasDigit ) + const size_t pos = numericPrefixLength( unitStr ); + if ( pos == 0 ) return false; - if ( pos < unitStr.size() && ( unitStr[pos] == 'e' || unitStr[pos] == 'E' ) ) { - size_t expPos = pos + 1; - if ( expPos < unitStr.size() && ( unitStr[expPos] == '-' || unitStr[expPos] == '+' ) ) - expPos++; - bool hasExpDigit = false; - while ( expPos < unitStr.size() && unitStr[expPos] >= '0' && unitStr[expPos] <= '9' ) { - hasExpDigit = true; - expPos++; - } - if ( hasExpDigit ) - pos = expPos; - } - std::string_view unit = unitStr.substr( pos ); if ( unit.empty() ) return true; @@ -390,14 +382,11 @@ StyleSheetLength& StyleSheetLength::operator=( const StyleSheetLength& val ) { StyleSheetLength StyleSheetLength::fromString( const std::string& str, const Float& defaultValue, bool pxAsDp ) { - PercentagePositions isPercentage = isPercentagePosition( String::hashToLower( str ) ); - if ( PercentagePositions::None != isPercentage ) - return fromString( positionToPercentage( isPercentage ), defaultValue ); - StyleSheetLength length; length.setValue( defaultValue, Unit::Px ); + const std::string_view value = String::trim( std::string_view( str ) ); - if ( isFunctionString( str ) ) { + if ( isFunctionString( value ) ) { Unit funcUnit = Unit::Px; Arguments args; if ( parseFunction( str, funcUnit, args ) ) { @@ -409,30 +398,39 @@ StyleSheetLength StyleSheetLength::fromString( const std::string& str, const Flo return length; } - std::string num; - std::string unit; - - for ( std::size_t i = 0; i < str.size(); i++ ) { - char c = str[i]; - if ( String::isNumber( c, true, true ) || ( '-' == c && i == 0 ) || - ( '+' == c && i == 0 ) ) { - num += c; + if ( !value.empty() ) { + const char first = value.front(); + const bool startsNumeric = + ( first >= '0' && first <= '9' ) || first == '.' || first == '-' || first == '+'; + if ( startsNumeric ) { + const size_t numberLength = numericPrefixLength( value ); + if ( numberLength != 0 ) { + std::string_view number = value.substr( 0, numberLength ); + if ( number.front() == '+' ) + number.remove_prefix( 1 ); + Float parsedValue = 0; + if ( String::fromString( parsedValue, number ) ) + length.setValue( parsedValue, unitFromString( value.substr( numberLength ) ) ); + } } else { - unit = str.substr( i ); - break; + switch ( isPercentagePosition( String::hashToLower( value ) ) ) { + case PercentagePositions::Center: + length.setValue( 50, Unit::Percentage ); + break; + case PercentagePositions::Right: + case PercentagePositions::Bottom: + length.setValue( 100, Unit::Percentage ); + break; + case PercentagePositions::Left: + case PercentagePositions::Top: + length.setValue( 0, Unit::Percentage ); + break; + case PercentagePositions::None: + break; + } } } - if ( !num.empty() ) { - Float val = 0; - while ( !num.empty() && !String::fromString( val, num ) ) { - unit = num.back() + unit; - num.pop_back(); - } - if ( !num.empty() ) - length.setValue( val, unitFromString( unit ) ); - } - if ( pxAsDp && length.getUnit() == Unit::Px ) length.mUnit = Unit::Dp; diff --git a/src/eepp/ui/uihtmltable.cpp b/src/eepp/ui/uihtmltable.cpp index 319e45975..bc0b95ef9 100644 --- a/src/eepp/ui/uihtmltable.cpp +++ b/src/eepp/ui/uihtmltable.cpp @@ -19,7 +19,6 @@ UIHTMLTable::UIHTMLTable() : UIHTMLWidget( "table" ) { Uint32 UIHTMLTable::getType() const { return UI_TYPE_HTML_TABLE; } - bool UIHTMLTable::isType( const Uint32& type ) const { return UIHTMLTable::getType() == type || UIHTMLWidget::isType( type ); } @@ -196,7 +195,6 @@ UIHTMLTableRow::UIHTMLTableRow() : UIHTMLWidget( "tr" ) { Uint32 UIHTMLTableRow::getType() const { return UI_TYPE_HTML_TABLE_ROW; } - bool UIHTMLTableRow::isType( const Uint32& type ) const { return UIHTMLTableRow::getType() == type || UIHTMLWidget::isType( type ); } @@ -214,7 +212,6 @@ UIHTMLTableCell::UIHTMLTableCell( const std::string& tag ) : UIRichText( tag ) { Uint32 UIHTMLTableCell::getType() const { return UI_TYPE_HTML_TABLE_CELL; } - bool UIHTMLTableCell::isType( const Uint32& type ) const { return UIHTMLTableCell::getType() == type || UIRichText::isType( type ); } @@ -292,7 +289,6 @@ UIHTMLTableHead::UIHTMLTableHead() : UIHTMLWidget( "thead" ) { Uint32 UIHTMLTableHead::getType() const { return UI_TYPE_HTML_TABLE_HEAD; } - bool UIHTMLTableHead::isType( const Uint32& type ) const { return UIHTMLTableHead::getType() == type || UIHTMLWidget::isType( type ); } @@ -310,7 +306,6 @@ UIHTMLTableBody::UIHTMLTableBody() : UIHTMLWidget( "tbody" ) { Uint32 UIHTMLTableBody::getType() const { return UI_TYPE_HTML_TABLE_BODY; } - bool UIHTMLTableBody::isType( const Uint32& type ) const { return UIHTMLTableBody::getType() == type || UIHTMLWidget::isType( type ); } @@ -328,7 +323,6 @@ UIHTMLTableFooter::UIHTMLTableFooter() : UIHTMLWidget( "tfoot" ) { Uint32 UIHTMLTableFooter::getType() const { return UI_TYPE_HTML_TABLE_FOOTER; } - bool UIHTMLTableFooter::isType( const Uint32& type ) const { return UIHTMLTableFooter::getType() == type || UIHTMLWidget::isType( type ); } diff --git a/src/eepp/ui/uihtmlwidget.cpp b/src/eepp/ui/uihtmlwidget.cpp index 2449f230e..a80deb935 100644 --- a/src/eepp/ui/uihtmlwidget.cpp +++ b/src/eepp/ui/uihtmlwidget.cpp @@ -93,14 +93,6 @@ UILayouter* UIHTMLWidget::getLayouter() { return mLayouter; } -Uint32 UIHTMLWidget::getType() const { - return UI_TYPE_HTML_WIDGET; -} - -bool UIHTMLWidget::isType( const Uint32& type ) const { - return UIHTMLWidget::getType() == type ? true : UILayout::isType( type ); -} - bool UIHTMLWidget::isPacking() const { UILayouter* layouter = const_cast( this )->getLayouter(); if ( layouter ) @@ -157,14 +149,6 @@ void UIHTMLWidget::setDisplay( CSSDisplay display ) { } } -bool UIHTMLWidget::isFlex() const { - return mDisplay == CSSDisplay::Flex || mDisplay == CSSDisplay::InlineFlex; -} - -bool UIHTMLWidget::isGrid() const { - return mDisplay == CSSDisplay::Grid || mDisplay == CSSDisplay::InlineGrid; -} - Float UIHTMLWidget::getBaseline() const { if ( mLayouter ) { if ( isFlex() ) { @@ -1341,10 +1325,6 @@ void UIHTMLWidget::invalidateIntrinsicSize() { UIWidget::invalidateIntrinsicSize(); } -bool UIHTMLWidget::isOutOfFlow() const { - return mPosition == CSSPosition::Absolute || mPosition == CSSPosition::Fixed; -} - bool UIHTMLWidget::establishesBlockFormattingContext() const { if ( mFloat != CSSFloat::None || isOutOfFlow() || mDisplay == CSSDisplay::InlineBlock ) return true; diff --git a/src/eepp/ui/uilayout.cpp b/src/eepp/ui/uilayout.cpp index 863b9fc1c..9834020d4 100644 --- a/src/eepp/ui/uilayout.cpp +++ b/src/eepp/ui/uilayout.cpp @@ -51,14 +51,6 @@ void UILayout::onLayoutUpdate() { sendCommonEvent( Event::OnLayoutUpdate ); } -Uint32 UILayout::getType() const { - return UI_TYPE_LAYOUT; -} - -bool UILayout::isType( const Uint32& type ) const { - return UILayout::getType() == type ? true : UIWidget::isType( type ); -} - const Sizef& UILayout::getSize() const { if ( mDirtyLayout ) const_cast( this )->updateLayout(); diff --git a/src/eepp/ui/uilinearlayout.cpp b/src/eepp/ui/uilinearlayout.cpp index 2eb90a4fd..d2348bbf7 100644 --- a/src/eepp/ui/uilinearlayout.cpp +++ b/src/eepp/ui/uilinearlayout.cpp @@ -39,16 +39,16 @@ UILinearLayout::UILinearLayout( const std::string& tag, const UIOrientation& ori setClipType( ClipType::ContentBox ); } +UIOrientation UILinearLayout::getOrientation() const { + return mOrientation; +} + Uint32 UILinearLayout::getType() const { return UI_TYPE_LINEAR_LAYOUT; } bool UILinearLayout::isType( const Uint32& type ) const { - return UILinearLayout::getType() == type ? true : UILayout::isType( type ); -} - -UIOrientation UILinearLayout::getOrientation() const { - return mOrientation; + return UILinearLayout::getType() == type || UILayout::isType( type ); } UILinearLayout* UILinearLayout::setOrientation( const UIOrientation& orientation ) { diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 156d601e1..997eb2e63 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -89,14 +89,6 @@ void UINode::nodeToWorldTranslation( Vector2f& Pos ) const { } } -Uint32 UINode::getType() const { - return UI_TYPE_UINODE; -} - -bool UINode::isType( const Uint32& type ) const { - return UINode::getType() == type || Node::isType( type ); -} - void UINode::setInternalPosition( const Vector2f& Pos ) { mDpPos = Pos; Transformable::setPosition( PixelDensity::dpToPx( Pos ) ); diff --git a/src/eepp/ui/uirichtext.cpp b/src/eepp/ui/uirichtext.cpp index ec2f06fca..244d59448 100644 --- a/src/eepp/ui/uirichtext.cpp +++ b/src/eepp/ui/uirichtext.cpp @@ -434,14 +434,6 @@ UIRichText::UIRichText( const std::string& tag ) : UIHTMLWidget( tag ) { setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::WrapContent ); } -Uint32 UIRichText::getType() const { - return UI_TYPE_RICHTEXT; -} - -bool UIRichText::isType( const Uint32& type ) const { - return UIRichText::getType() == type ? true : UIHTMLWidget::isType( type ); -} - const RichText& UIRichText::getRichText() { return mRichText; } diff --git a/src/eepp/ui/uiwidget.cpp b/src/eepp/ui/uiwidget.cpp index 5f6aafe37..9bf5de913 100644 --- a/src/eepp/ui/uiwidget.cpp +++ b/src/eepp/ui/uiwidget.cpp @@ -105,14 +105,6 @@ UIWidget::~UIWidget() { eeSAFE_DELETE( mTooltip ); } -Uint32 UIWidget::getType() const { - return UI_TYPE_WIDGET; -} - -bool UIWidget::isType( const Uint32& type ) const { - return UIWidget::getType() == type ? true : UINode::isType( type ); -} - void UIWidget::updateAnchorsDistances() { if ( NULL != mParentNode ) { mDistToBorder = Rect( mPosition.x, mPosition.y, diff --git a/src/tests/unit_tests/stringsoperations_tests.cpp b/src/tests/unit_tests/stringsoperations_tests.cpp index 1dcd7e310..67795dc73 100644 --- a/src/tests/unit_tests/stringsoperations_tests.cpp +++ b/src/tests/unit_tests/stringsoperations_tests.cpp @@ -18,6 +18,18 @@ UTEST( String, countLines ) { EXPECT_EQ( static_cast( 3 ), String::countLines( "\n\n" ) ); } +UTEST( String, fromStringView ) { + const std::string values = "1234.5px"; + Float floatValue = 0; + EXPECT_TRUE( String::fromString( floatValue, std::string_view( values ).substr( 0, 6 ) ) ); + EXPECT_NEAR( 1234.5f, floatValue, 0.0001f ); + EXPECT_FALSE( String::fromString( floatValue, std::string_view( values ) ) ); + + Int32 intValue = 0; + EXPECT_TRUE( String::fromString( intValue, std::string_view( values ).substr( 0, 4 ) ) ); + EXPECT_EQ( 1234, intValue ); +} + UTEST( FileSystem, fileCountLines ) { std::string path = Sys::getTempPath() + "eepp_test_count_lines.txt"; FileSystem::fileWrite( path, "A\nB\nC" ); @@ -153,7 +165,8 @@ UTEST( String, isLatin1 ) { EXPECT_TRUE( str255.isLatin1() ); // Complex string with Latin1 chars - String complexLatin1 = String::fromUtf8( "Héllø Wørld"sv ); // Assuming these are in Latin1 range + String complexLatin1 = + String::fromUtf8( "Héllø Wørld"sv ); // Assuming these are in Latin1 range // Note: 'ø' is 0xF8 (248), 'é' is 0xE9 (233). Both in Latin1. EXPECT_TRUE( complexLatin1.isLatin1() ); @@ -162,7 +175,6 @@ UTEST( String, isLatin1 ) { // 32 chars of 255 String longLatin1( 32, (String::StringBaseType)255 ); EXPECT_TRUE( longLatin1.isLatin1() ); - } } @@ -187,14 +199,15 @@ UTEST( String, isAsciiHighBit ) { UTEST( String, isAsciiPatterns ) { // Alternating String alt; - for (int i = 0; i < 100; i++) { - alt += (i % 2 == 0) ? 'a' : (char)128; + for ( int i = 0; i < 100; i++ ) { + alt += ( i % 2 == 0 ) ? 'a' : (char)128; } EXPECT_FALSE( alt.isAscii() ); // Block of invalid in middle of valid - String block(100, 'a'); - for(int i=40; i<60; i++) block[i] = 200; + String block( 100, 'a' ); + for ( int i = 40; i < 60; i++ ) + block[i] = 200; EXPECT_FALSE( block.isAscii() ); } diff --git a/src/tests/unit_tests/uicss_inheritance_tests.cpp b/src/tests/unit_tests/uicss_inheritance_tests.cpp index 6622f4be6..1bd5bd109 100644 --- a/src/tests/unit_tests/uicss_inheritance_tests.cpp +++ b/src/tests/unit_tests/uicss_inheritance_tests.cpp @@ -1349,6 +1349,33 @@ UTEST( CSSFunctions, ClampResolvesPixels ) { EXPECT_EQ( 50, resolved ); } +UTEST( CSSLength, ScalarParsing ) { + auto expectLength = [&]( const std::string& value, Float expectedValue, + StyleSheetLength::Unit expectedUnit ) { + const auto length = StyleSheetLength::fromString( value ); + EXPECT_NEAR( expectedValue, length.getValue(), 0.0001f ); + EXPECT_EQ( expectedUnit, length.getUnit() ); + }; + + expectLength( "12px", 12, StyleSheetLength::Unit::Px ); + expectLength( "-1.5em", -1.5f, StyleSheetLength::Unit::Em ); + expectLength( "+24dp", 24, StyleSheetLength::Unit::Dp ); + expectLength( ".75rem", 0.75f, StyleSheetLength::Unit::Rem ); + expectLength( "1e3px", 1000, StyleSheetLength::Unit::Px ); + expectLength( " 50% ", 50, StyleSheetLength::Unit::Percentage ); + expectLength( " center ", 50, StyleSheetLength::Unit::Percentage ); + expectLength( "left", 0, StyleSheetLength::Unit::Percentage ); + expectLength( "bottom", 100, StyleSheetLength::Unit::Percentage ); + expectLength( "10", 10, StyleSheetLength::Unit::Dp ); + expectLength( "10unknown", 10, StyleSheetLength::Unit::Dp ); + const auto invalid = StyleSheetLength::fromString( "invalid", 7 ); + EXPECT_EQ( 7, invalid.getValue() ); + EXPECT_EQ( StyleSheetLength::Unit::Px, invalid.getUnit() ); + + const auto pxAsDp = StyleSheetLength::fromString( "12px", 0, true ); + EXPECT_EQ( StyleSheetLength::Unit::Dp, pxAsDp.getUnit() ); +} + UTEST( CSSFunctions, ClampHonorsMinimum ) { auto len = StyleSheetLength::fromString( "clamp(40px, 30px, 100px)" ); Float resolved = len.asPixels( 0, Sizef::Zero, 96, 12, 12, nullptr );