From ee4363d52ff92872cc456d7051e5c1ece7d805a3 Mon Sep 17 00:00:00 2001 From: Sasi Shanmugarajah Date: Thu, 8 Dec 2022 11:20:30 -0500 Subject: [PATCH] APL-CORE: December 2022 Release of APL 2022.2 compilant core engine (2022.2.2) For more details on this release refer to CHANGELOG.md To learn about APL see: https://developer.amazon.com/docs/alexa-presentation-language/understand-apl.html --- CHANGELOG.md | 10 +- CMakeLists.txt | 2 +- aplcore/CMakeLists.txt | 8 + aplcore/aplcoreConfig.cmake.in | 9 +- aplcore/include/apl/animation/easing.h | 15 +- aplcore/include/apl/animation/easinggrammar.h | 106 +- aplcore/include/apl/apl.h | 1 + .../include/apl/colorgrammar/colorgrammar.h | 74 +- .../apl/component/actionablecomponent.h | 8 +- aplcore/include/apl/component/component.h | 9 +- .../apl/component/componenteventwrapper.h | 21 +- aplcore/include/apl/component/corecomponent.h | 37 +- .../include/apl/component/imagecomponent.h | 4 +- .../component/multichildscrollablecomponent.h | 2 +- .../include/apl/component/pagercomponent.h | 10 +- .../apl/component/scrollablecomponent.h | 6 + .../include/apl/component/textmeasurement.h | 5 + .../apl/component/touchwrappercomponent.h | 6 + .../apl/component/vectorgraphiccomponent.h | 9 +- .../include/apl/component/videocomponent.h | 8 +- .../apl/content/extensioncommanddefinition.h | 2 + aplcore/include/apl/content/rootconfig.h | 4 +- aplcore/include/apl/datagrammar/boundsymbol.h | 12 +- aplcore/include/apl/datagrammar/bytecode.h | 12 +- .../apl/datagrammar/databindingerrors.h | 11 +- .../apl/datagrammar/databindinggrammar.h | 2 + .../apl/datagrammar/databindingrules.h | 166 +- .../apl/datagrammar/databindingstack.h | 3 +- .../include/apl/datagrammar/grammarpolyfill.h | 178 + aplcore/include/apl/engine/contextwrapper.h | 17 +- aplcore/include/apl/engine/propdef.h | 12 +- .../include/apl/engine/recalculatetarget.h | 9 + aplcore/include/apl/engine/uidobject.h | 18 +- .../apl/extension/extensioncomponent.h | 10 +- aplcore/include/apl/graphic/graphic.h | 17 +- aplcore/include/apl/graphic/graphicelement.h | 4 +- .../apl/graphic/graphicelementcontainer.h | 12 +- .../include/apl/graphic/graphicelementgroup.h | 12 +- .../include/apl/graphic/graphicelementpath.h | 4 +- .../include/apl/graphic/graphicelementtext.h | 6 +- aplcore/include/apl/graphic/graphicfilter.h | 4 +- aplcore/include/apl/graphic/graphicpattern.h | 4 +- aplcore/include/apl/livedata/livearray.h | 2 +- .../include/apl/livedata/livearrayobject.h | 9 +- aplcore/include/apl/livedata/livedataobject.h | 4 +- aplcore/include/apl/livedata/livemap.h | 2 +- aplcore/include/apl/livedata/livemapobject.h | 9 +- aplcore/include/apl/livedata/liveobject.h | 7 +- aplcore/include/apl/media/mediaobject.h | 5 + .../apl/primitives/accessibilityaction.h | 11 +- aplcore/include/apl/primitives/color.h | 37 +- aplcore/include/apl/primitives/dimension.h | 176 + aplcore/include/apl/primitives/filter.h | 5 +- aplcore/include/apl/primitives/functions.h | 18 +- aplcore/include/apl/primitives/gradient.h | 4 +- aplcore/include/apl/primitives/mediasource.h | 4 +- aplcore/include/apl/primitives/object.h | 308 +- aplcore/include/apl/primitives/objectbag.h | 2 +- aplcore/include/apl/primitives/objectdata.h | 87 +- aplcore/include/apl/primitives/objecttype.h | 549 +++ aplcore/include/apl/primitives/radii.h | 5 +- aplcore/include/apl/primitives/range.h | 6 + aplcore/include/apl/primitives/rect.h | 12 + aplcore/include/apl/primitives/styledtext.h | 29 +- .../apl/primitives/textmeasurerequest.h | 2 +- aplcore/include/apl/primitives/timegrammar.h | 5 +- aplcore/include/apl/primitives/transform.h | 24 +- aplcore/include/apl/primitives/transform2d.h | 41 +- aplcore/include/apl/primitives/urlrequest.h | 31 +- .../include/apl/scenegraph/accessibility.h | 1 - aplcore/include/apl/scenegraph/builder.h | 1 - aplcore/include/apl/scenegraph/layer.h | 8 +- .../include/apl/scenegraph/modifiednodelist.h | 2 - aplcore/include/apl/scenegraph/node.h | 42 +- aplcore/include/apl/scenegraph/paint.h | 2 + aplcore/include/apl/scenegraph/path.h | 28 +- aplcore/include/apl/scenegraph/pathbounds.h | 57 + aplcore/include/apl/scenegraph/pathop.h | 11 +- aplcore/include/apl/scenegraph/pathparser.h | 7 + .../include/apl/scenegraph/textmeasurement.h | 4 + .../apl/touch/gestures/scrollgesture.h | 8 +- aplcore/include/apl/utils/dump_object.h | 2 + aplcore/include/apl/utils/stickyfunctions.h | 10 +- aplcore/include/apl/utils/stringfunctions.h | 45 + aplcore/include/apl/utils/throw.h | 29 + aplcore/src/action/animatedscrollaction.cpp | 10 +- aplcore/src/action/animateitemaction.cpp | 2 +- aplcore/src/action/controlmediaaction.cpp | 2 +- aplcore/src/action/playmediaaction.cpp | 15 +- aplcore/src/action/scrollaction.cpp | 2 +- aplcore/src/action/scrolltoaction.cpp | 12 +- aplcore/src/action/speakitemaction.cpp | 5 +- aplcore/src/action/speaklistaction.cpp | 4 +- aplcore/src/animation/animatedproperty.cpp | 2 +- aplcore/src/animation/coreeasing.cpp | 2 +- aplcore/src/animation/easing.cpp | 23 +- aplcore/src/animation/easingapproximation.cpp | 2 + aplcore/src/audio/speechmark.cpp | 33 +- aplcore/src/command/animateitemcommand.cpp | 2 +- aplcore/src/command/corecommand.cpp | 8 +- aplcore/src/command/documentcommand.cpp | 2 +- aplcore/src/command/extensioneventcommand.cpp | 2 +- aplcore/src/command/sendeventcommand.cpp | 10 +- aplcore/src/component/actionablecomponent.cpp | 12 +- aplcore/src/component/component.cpp | 6 +- aplcore/src/component/corecomponent.cpp | 126 +- aplcore/src/component/edittextcomponent.cpp | 54 +- aplcore/src/component/framecomponent.cpp | 45 +- .../src/component/gridsequencecomponent.cpp | 4 +- aplcore/src/component/imagecomponent.cpp | 38 +- aplcore/src/component/mediacomponenttrait.cpp | 9 +- .../multichildscrollablecomponent.cpp | 128 +- aplcore/src/component/pagercomponent.cpp | 35 +- aplcore/src/component/scrollablecomponent.cpp | 16 +- aplcore/src/component/scrollviewcomponent.cpp | 8 +- aplcore/src/component/sequencecomponent.cpp | 2 +- aplcore/src/component/textcomponent.cpp | 70 +- aplcore/src/component/touchablecomponent.cpp | 2 +- .../src/component/touchwrappercomponent.cpp | 8 +- .../src/component/vectorgraphiccomponent.cpp | 148 +- aplcore/src/component/videocomponent.cpp | 28 +- aplcore/src/content/rootconfig.cpp | 8 + aplcore/src/datagrammar/bytecode.cpp | 2 +- aplcore/src/datagrammar/bytecodeassembler.cpp | 22 +- aplcore/src/datagrammar/bytecodeoptimizer.cpp | 3 +- aplcore/src/datagrammar/functions.cpp | 3 +- .../dynamicindexlistdatasourceprovider.cpp | 7 +- .../dynamictokenlistdatasourceprovider.cpp | 5 +- aplcore/src/engine/binding.cpp | 1 + aplcore/src/engine/builder.cpp | 3 +- aplcore/src/engine/evaluate.cpp | 2 +- aplcore/src/engine/hovermanager.cpp | 2 +- aplcore/src/engine/keyboardmanager.cpp | 2 +- aplcore/src/engine/layoutmanager.cpp | 4 +- aplcore/src/engine/rootcontext.cpp | 12 +- aplcore/src/engine/uidobject.cpp | 5 +- aplcore/src/extension/extensionclient.cpp | 12 +- aplcore/src/extension/extensioncomponent.cpp | 24 +- aplcore/src/extension/extensionmanager.cpp | 2 +- aplcore/src/focus/focusfinder.cpp | 6 +- aplcore/src/focus/focusmanager.cpp | 8 +- aplcore/src/focus/focusvisitor.cpp | 2 +- aplcore/src/graphic/graphic.cpp | 10 +- aplcore/src/graphic/graphicelement.cpp | 15 +- .../src/graphic/graphicelementcontainer.cpp | 9 +- aplcore/src/graphic/graphicelementgroup.cpp | 17 +- aplcore/src/graphic/graphicelementpath.cpp | 110 +- aplcore/src/graphic/graphicelementtext.cpp | 94 +- aplcore/src/graphic/graphicfilter.cpp | 2 +- aplcore/src/graphic/graphicpattern.cpp | 8 +- aplcore/src/livedata/livearrayobject.cpp | 6 + aplcore/src/livedata/livedataobject.cpp | 14 +- aplcore/src/livedata/livemapobject.cpp | 6 + aplcore/src/media/coremediamanager.cpp | 2 +- aplcore/src/media/mediautils.cpp | 2 +- aplcore/src/primitives/CMakeLists.txt | 2 +- .../src/primitives/accessibilityaction.cpp | 4 +- aplcore/src/primitives/color.cpp | 13 +- aplcore/src/primitives/dimension.cpp | 231 +- aplcore/src/primitives/filter.cpp | 2 +- aplcore/src/primitives/functions.cpp | 1 - aplcore/src/primitives/gradient.cpp | 4 +- aplcore/src/primitives/mediasource.cpp | 8 +- aplcore/src/primitives/object.cpp | 1300 +----- aplcore/src/primitives/objectdata.cpp | 44 - aplcore/src/primitives/objecttype.cpp | 122 + aplcore/src/primitives/rect.cpp | 20 + aplcore/src/primitives/styledtext.cpp | 76 +- aplcore/src/primitives/styledtextstate.cpp | 1 + aplcore/src/primitives/timegrammar.cpp | 12 +- aplcore/src/primitives/transform.cpp | 27 +- aplcore/src/primitives/transform2d.cpp | 25 +- aplcore/src/primitives/urlrequest.cpp | 41 +- aplcore/src/scenegraph/CMakeLists.txt | 1 + aplcore/src/scenegraph/accessibility.cpp | 6 - aplcore/src/scenegraph/builder.cpp | 48 +- aplcore/src/scenegraph/filter.cpp | 8 +- aplcore/src/scenegraph/layer.cpp | 28 +- aplcore/src/scenegraph/modifiednodelist.cpp | 14 +- aplcore/src/scenegraph/node.cpp | 187 +- aplcore/src/scenegraph/paint.cpp | 14 +- aplcore/src/scenegraph/path.cpp | 51 +- aplcore/src/scenegraph/pathbounds.cpp | 284 ++ aplcore/src/scenegraph/pathop.cpp | 14 +- aplcore/src/scenegraph/pathparser.cpp | 517 ++- aplcore/src/scenegraph/utilities.cpp | 16 +- .../src/touch/gestures/pagerflinggesture.cpp | 12 +- aplcore/src/touch/gestures/scrollgesture.cpp | 4 +- .../src/touch/gestures/swipeawaygesture.cpp | 8 +- aplcore/src/touch/pointermanager.cpp | 8 +- aplcore/src/touch/utils/pagemovehandler.cpp | 8 +- .../utils/unidirectionaleasingscroller.cpp | 4 +- aplcore/src/utils/CMakeLists.txt | 1 + aplcore/src/utils/log.cpp | 2 +- aplcore/src/utils/stickychildrentree.cpp | 4 +- aplcore/src/utils/stickyfunctions.cpp | 18 +- aplcore/src/utils/stringfunctions.cpp | 102 + aplcore/src/utils/throw.cpp | 29 + bin/apl-header-inclusion-validation.sh | 2 + components.cmake | 7 + doc/scene_graph.puml | 4 - doc/scenegraph.md | 160 +- extensions/alexaext/CMakeLists.txt | 4 + .../include/alexaext/extensionmessage.h | 10 + .../alexaext/extensionresourceholder.h | 1 + .../alexaext/extensionresourceprovider.h | 4 + .../include/alexaext/extensionschema.h | 81 +- .../AplAudioPlayerExtension.cpp | 3 +- .../alexaext/src/localextensionproxy.cpp | 22 +- extensions/unit/unittest_apl_audio_player.cpp | 40 +- .../unit/unittest_extension_message.cpp | 2 + .../unit/unittest_extension_provider.cpp | 64 +- extensions/unit/unittest_extension_schema.cpp | 95 +- options.cmake | 1 + patches/pegtl.patch | 3508 ++++++++++++++++- patches/rapidjson.patch | 226 +- patches/yoga.patch | 36 + test/parseDirective.cpp | 19 +- test/parseExpression.cpp | 10 +- thirdparty/thirdparty.cmake | 2 +- unit/CMakeLists.txt | 3 +- unit/animation/unittest_easing.cpp | 14 +- .../unittest_easing_approximation.cpp | 2 +- unit/audio/testaudioplayer.cpp | 2 + unit/audio/testaudioplayerfactory.h | 2 +- unit/audio/unittest_command_page_audio.cpp | 2 +- .../audio/unittest_dynamictokenlist_audio.cpp | 2 +- unit/audio/unittest_sequencer_audio.cpp | 4 +- unit/audio/unittest_speak_item_audio.cpp | 14 +- unit/audio/unittest_speech_marks.cpp | 30 + unit/command/unittest_command_animateitem.cpp | 28 +- .../unittest_command_animateitem_values.cpp | 6 +- unit/command/unittest_command_document.cpp | 2 +- .../unittest_command_event_binding.cpp | 8 +- unit/command/unittest_command_page.cpp | 2 +- unit/command/unittest_command_sendevent.cpp | 99 + unit/command/unittest_command_setvalue.cpp | 4 +- unit/command/unittest_commands.cpp | 28 +- unit/command/unittest_setstate.cpp | 8 +- .../unittest_accessibility_actions.cpp | 12 +- unit/component/unittest_bounds.cpp | 44 +- unit/component/unittest_component_events.cpp | 16 +- .../unittest_default_component_size.cpp | 5 +- unit/component/unittest_draw.cpp | 4 +- unit/component/unittest_dynamic_component.cpp | 2 +- .../component/unittest_dynamic_properties.cpp | 176 +- .../unittest_edit_text_component.cpp | 183 +- unit/component/unittest_flexbox.cpp | 252 +- unit/component/unittest_frame_component.cpp | 14 +- .../unittest_grid_sequence_component.cpp | 20 +- unit/component/unittest_layout_direction.cpp | 58 +- unit/component/unittest_scroll.cpp | 10 +- unit/component/unittest_serialize.cpp | 42 +- unit/component/unittest_text_component.cpp | 10 +- unit/component/unittest_visual_hash.cpp | 4 +- unit/content/unittest_apl.cpp | 6 +- unit/content/unittest_document.cpp | 4 +- unit/content/unittest_document_background.cpp | 24 +- unit/datagrammar/unittest_decompile.cpp | 2 +- unit/datagrammar/unittest_grammar.cpp | 8 +- unit/datagrammar/unittest_optimize.cpp | 6 +- unit/datagrammar/unittest_parse.cpp | 2 +- unit/datasource/unittest_dynamicindexlist.cpp | 28 +- unit/datasource/unittest_dynamictokenlist.cpp | 6 +- unit/debugtools.cpp | 6 +- unit/engine/unittest_builder.cpp | 82 +- .../engine/unittest_builder_config_change.cpp | 32 +- unit/engine/unittest_builder_pager.cpp | 2 +- unit/engine/unittest_builder_preserve.cpp | 10 +- .../unittest_builder_preserve_scroll.cpp | 12 +- unit/engine/unittest_builder_sequence.cpp | 6 +- unit/engine/unittest_context.cpp | 4 +- unit/engine/unittest_dependant.cpp | 4 +- unit/engine/unittest_hover.cpp | 22 +- unit/engine/unittest_keyboard_manager.cpp | 48 +- unit/engine/unittest_layouts.cpp | 4 +- unit/engine/unittest_resources.cpp | 24 +- unit/engine/unittest_styles.cpp | 2 +- unit/extension/unittest_extension_client.cpp | 34 +- .../unittest_extension_component.cpp | 6 +- .../extension/unittest_extension_mediator.cpp | 252 +- .../unittest_requested_extension.cpp | 2 +- unit/focus/unittest_focus_manager.cpp | 26 +- unit/focus/unittest_native_focus.cpp | 12 +- unit/graphic/unittest_dependant_graphic.cpp | 42 +- unit/graphic/unittest_graphic.cpp | 138 +- unit/graphic/unittest_graphic_bind.cpp | 6 +- unit/graphic/unittest_graphic_component.cpp | 140 +- unit/graphic/unittest_graphic_data.cpp | 10 +- unit/graphic/unittest_graphic_filters.cpp | 26 +- unit/livedata/unittest_livearray_rebuild.cpp | 32 +- unit/media/testmediaplayer.cpp | 2 + unit/media/testmediaplayerfactory.h | 2 +- unit/media/unittest_media_manager.cpp | 124 +- unit/media/unittest_media_player.cpp | 80 +- unit/primitives/unittest_color.cpp | 8 +- unit/primitives/unittest_dimension.cpp | 42 +- unit/primitives/unittest_filters.cpp | 88 +- unit/primitives/unittest_object.cpp | 175 +- unit/primitives/unittest_styledtext.cpp | 116 +- unit/primitives/unittest_transform.cpp | 17 +- unit/scenegraph/CMakeLists.txt | 10 + unit/scenegraph/test_sg.cpp | 195 +- unit/scenegraph/test_sg.h | 30 +- unit/scenegraph/unittest_sg_accessibility.cpp | 50 + unit/scenegraph/unittest_sg_edit_text.cpp | 25 +- .../unittest_sg_edit_text_config.cpp | 106 + unit/scenegraph/unittest_sg_filter.cpp | 215 + unit/scenegraph/unittest_sg_frame.cpp | 30 +- unit/scenegraph/unittest_sg_graphic.cpp | 804 ++-- .../unittest_sg_graphic_component.cpp | 108 +- .../unittest_sg_graphic_loading.cpp | 381 ++ unit/scenegraph/unittest_sg_layer.cpp | 267 ++ .../unittest_sg_line_highlighting.cpp | 277 +- unit/scenegraph/unittest_sg_node.cpp | 473 +++ unit/scenegraph/unittest_sg_nodebounds.cpp | 269 ++ unit/scenegraph/unittest_sg_paint.cpp | 268 ++ unit/scenegraph/unittest_sg_path.cpp | 109 +- unit/scenegraph/unittest_sg_pathbounds.cpp | 101 + unit/scenegraph/unittest_sg_pathop.cpp | 123 + unit/scenegraph/unittest_sg_pathparser.cpp | 104 +- unit/scenegraph/unittest_sg_text.cpp | 1 - .../unittest_sg_text_properties.cpp | 107 + unit/testeventloop.h | 88 +- unit/time/unittest_sequencer.cpp | 4 +- unit/touch/unittest_gestures.cpp | 146 +- unit/touch/unittest_pointer.cpp | 4 +- unit/utils/unittest_stringfunctions.cpp | 43 + 328 files changed, 13655 insertions(+), 4968 deletions(-) create mode 100644 aplcore/include/apl/datagrammar/grammarpolyfill.h create mode 100644 aplcore/include/apl/primitives/objecttype.h create mode 100644 aplcore/include/apl/scenegraph/pathbounds.h create mode 100644 aplcore/include/apl/utils/throw.h delete mode 100644 aplcore/src/primitives/objectdata.cpp create mode 100644 aplcore/src/primitives/objecttype.cpp create mode 100644 aplcore/src/scenegraph/pathbounds.cpp create mode 100644 aplcore/src/utils/throw.cpp create mode 100644 unit/scenegraph/unittest_sg_edit_text_config.cpp create mode 100644 unit/scenegraph/unittest_sg_filter.cpp create mode 100644 unit/scenegraph/unittest_sg_graphic_loading.cpp create mode 100644 unit/scenegraph/unittest_sg_layer.cpp create mode 100644 unit/scenegraph/unittest_sg_node.cpp create mode 100644 unit/scenegraph/unittest_sg_nodebounds.cpp create mode 100644 unit/scenegraph/unittest_sg_paint.cpp create mode 100644 unit/scenegraph/unittest_sg_pathbounds.cpp create mode 100644 unit/scenegraph/unittest_sg_pathop.cpp create mode 100644 unit/scenegraph/unittest_sg_text_properties.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0c7f4..372f912 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog -## [2022.2] +## [2022.2.1] + +### Changed + +- Bug fixes. +- Performance improvements. +- Build improvements. + +## [2022.2.0] This release adds support for version 2022.2 of the APL specification. diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ff07ed..17bb4dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ # on older systems include(FetchContent OPTIONAL RESULT_VARIABLE HAS_FETCH_CONTENT) -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.11) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/aplcore/CMakeLists.txt b/aplcore/CMakeLists.txt index 8be248a..7a11bae 100644 --- a/aplcore/CMakeLists.txt +++ b/aplcore/CMakeLists.txt @@ -18,6 +18,10 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth=1024") endif() +# Disable what needs to be disabled +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-exceptions") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") + include(target_sources_local.cmake) # Check for the presence of GIT @@ -179,6 +183,10 @@ if (USE_PROVIDED_YOGA_AS_LIB) # We built the bundled yoga lib, install it install(FILES ${YOGA_LIB} DESTINATION lib) + install(DIRECTORY ${YOGA_INCLUDE}/yoga + DESTINATION include + FILES_MATCHING PATTERN "*.h") + set(YOGA_EXTERNAL_LIB ${YOGA_LIB}) # used by aplcoreConfig.cmake.in endif() if (NOT USE_PROVIDED_YOGA_INLINE) diff --git a/aplcore/aplcoreConfig.cmake.in b/aplcore/aplcoreConfig.cmake.in index 60a26f1..db2d4e6 100644 --- a/aplcore/aplcoreConfig.cmake.in +++ b/aplcore/aplcoreConfig.cmake.in @@ -13,11 +13,14 @@ else() endif() +set(ENABLE_ALEXAEXTENSIONS @ENABLE_ALEXAEXTENSIONS@) set(USE_INTERNAL_ALEXAEXT @BUILD_ALEXAEXTENSIONS@) -if(NOT USE_INTERNAL_ALEXAEXT) - find_package(alexaext REQUIRED) -endif() +if(ENABLE_ALEXAEXTENSIONS) + if(NOT USE_INTERNAL_ALEXAEXT) + find_package(alexaext REQUIRED) + endif() +endif(ENABLE_ALEXAEXTENSIONS) include("${CMAKE_CURRENT_LIST_DIR}/aplcoreTargets.cmake") diff --git a/aplcore/include/apl/animation/easing.h b/aplcore/include/apl/animation/easing.h index 2ac3c47..a6631e9 100644 --- a/aplcore/include/apl/animation/easing.h +++ b/aplcore/include/apl/animation/easing.h @@ -17,7 +17,7 @@ #ifndef _APL_EASING_H #define _APL_EASING_H -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -67,6 +67,19 @@ class Easing : public ObjectData { virtual bool operator==(const Easing& other) const = 0; virtual bool operator==(const CoreEasing& other) const = 0; virtual ~Easing() noexcept; + + class ObjectType final : public PointerHolderObjectType { + public: + bool isCallable() const override { return true; } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *lhs.data == *rhs.data; + } + + Object call(const Object::DataHolder& dataHolder, const ObjectArray& args) const override { + return dataHolder.data->call(args); + } + }; }; } // namespace apl diff --git a/aplcore/include/apl/animation/easinggrammar.h b/aplcore/include/apl/animation/easinggrammar.h index 2192eaa..cf60e5d 100644 --- a/aplcore/include/apl/animation/easinggrammar.h +++ b/aplcore/include/apl/animation/easinggrammar.h @@ -16,10 +16,8 @@ #ifndef _APL_EASING_GRAMMAR_H #define _APL_EASING_GRAMMAR_H -#include -#include - #include "apl/animation/coreeasing.h" +#include "apl/datagrammar/grammarpolyfill.h" #include "apl/utils/log.h" #include "apl/utils/stringfunctions.h" @@ -86,7 +84,7 @@ struct action : nothing< Rule > { }; -struct easing_state +struct easing_state : fail_state { float lastTime = 0; size_t startIndex = 0; @@ -118,14 +116,18 @@ template<> struct action< path > template< typename Input > static void apply(const Input& in, easing_state& state) { auto argCount = state.args.size() - state.startIndex; - if (argCount % 2 == 1) - throw parse_error("Path easing function needs an even number of arguments", in); + if (argCount % 2 == 1) { + state.fail("Path easing function needs an even number of arguments", in); + return; + } // Push each linear segment. Check to ensure time is incrementing for (auto offset = state.startIndex ; offset < state.args.size() ; offset += 2) { auto time = state.args.at(offset); - if (time <= state.lastTime || time >= 1) - throw parse_error("Path easing function needs ordered array of segments", in); + if (time <= state.lastTime || time >= 1) { + state.fail("Path easing function needs ordered array of segments", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kLinearSegment, offset)); } @@ -153,8 +155,10 @@ template<> struct action< cubicbezier > template< typename Input > static void apply(const Input& in, easing_state& state) { auto argCount = state.args.size() - state.startIndex; - if (argCount != 4) - throw parse_error("Cubic-bezier easing function requires 4 arguments", in); + if (argCount != 4) { + state.fail("Cubic-bezier easing function requires 4 arguments", in); + return; + } // Add a final segment at (1,1) state.segments.emplace_back(EasingSegment(kEndSegment, state.args.size())); @@ -176,12 +180,16 @@ template<> struct action< end > template< typename Input > static void apply(const Input& in, easing_state& state) { auto argCount = state.args.size() - state.startIndex; - if (argCount != 2) - throw parse_error("End easing function segment requires 2 arguments", in); + if (argCount != 2) { + state.fail("End easing function segment requires 2 arguments", in); + return; + } auto time = state.args.at(state.startIndex); - if (time <= state.lastTime && state.startIndex > 0) - throw parse_error("End easing function segment cannot start at this time", in); + if (time <= state.lastTime && state.startIndex > 0) { + state.fail("End easing function segment cannot start at this time", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kEndSegment, state.startIndex)); @@ -201,12 +209,16 @@ template<> struct action< line > template< typename Input > static void apply(const Input& in, easing_state& state) { auto argCount = state.args.size() - state.startIndex; - if (argCount != 2) - throw parse_error("Line easing function segment requires 2 arguments", in); + if (argCount != 2) { + state.fail("Line easing function segment requires 2 arguments", in); + return; + } auto time = state.args.at(state.startIndex); - if (time <= state.lastTime && state.startIndex > 0) - throw parse_error("Line easing function segment cannot start at this time", in); + if (time <= state.lastTime && state.startIndex > 0) { + state.fail("Line easing function segment cannot start at this time", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kLinearSegment, state.startIndex)); @@ -226,12 +238,16 @@ template<> struct action< curve > template< typename Input > static void apply(const Input& in, easing_state& state) { auto argCount = state.args.size() - state.startIndex; - if (argCount != 6) - throw parse_error("Curve easing function segment requires 6 arguments", in); + if (argCount != 6) { + state.fail("Curve easing function segment requires 6 arguments", in); + return; + } auto time = state.args.at(state.startIndex); - if (time <= state.lastTime && state.startIndex > 0) - throw parse_error("Curve easing function segment cannot start at this time", in); + if (time <= state.lastTime && state.startIndex > 0) { + state.fail("Curve easing function segment cannot start at this time", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kCurveSegment, state.startIndex)); @@ -246,16 +262,22 @@ template<> struct action< spatial > assert(state.startIndex == 0); auto argCount = state.args.size(); - if (argCount != 2) - throw parse_error("Wrong number of arguments to spatial", in); + if (argCount != 2) { + state.fail("Wrong number of arguments to spatial", in); + return; + } auto dof = static_cast(state.args.at(0)); - if (dof < 2) - throw parse_error("invalid number of indices in spatial segment", in); + if (dof < 2) { + state.fail("invalid number of indices in spatial segment", in); + return; + } auto index = static_cast(state.args.at(1)); - if (index < 0 || index >= dof) - throw parse_error("select index out of range in spatial segment", in); + if (index < 0 || index >= dof) { + state.fail("select index out of range in spatial segment", in); + return; + } } }; @@ -274,13 +296,17 @@ template<> struct action< send > auto argCount = state.args.size() - state.startIndex; // Time, pcount for value - auto dof = ::abs(static_cast(state.args[0])); - if (argCount != 1 + dof) - throw parse_error("Wrong number of arguments to send", in); + auto dof = std::abs(static_cast(state.args[0])); + if (argCount != 1 + dof) { + state.fail("Wrong number of arguments to send", in); + return; + } auto time = state.args.at(state.startIndex); - if (time <= state.lastTime && !state.segments.empty()) - throw parse_error("send easing function segment cannot start at this time", in); + if (time <= state.lastTime && !state.segments.empty()) { + state.fail("send easing function segment cannot start at this time", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kSEndSegment, state.startIndex)); @@ -302,13 +328,17 @@ template<> struct action< scurve > auto argCount = state.args.size() - state.startIndex; // Time, pcount * 3 for value, tin, tout, 4 for the time curve = - auto dof = ::abs(static_cast(state.args[0])); - if (argCount != 5 + dof * 3) - throw parse_error("Wrong number of arguments to scurve", in); + auto dof = std::abs(static_cast(state.args[0])); + if (argCount != 5 + dof * 3) { + state.fail("Wrong number of arguments to scurve", in); + return; + } auto time = state.args.at(state.startIndex); - if (time <= state.lastTime && !state.segments.empty()) - throw parse_error("scurve easing function segment cannot start at this time", in); + if (time <= state.lastTime && !state.segments.empty()) { + state.fail("scurve easing function segment cannot start at this time", in); + return; + } state.lastTime = time; state.segments.emplace_back(EasingSegment(kSCurveSegment, state.startIndex)); diff --git a/aplcore/include/apl/apl.h b/aplcore/include/apl/apl.h index 1d4e032..18bd5e0 100644 --- a/aplcore/include/apl/apl.h +++ b/aplcore/include/apl/apl.h @@ -67,6 +67,7 @@ #include "apl/primitives/range.h" #include "apl/primitives/roundedrect.h" #include "apl/primitives/styledtext.h" +#include "apl/primitives/transform2d.h" #include "apl/scaling/metricstransform.h" #ifdef SCENEGRAPH #include "apl/scenegraph/accessibility.h" diff --git a/aplcore/include/apl/colorgrammar/colorgrammar.h b/aplcore/include/apl/colorgrammar/colorgrammar.h index 9c1b413..cb261fa 100644 --- a/aplcore/include/apl/colorgrammar/colorgrammar.h +++ b/aplcore/include/apl/colorgrammar/colorgrammar.h @@ -27,6 +27,8 @@ #include "apl/primitives/color.h" #include "colorfunctions.h" +#include "apl/datagrammar/grammarpolyfill.h" + #include "apl/utils/log.h" #include "apl/utils/stringfunctions.h" @@ -35,7 +37,7 @@ namespace colorgrammar { namespace pegtl = tao::TAO_PEGTL_NAMESPACE; using namespace pegtl; - static bool DEBUG_GRAMMAR = false; + static const bool DEBUG_GRAMMAR = false; /** * \cond ShowColorGrammar @@ -65,7 +67,7 @@ namespace colorgrammar { : pegtl::nothing< Rule > { }; - struct color_state + struct color_state : fail_state { std::stack mStack; @@ -74,14 +76,12 @@ namespace colorgrammar { uint32_t getColor() {return (uint32_t) mStack.top();} }; - /* - * The first argument to a function call pushes a sentinel on the stack (the value -1). - */ - template<> struct action< firstarg > + // Push a sentinel on the stack with the opening parenthesis + template<> struct action< one<'('> > { template< typename Input > static void apply(const Input& in, color_state& state) { - LOGF_IF(DEBUG_GRAMMAR, "Firstarg: '%s'", in.string().c_str()); + if (state.failed) return; state.push(-1); } }; @@ -93,10 +93,13 @@ namespace colorgrammar { { template< typename Input > static void apply( const Input& in, color_state& state ) { + if (state.failed) return; uint32_t color; - if (!colorFromHex(in.string(), color)) - throw parse_error("Invalid hexidecimal color", in); + if (!colorFromHex(in.string(), color)) { + state.fail("Invalid hexidecimal color", in); + return; + } LOGF_IF(DEBUG_GRAMMAR, "Hexidecimal: '%s' -> %08x", in.string().c_str(), color); state.push(color); @@ -111,6 +114,7 @@ namespace colorgrammar { { template< typename Input > static void apply( const Input& in, color_state& state) { + if (state.failed) return; std::string s(in.string()); double value; @@ -133,9 +137,14 @@ namespace colorgrammar { { template< typename Input > static void apply(const Input& in, color_state& state) { + if (state.failed) return; + auto result = Color::lookup(in.string()); if (!result.first) { - throw parse_error((std::string("Invalid named color: '") + in.string()).c_str() + std::string("'"), in); + state.fail( + std::string("Invalid named color: '") + in.string() + std::string("'"), + in); + return; } LOGF_IF(DEBUG_GRAMMAR, "Color map: '%s'", in.string().c_str()); @@ -155,21 +164,24 @@ namespace colorgrammar { { template< typename Input > static void apply( const Input& in, color_state& state) { + if (state.failed) return; + double v; double args[4]; int argc = 0; while ((v = state.pop()) != -1) { - if (argc >= 3) - throw parse_error("too many arguments in an hsl function", in); + if (argc > 3) { + state.fail("too many arguments in an hsl function", in); + return; + } args[argc++] = v; } - // Grab the last argument (it was behind the sentinel) - args[argc++] = state.pop(); - - if (argc < 3) - throw parse_error("expected at least three arguments for an hsl function", in); + if (argc < 3) { + state.fail("expected at least three arguments for an hsl function", in); + return; + } if (argc == 3) state.push(colorFromHSL(args[2], args[1], args[0])); @@ -189,21 +201,24 @@ namespace colorgrammar { { template< typename Input > static void apply( const Input& in, color_state& state) { + if (state.failed) return; + double v; double args[4]; int argc = 0; while ((v = state.pop()) != -1) { - if (argc >= 3) - throw parse_error("too many arguments in a color function", in); + if (argc > 3) { + state.fail("too many arguments in a color function", in); + return; + } args[argc++] = v; } - // Grab the last argument (it was behind the sentinel) - args[argc++] = state.pop(); - - if (argc < 2) - throw parse_error("expected at least two arguments for a color function", in); + if (argc < 2) { + state.fail("expected at least two arguments for a color function", in); + return; + } if (argc == 2) state.push(applyAlpha(args[1], args[0])); @@ -217,20 +232,19 @@ namespace colorgrammar { // ****************** Error messages ******************* template< typename Rule > - struct errors - : public pegtl::normal< Rule > + struct c_control : public apl_control< Rule > { static const std::string error_message; template< typename Input, typename ... States > - static void raise( const Input & in, States && ... ) + static void raise( const Input & in, States &&... st) { - throw pegtl::parse_error( error_message, in ); + apl_control::fail(in, st...); } }; - template<> const std::string errors< digits >::error_message = "expected at least one digit"; - template<> const std::string errors< pegtl::eof >::error_message = "unexpected end"; + template<> const std::string c_control< digits >::error_message = "expected at least one digit"; + template<> const std::string c_control< pegtl::eof >::error_message = "unexpected end"; /** * \endcond diff --git a/aplcore/include/apl/component/actionablecomponent.h b/aplcore/include/apl/component/actionablecomponent.h index 02e7fda..83bef26 100644 --- a/aplcore/include/apl/component/actionablecomponent.h +++ b/aplcore/include/apl/component/actionablecomponent.h @@ -56,6 +56,12 @@ class ActionableComponent : public CoreComponent { */ virtual bool isVertical() const { return false; } + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + /// CoreComponent overrides bool isActionable() const override { return true; } bool canConsumeFocusDirectionEvent(FocusDirection direction, bool fromInside) override { return !fromInside; } @@ -69,7 +75,7 @@ class ActionableComponent : public CoreComponent { bool executeKeyHandlers(KeyHandlerType type, const Keyboard& keyboard) override; bool executeIntrinsicKeyHandlers(KeyHandlerType type, const Keyboard& keyboard) override; const ComponentPropDefSet& propDefSet() const override; - void release() override; + void releaseSelf() override; ActionableComponent(const ContextPtr& context, Properties&& properties, const Path& path) : CoreComponent(context, std::move(properties), path), mGesturesDisabled(false) {} diff --git a/aplcore/include/apl/component/component.h b/aplcore/include/apl/component/component.h index 517c527..0a644d7 100644 --- a/aplcore/include/apl/component/component.h +++ b/aplcore/include/apl/component/component.h @@ -322,14 +322,14 @@ class Component : public UIDObject, * p.opacity *= opacity; * * // Apply clip bounds, exit if nothing visible - * auto bounds = getCalculated(kPropertyBounds).getRect(); + * const auto& bounds = getCalculated(kPropertyBounds).get(); * p.addClipping( bounds ); * if (p.clipRegionEmpty()) * return; * * // Transform to the local coordinate space * p.translate( bounds.getTopLeft() ); - * auto transform = getCalculated(kPropertyTransform).getTransform2D(); + * auto transform = getCalculated(kPropertyTransform).get(); * p.applyTransform( c.getTransform() ); * * // Draw self, then children @@ -502,6 +502,11 @@ class Component : public UIDObject, */ virtual bool isAccessible() const { return false; } + /** + * @return MediaPlayer attached to this component, if any. + */ + virtual MediaPlayerPtr getMediaPlayer() const { return nullptr; } + protected: Component(const ContextPtr& context, const std::string& id); diff --git a/aplcore/include/apl/component/componenteventwrapper.h b/aplcore/include/apl/component/componenteventwrapper.h index c6a697a..a7a751e 100644 --- a/aplcore/include/apl/component/componenteventwrapper.h +++ b/aplcore/include/apl/component/componenteventwrapper.h @@ -17,7 +17,7 @@ #define _APL_COMPONENT_EVENT_WRAPPER_H #include "apl/common.h" -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -48,6 +48,25 @@ class ComponentEventWrapper : public ObjectData { virtual bool operator==(const ComponentEventSourceWrapper& rhs) const { return false; } virtual bool operator==(const ComponentEventTargetWrapper& rhs) const { return false; } + class ObjectType final : public MapLikeObjectType { + public: + bool truthy(const Object::DataHolder& dataHolder) const override { + return std::static_pointer_cast(dataHolder.data)->getComponent() != nullptr; + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return std::static_pointer_cast(dataHolder.data)->serialize(allocator); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *std::static_pointer_cast(lhs.data) == + *std::static_pointer_cast(rhs.data); + } + }; + protected: std::weak_ptr mComponent; }; diff --git a/aplcore/include/apl/component/corecomponent.h b/aplcore/include/apl/component/corecomponent.h index 9d3e2f9..b38b38e 100644 --- a/aplcore/include/apl/component/corecomponent.h +++ b/aplcore/include/apl/component/corecomponent.h @@ -32,6 +32,7 @@ #include "apl/focus/focusdirection.h" #include "apl/primitives/keyboard.h" #include "apl/primitives/size.h" +#include "apl/primitives/transform2d.h" namespace apl { @@ -100,7 +101,7 @@ class CoreComponent : public Component, /** * Release this component and all children. This component may still be in - * its parents child list. + * its parent's child list. */ void release() override; @@ -933,6 +934,12 @@ class CoreComponent : public Component, */ void setStickyOffset(Point stickyOffset) { mStickyOffset = stickyOffset; } + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + #ifdef SCENEGRAPH /** * @return The current scene graph node. @@ -948,7 +955,6 @@ class CoreComponent : public Component, protected: // internal, do not call directly virtual bool insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag); - virtual void removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag); virtual void reportLoaded(size_t index); // Attach the yoga node of this child @@ -1015,6 +1021,31 @@ class CoreComponent : public Component, */ void fixVisualHash(bool useDirtyFlag); + /** + * Traverse the component hierarchy rooted at this component, invoking pre on each component + * before traversing all children, and post on each component after traversing all children. + * @param pre pre-order traversal function accepting CoreComponent& + * @param post post-order traversal function accepting CoreComponent& + */ + template + void traverse(const Pre& pre, const Post& post); + + /** + * Traverse the component hierarchy rooted at this component, invoking pre on each component + * before traversing each child. + * @param pre pre-order traversal function accepting CoreComponent& + */ + template + void traverse(const Pre& pre) { traverse(pre, [](CoreComponent& c) {}); }; + + /** + * Release this component. This component may still be in its parent's child list. This does + * not release children of this component, nor does it clear this component's list of children. + */ + virtual void releaseSelf(); + + virtual void removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag); + #ifdef SCENEGRAPH /* * Used by getSceneGraph() to build the component's scene graph (and attached children). @@ -1068,7 +1099,7 @@ class CoreComponent : public Component, void removeChildAt(size_t index, bool useDirtyFlag); - void markRemoved(); + void markSelfRemoved(); void markAdded(); diff --git a/aplcore/include/apl/component/imagecomponent.h b/aplcore/include/apl/component/imagecomponent.h index e7aac43..3ce4f1f 100644 --- a/aplcore/include/apl/component/imagecomponent.h +++ b/aplcore/include/apl/component/imagecomponent.h @@ -31,14 +31,14 @@ class ImageComponent : public CoreComponent, void postProcessLayoutChanges() override; - void release() override; - protected: const EventPropertyMap& eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; std::string getVisualContextType() const override; + void releaseSelf() override; + /// Media component trait overrides std::vector getSources() override; EventMediaType mediaType() const override { return kEventMediaTypeImage; } diff --git a/aplcore/include/apl/component/multichildscrollablecomponent.h b/aplcore/include/apl/component/multichildscrollablecomponent.h index 15c13f2..09e5e1b 100644 --- a/aplcore/include/apl/component/multichildscrollablecomponent.h +++ b/aplcore/include/apl/component/multichildscrollablecomponent.h @@ -113,7 +113,7 @@ class MultiChildScrollableComponent : public ScrollableComponent { const ComponentPropDefSet& propDefSet() const override; std::map getChildrenVisibility(float realOpacity, const Rect &visibleRect) const override; bool insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; - void removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; + void removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; bool getTags(rapidjson::Value& outMap, rapidjson::Document::AllocatorType& allocator) override; virtual void layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, bool useDirtyFlag, bool first); void relayoutInPlace(bool useDirtyFlag, bool first); diff --git a/aplcore/include/apl/component/pagercomponent.h b/aplcore/include/apl/component/pagercomponent.h index 22b424d..96d6283 100644 --- a/aplcore/include/apl/component/pagercomponent.h +++ b/aplcore/include/apl/component/pagercomponent.h @@ -32,6 +32,12 @@ class PagerComponent : public ActionableComponent { ComponentType getType() const override { return kComponentTypePager; }; + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + /// Component overrides. void initialize() override; void update(UpdateType type, float value) override; @@ -44,7 +50,6 @@ class PagerComponent : public ActionableComponent { void processLayoutChanges(bool useDirtyFlag, bool first) override; bool allowForward() const override; bool allowBackwards() const override; - void release() override; /// Actionable overrides bool isHorizontal() const override { return scrollType() == kScrollTypeHorizontalPager; } @@ -94,10 +99,11 @@ class PagerComponent : public ActionableComponent { void accept(Visitor& visitor) const override; void raccept(Visitor& visitor) const override; bool insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; - void removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; + void removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; bool shouldAttachChildYogaNode(int index) const override; void finalizePopulate() override; void ensureDisplayedChildren() override; + void releaseSelf() override; private: bool multiChild() const override { return true; } diff --git a/aplcore/include/apl/component/scrollablecomponent.h b/aplcore/include/apl/component/scrollablecomponent.h index 17e46a7..5d8aae5 100644 --- a/aplcore/include/apl/component/scrollablecomponent.h +++ b/aplcore/include/apl/component/scrollablecomponent.h @@ -36,6 +36,12 @@ class ScrollableComponent : public ActionableComponent { */ virtual bool shouldForceSnap() const { return false; } + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + void update(UpdateType type, float value) override; bool canConsumeFocusDirectionEvent(FocusDirection direction, bool fromInside) override; CoreComponentPtr takeFocusFromChild(FocusDirection direction, const Rect& origin) override; diff --git a/aplcore/include/apl/component/textmeasurement.h b/aplcore/include/apl/component/textmeasurement.h index c399f8d..be838b7 100644 --- a/aplcore/include/apl/component/textmeasurement.h +++ b/aplcore/include/apl/component/textmeasurement.h @@ -18,6 +18,7 @@ #include +#include "apl/apl_config.h" #include "apl/common.h" namespace apl { @@ -106,6 +107,10 @@ class TextMeasurement { virtual float baseline( Component *component, float width, float height ) = 0; + +#ifdef SCENEGRAPH + virtual bool sceneGraphCompatible() const { return false; } +#endif // SCENEGRAPH }; } // namespace apl diff --git a/aplcore/include/apl/component/touchwrappercomponent.h b/aplcore/include/apl/component/touchwrappercomponent.h index d03fb2f..5f6a504 100644 --- a/aplcore/include/apl/component/touchwrappercomponent.h +++ b/aplcore/include/apl/component/touchwrappercomponent.h @@ -31,6 +31,12 @@ class TouchWrapperComponent : public TouchableComponent { Object getValue() const override { return mState.get(kStateChecked); } + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + /** * Inject component that will replace current child of touch wrapper. While likely short lived - it will exist for * customer interaction time + any animations left. diff --git a/aplcore/include/apl/component/vectorgraphiccomponent.h b/aplcore/include/apl/component/vectorgraphiccomponent.h index c2c88e9..2aafd69 100644 --- a/aplcore/include/apl/component/vectorgraphiccomponent.h +++ b/aplcore/include/apl/component/vectorgraphiccomponent.h @@ -26,7 +26,6 @@ class VectorGraphicComponent : public TouchableComponent, public: static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); VectorGraphicComponent(const ContextPtr& context, Properties&& properties, const Path& path); - void release() override; ComponentType getType() const override { return kComponentTypeVectorGraphic; }; void initialize() override; @@ -44,8 +43,8 @@ class VectorGraphicComponent : public TouchableComponent, const EventPropertyMap& eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; void processLayoutChanges(bool useDirtyFlag, bool first) override; + void releaseSelf() override; -protected: std::string getVisualContextType() const override; bool setPropertyInternal(const std::string& key, const Object& value) override; std::pair getPropertyInternal(const std::string& key) const override; @@ -59,6 +58,11 @@ class VectorGraphicComponent : public TouchableComponent, // Component trait overrides CoreComponentPtr getComponent() override { return shared_from_corecomponent(); } + // Release any existing vector graphic + void releaseGraphic(); + // The graphic source has changed + void sourcePropertyChanged(); + #ifdef SCENEGRAPH // Common scene graph handling sg::LayerPtr constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) override; @@ -68,6 +72,7 @@ class VectorGraphicComponent : public TouchableComponent, private: bool mOnLoadOnFailReported = false; + bool mGraphicReplaced = false; }; diff --git a/aplcore/include/apl/component/videocomponent.h b/aplcore/include/apl/component/videocomponent.h index 3414f71..76e1fa4 100644 --- a/aplcore/include/apl/component/videocomponent.h +++ b/aplcore/include/apl/component/videocomponent.h @@ -38,7 +38,7 @@ class VideoComponent : public CoreComponent { std::string getCurrentUrl() const; - MediaPlayerPtr getMediaPlayer() const { return mMediaPlayer; } + MediaPlayerPtr getMediaPlayer() const override { return mMediaPlayer; } /** * Detach media player from the component. @@ -51,6 +51,12 @@ class VideoComponent : public CoreComponent { */ void attachPlayer(const MediaPlayerPtr& player); + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + protected: const EventPropertyMap & eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; diff --git a/aplcore/include/apl/content/extensioncommanddefinition.h b/aplcore/include/apl/content/extensioncommanddefinition.h index c931984..49e9fc5 100644 --- a/aplcore/include/apl/content/extensioncommanddefinition.h +++ b/aplcore/include/apl/content/extensioncommanddefinition.h @@ -18,6 +18,8 @@ #include "apl/content/extensionproperty.h" +#include "apl/utils/log.h" + namespace apl { /** diff --git a/aplcore/include/apl/content/rootconfig.h b/aplcore/include/apl/content/rootconfig.h index b10c158..a64daae 100644 --- a/aplcore/include/apl/content/rootconfig.h +++ b/aplcore/include/apl/content/rootconfig.h @@ -1245,9 +1245,7 @@ class RootConfig { /** * @return Animation easing for SwipeAway gesture. */ - EasingPtr getSwipeAwayAnimationEasing() const { - return getProperty(RootProperty::kSwipeAwayAnimationEasing).getEasing(); - } + EasingPtr getSwipeAwayAnimationEasing() const; /** * @return Swipe velocity threshold. diff --git a/aplcore/include/apl/datagrammar/boundsymbol.h b/aplcore/include/apl/datagrammar/boundsymbol.h index 81af173..df2c763 100644 --- a/aplcore/include/apl/datagrammar/boundsymbol.h +++ b/aplcore/include/apl/datagrammar/boundsymbol.h @@ -16,7 +16,7 @@ #ifndef _APL_BOUND_SYMBOL_H #define _APL_BOUND_SYMBOL_H -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" #include "apl/primitives/symbolreferencemap.h" namespace apl { @@ -48,6 +48,16 @@ class BoundSymbol : public ObjectData friend streamer& operator<<(streamer&, const BoundSymbol&); + class ObjectType final : public EvaluableObjectType { + public: + rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value("BOUND SYMBOL", allocator); + } + }; + private: std::weak_ptr mContext; std::string mName; diff --git a/aplcore/include/apl/datagrammar/bytecode.h b/aplcore/include/apl/datagrammar/bytecode.h index bebb9ea..e4dc08a 100644 --- a/aplcore/include/apl/datagrammar/bytecode.h +++ b/aplcore/include/apl/datagrammar/bytecode.h @@ -19,7 +19,7 @@ #include #include "apl/datagrammar/functions.h" -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -234,6 +234,16 @@ class ByteCode : public ObjectData, public std::enable_shared_from_this { + public: + rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value("COMPILED BYTE CODE", allocator); + } + }; + private: std::weak_ptr mContext; std::vector mInstructions; diff --git a/aplcore/include/apl/datagrammar/databindingerrors.h b/aplcore/include/apl/datagrammar/databindingerrors.h index 34b9d5b..226b0d2 100644 --- a/aplcore/include/apl/datagrammar/databindingerrors.h +++ b/aplcore/include/apl/datagrammar/databindingerrors.h @@ -32,12 +32,17 @@ namespace datagrammar { * @tparam Rule The base type for the rules */ template< typename Rule > -struct error_control : tao::pegtl::normal< Rule > { +struct error_control : apl_control< Rule > { static const GrammarError error_value; + template< typename Input, typename... States> + static void fail( const Input& in, fail_state& failState, States&&...) { + failState.fail(errorToString(error_value), in); + } + template - static void raise(const Input &in, States &&...) { - throw tao::pegtl::parse_error(errorToString(error_value), in); + static void raise(const Input &in, States &&... st) { + fail(in, st...); } }; diff --git a/aplcore/include/apl/datagrammar/databindinggrammar.h b/aplcore/include/apl/datagrammar/databindinggrammar.h index 34cff26..08d2eb1 100644 --- a/aplcore/include/apl/datagrammar/databindinggrammar.h +++ b/aplcore/include/apl/datagrammar/databindinggrammar.h @@ -34,6 +34,8 @@ #include #include +#include "apl/datagrammar/grammarpolyfill.h" + namespace apl { namespace datagrammar { diff --git a/aplcore/include/apl/datagrammar/databindingrules.h b/aplcore/include/apl/datagrammar/databindingrules.h index 1135c1f..c79e289 100644 --- a/aplcore/include/apl/datagrammar/databindingrules.h +++ b/aplcore/include/apl/datagrammar/databindingrules.h @@ -24,6 +24,7 @@ #include "apl/primitives/object.h" +#include "apl/primitives/dimension.h" #include "apl/utils/stringfunctions.h" #ifdef APL_CORE_UWP @@ -53,7 +54,8 @@ struct action : nothing< Rule > template<> struct action< number > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; double value = sutil::stod(in.string()); if (fitsInBCI(value)) assembler.loadImmediate(asBCI(value)); @@ -65,7 +67,8 @@ template<> struct action< number > template<> struct action< key_null > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.loadConstant(BC_CONSTANT_NULL); } }; @@ -73,7 +76,8 @@ template<> struct action< key_null > template<> struct action< key_true > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.loadConstant(BC_CONSTANT_TRUE); } }; @@ -81,7 +85,8 @@ template<> struct action< key_true > template<> struct action< key_false > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.loadConstant(BC_CONSTANT_FALSE); } }; @@ -91,7 +96,8 @@ template<> struct action< key_false > template<> struct action< dimension > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.loadOperand(Object(Dimension(*(assembler.context()), in.string()))); } }; @@ -100,7 +106,8 @@ template<> struct action< dimension > template<> struct action< sym_unary > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushUnaryOperator(in.string()[0]); } }; @@ -108,7 +115,8 @@ template<> struct action< sym_unary > template<> struct action< unary_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceUnary(); } }; @@ -116,7 +124,8 @@ template<> struct action< unary_expression > // ************* Multiplication, division, modulus ************* template<> struct action< sym_multiplicative > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushBinaryOperator(in.string()); } }; @@ -124,7 +133,8 @@ template<> struct action< sym_multiplicative > { template<> struct action< multiplicative_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceBinary(BC_ORDER_MULTIPLICATIVE); } }; @@ -132,7 +142,8 @@ template<> struct action< multiplicative_expression > // ************* Addition, subtraction ************* template<> struct action< sym_additive > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushBinaryOperator(in.string()); } }; @@ -140,7 +151,8 @@ template<> struct action< sym_additive > { template<> struct action< additive_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceBinary(BC_ORDER_ADDITIVE); } }; @@ -148,7 +160,8 @@ template<> struct action< additive_expression > // ************* Comparison ************* template<> struct action< sym_compare > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushBinaryOperator(in.string()); } }; @@ -156,7 +169,8 @@ template<> struct action< sym_compare > { template<> struct action< comparison_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceBinary(BC_ORDER_COMPARISON); } }; @@ -164,7 +178,8 @@ template<> struct action< comparison_expression > // ************* Equality ************* template<> struct action< sym_equal > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushBinaryOperator(in.string()); } }; @@ -172,7 +187,8 @@ template<> struct action< sym_equal > { template<> struct action< equality_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceBinary(BC_ORDER_EQUALITY); } }; @@ -180,7 +196,8 @@ template<> struct action< equality_expression > // ************* Logical AND ************* template<> struct action< sym_and > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushAnd(); } }; @@ -188,7 +205,8 @@ template<> struct action< sym_and > { template<> struct action< logical_and_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceJumps(BC_ORDER_LOGICAL_AND); } }; @@ -196,7 +214,8 @@ template<> struct action< logical_and_expression > // ************* Logical OR ************* template<> struct action< sym_or > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushOr(); } }; @@ -204,7 +223,8 @@ template<> struct action< sym_or > { template<> struct action< logical_or_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceJumps(BC_ORDER_LOGICAL_OR); } }; @@ -212,7 +232,8 @@ template<> struct action< logical_or_expression > // ************* Null coalescence ************* template<> struct action< sym_nullc > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.pushNullC(); } }; @@ -220,7 +241,8 @@ template<> struct action< sym_nullc > { template<> struct action< nullc_expression > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceJumps(BC_ORDER_NULLC); } }; @@ -229,7 +251,8 @@ template<> struct action< nullc_expression > template<> struct action < sym_question > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushTernaryIf(); } }; @@ -237,7 +260,8 @@ template<> struct action < sym_question > template<> struct action < sym_colon > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushTernaryElse(); } }; @@ -245,7 +269,8 @@ template<> struct action < sym_colon > template<> struct action< ternary_tail > { template< typename Input > - static void apply(const Input& in, ByteCodeAssembler& assembler) { + static void apply(const Input& in, fail_state& failState, ByteCodeAssembler& assembler) { + if (failState.failed) return; assembler.reduceOneJump(BC_ORDER_TERNARY_ELSE); } }; @@ -254,7 +279,8 @@ template<> struct action< ternary_tail > template<> struct action< group_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushGroup(); } }; @@ -263,7 +289,8 @@ template<> struct action< group_start > template<> struct action< grouping > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.popGroup(); } }; @@ -272,7 +299,8 @@ template<> struct action< grouping > template<> struct action< resource > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.loadGlobal(in.string()); // TODO: Should this be treated differently? } }; @@ -281,7 +309,8 @@ template<> struct action< resource > template<> struct action< plain_symbol > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.loadGlobal(in.string()); } }; @@ -290,7 +319,8 @@ template<> struct action< plain_symbol > template<> struct action< array_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushInlineArrayStart(); } }; @@ -298,7 +328,8 @@ template<> struct action< array_start > template<> struct action< array_end > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushInlineArrayEnd(); } }; @@ -306,7 +337,8 @@ template<> struct action< array_end > template<> struct action< array_comma > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.appendInlineArrayArgument(); } }; @@ -314,7 +346,8 @@ template<> struct action< array_comma > template<> struct action< array_list > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.appendInlineArrayArgument(); // Insert a fake comma } }; @@ -323,7 +356,8 @@ template<> struct action< array_list > template<> struct action< map_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushInlineMapStart(); } }; @@ -331,7 +365,8 @@ template<> struct action< map_start > template<> struct action< map_end > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushInlineMapEnd(); } }; @@ -339,7 +374,8 @@ template<> struct action< map_end > template<> struct action< map_comma > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.appendInlineMapArgument(); } }; @@ -347,7 +383,8 @@ template<> struct action< map_comma > template<> struct action< map_list > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.appendInlineMapArgument(); // Insert a fake comma } }; @@ -356,7 +393,8 @@ template<> struct action< map_list > template<> struct action< postfix_identifier > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushAttributeName(in.string()); assembler.loadAttribute(); } @@ -366,7 +404,8 @@ template<> struct action< postfix_identifier > template<> struct action< sym_array_access_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushArrayAccessStart(); } }; @@ -374,7 +413,8 @@ template<> struct action< sym_array_access_start > template<> struct action< postfix_array_access > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushArrayAccessEnd(); } }; @@ -383,7 +423,8 @@ template<> struct action< postfix_array_access > template<> struct action { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushFunctionStart(); } }; @@ -391,7 +432,8 @@ template<> struct action template<> struct action< sym_comma > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushComma(); } }; @@ -399,7 +441,8 @@ template<> struct action< sym_comma > template<> struct action< argument_list > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushComma(); // Insert a fake comma } }; @@ -407,7 +450,8 @@ template<> struct action< argument_list > template<> struct action< postfix_right_paren > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushFunctionEnd(); } }; @@ -417,7 +461,8 @@ template<> struct action< postfix_right_paren > template<> struct action< sym_dbstart > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.pushDBGroup(); } }; @@ -425,7 +470,8 @@ template<> struct action< sym_dbstart > template<> struct action { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler ) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.loadConstant(BC_CONSTANT_EMPTY_STRING); } }; @@ -433,7 +479,8 @@ template<> struct action template<> struct action< db > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.popDBGroup(); } }; @@ -443,7 +490,8 @@ template<> struct action< db > template<> struct action< ds_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.startString(); } }; @@ -451,7 +499,8 @@ template<> struct action< ds_start > template<> struct action< ss_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.startString(); } }; @@ -459,7 +508,8 @@ template<> struct action< ss_start > template<> struct action< os_start > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.startString(); } }; @@ -467,7 +517,8 @@ template<> struct action< os_start > template<> struct action< ss_raw > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; auto s = in.string(); if (s.length() > 0) assembler.addString(s); @@ -477,7 +528,8 @@ template<> struct action< ss_raw > template<> struct action< ds_raw > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; auto s = in.string(); if (s.length() > 0) assembler.addString(s); @@ -487,7 +539,8 @@ template<> struct action< ds_raw > template<> struct action< os_raw > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; auto s = in.string(); if (s.length() > 0) assembler.addString(s); @@ -498,7 +551,8 @@ template<> struct action< os_raw > template<> struct action< os_string > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.endString(); } }; @@ -506,7 +560,8 @@ template<> struct action< os_string > template<> struct action< ss_string > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.endString(); } }; @@ -514,7 +569,8 @@ template<> struct action< ss_string > template<> struct action< ds_string > { template< typename Input > - static void apply( const Input& in, ByteCodeAssembler& assembler) { + static void apply( const Input& in, fail_state& failState, ByteCodeAssembler& assembler ) { + if (failState.failed) return; assembler.endString(); } }; diff --git a/aplcore/include/apl/datagrammar/databindingstack.h b/aplcore/include/apl/datagrammar/databindingstack.h index 4526272..d5ab05b 100644 --- a/aplcore/include/apl/datagrammar/databindingstack.h +++ b/aplcore/include/apl/datagrammar/databindingstack.h @@ -27,6 +27,7 @@ #include "apl/primitives/object.h" #include "apl/utils/log.h" #include "apl/utils/streamer.h" +#include "apl/utils/throw.h" namespace apl { namespace datagrammar { @@ -237,7 +238,7 @@ class Stack { return mObjects.back(); } - throw std::runtime_error("Illegal combination"); + aplThrow("Illegal combination"); } void dump() diff --git a/aplcore/include/apl/datagrammar/grammarpolyfill.h b/aplcore/include/apl/datagrammar/grammarpolyfill.h new file mode 100644 index 0000000..3137c7b --- /dev/null +++ b/aplcore/include/apl/datagrammar/grammarpolyfill.h @@ -0,0 +1,178 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0/ +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifndef _APL_GRAMMAR_POLYFILL_H +#define _APL_GRAMMAR_POLYFILL_H + +#include +#include + +#include +#include +#include + +namespace apl { + +namespace pegtl = tao::TAO_PEGTL_NAMESPACE; +using namespace pegtl; + +/// Pretty much structure of pegtl::parse_error, without exceptions involved +/// see: https://github.com/taocpp/PEGTL/blob/main/include/tao/pegtl/parse_error.hpp +struct parse_fail +{ + parse_fail() = default; + + parse_fail( const std::string& msg, std::vector< position >&& in_positions ) + : demangled( msg ), + positions( std::move( in_positions ) ) + { + } + + template< typename Input > + parse_fail( const std::string& msg, const Input& in ) + : parse_fail( msg, in.position() ) + { + } + + parse_fail( const std::string& msg, const position& pos ) + : demangled( to_string( pos ) + ": " + msg ), + positions( 1, pos ) + { + } + + parse_fail( const std::string& msg, position&& pos ) + : demangled( to_string( pos ) + ": " + msg ) + { + positions.emplace_back( std::move( pos ) ); + } + + std::string what() const { return demangled; } + + std::string demangled; + std::vector< position > positions; +}; + +/** + * Base state to be used in case of any "must" rule. + */ +struct fail_state { + bool failed = false; + parse_fail internal; + + template + void fail(const std::string& msg, const Input& in) { + if (!failed) { + failed = true; + internal = {msg, in}; + } + } + + std::string what() const { return internal.demangled; } + std::vector positions() const { return internal.positions; } +}; + +template +struct apl_control : pegtl::normal< Rule > { + static const std::string error_message; + + template< typename Input> + static void fail( const Input& in, fail_state& state ) { + state.fail(error_message, in); + } + + template + static void raise( const Input& in, States&&... st) { + fail(in, st...); + } +}; + +template const std::string apl_control::error_message = "parse error matching " + pegtl::internal::demangle(); + +/// "must" polyfills. Based on following: +/// https://github.com/taocpp/PEGTL/blob/main/include/tao/pegtl/internal/raise.hpp +/// https://github.com/taocpp/PEGTL/blob/main/include/tao/pegtl/internal/must.hpp +/// https://github.com/taocpp/PEGTL/blob/main/include/tao/pegtl/internal/if_must.hpp +/// https://github.com/taocpp/PEGTL/blob/main/include/tao/pegtl/internal/list_must.hpp +namespace internal { + template< typename T > + struct raise + { + template< apply_mode, + rewind_mode, + template< typename... > + class Action, + template< typename... > + class Control, + typename Input, + typename... States > + static bool match( Input& in, States&&... st ) + { + Control< T >::raise( static_cast< const Input& >( in ), st... ); + return false; + } + }; + + template< typename... Rules > + struct must : seq< must< Rules >... > + {}; + + template + struct must { + template class Action, + template class Control, typename Input, typename... States> + static bool match(Input& in, States&&... st) { + if (!Control::template match(in, st...)) { + raise< Rule >::template match< A, rewind_mode::dontcare, Action, Control >( in, st... ); + return false; + } + return true; + } + }; + + template< bool Default, typename Cond, typename... Rules > + struct if_must + { + template< apply_mode A, + rewind_mode M, + template< typename... > + class Action, + template< typename... > + class Control, + typename Input, + typename... States > + static bool match( Input& in, States&&... st ) + { + if( Control< Cond >::template match< A, M, Action, Control >( in, st... ) ) { + if (Control< must< Rules... > >::template match< A, M, Action, Control >( in, st... )) { + return true; + } + } + return Default; + } + }; + + template< typename Rule, typename Sep > + using list_must = seq< Rule, star< Sep, must< Rule > > >; +} + +template< typename... Rules > struct must : internal::must< Rules... > {}; +template< typename Cond, typename... Thens > struct if_must : internal::if_must< false, Cond, Thens... > {}; +template< typename Cond, typename... Rules > struct opt_must : internal::if_must< true, Cond, Rules... > {}; +template< typename Rule, typename Sep, typename Pad = void > struct list_must : internal::list_must< Rule, tao::pegtl::internal::pad< Sep, Pad > > {}; +template< typename Rule, typename Sep > struct list_must< Rule, Sep, void > : internal::list_must< Rule, Sep > {}; + +} // namespace apl + +#endif // _APL_GRAMMAR_POLYFILL_H diff --git a/aplcore/include/apl/engine/contextwrapper.h b/aplcore/include/apl/engine/contextwrapper.h index 51a03f3..c765aa7 100644 --- a/aplcore/include/apl/engine/contextwrapper.h +++ b/aplcore/include/apl/engine/contextwrapper.h @@ -16,7 +16,7 @@ #ifndef _APL_CONTEXT_WRAPPER_H #define _APL_CONTEXT_WRAPPER_H -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" #include "apl/engine/context.h" namespace apl { @@ -88,6 +88,21 @@ class ContextWrapper : public ObjectData { return rapidjson::Value(rapidjson::kObjectType); } + class ObjectType final : public MapLikeObjectType { + public: + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return dataHolder.data->serialize(allocator); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *std::static_pointer_cast(lhs.data) == + *std::static_pointer_cast(rhs.data); + } + }; + private: std::weak_ptr mContext; }; diff --git a/aplcore/include/apl/engine/propdef.h b/aplcore/include/apl/engine/propdef.h index bfd3cb6..33e40be 100644 --- a/aplcore/include/apl/engine/propdef.h +++ b/aplcore/include/apl/engine/propdef.h @@ -134,7 +134,7 @@ inline Object asFilterArray(const Context& context, const Object& object) { std::vector data; for (auto& m : arrayify(context, object)) { auto f = Filter::create(context, m); - if (f.isFilter()) + if (f.is()) data.push_back(std::move(f)); } return Object(std::move(data)); @@ -144,7 +144,7 @@ inline Object asGraphicFilterArray(const Context& context, const Object& object) std::vector data; for (auto& m : arrayify(context, object)) { auto f = GraphicFilter::create(context, m); - if (f.isGraphicFilter()) + if (f.is()) data.push_back(std::move(f)); } return Object(std::move(data)); @@ -180,7 +180,7 @@ inline Object asGradient(const Context& context, const Object& object) { inline Object asFill(const Context& context, const Object& object) { auto gradient = asGradient(context, object); - return gradient.isGradient() ? gradient : asColor(context, object); + return gradient.is() ? gradient : asColor(context, object); } inline Object asVectorGraphicSource(const Context& context, const Object& object) { @@ -211,7 +211,7 @@ inline Object asMediaSourceArray(const Context& context, const Object& object) { std::vector data; for (auto& m : arrayify(context, object)) { auto ms = MediaSource::create(context, m); - if (ms.isMediaSource()) + if (ms.is()) data.push_back(std::move(ms)); } return Object(std::move(data)); @@ -236,14 +236,14 @@ inline Object asFilteredText(const Context& context, const Object& object) { } inline Object asTransformOrArray(const Context& context, const Object& object) { - if (object.isTransform()) + if (object.is()) return object; return evaluateRecursive(context, arrayify(context, object)); } inline Object asEasing(const Context& context, const Object& object) { - if (object.isEasing()) + if (object.is()) return object; return Easing::parse(context.session(), object.asString()); diff --git a/aplcore/include/apl/engine/recalculatetarget.h b/aplcore/include/apl/engine/recalculatetarget.h index 95d0ab1..f3dff4d 100644 --- a/aplcore/include/apl/engine/recalculatetarget.h +++ b/aplcore/include/apl/engine/recalculatetarget.h @@ -61,6 +61,15 @@ class RecalculateTarget { it++; } + /** + * Return true if this key is driven by one or more upstream dependants + * @param key The key + * @return True if there is at least one upstream dependant. + */ + bool hasUpstream(T key) { + return mUpstream.find(key) != mUpstream.end(); + } + /** * Return how many upstream dependants are connected to this key. * @param key The key diff --git a/aplcore/include/apl/engine/uidobject.h b/aplcore/include/apl/engine/uidobject.h index b72f323..002db1f 100644 --- a/aplcore/include/apl/engine/uidobject.h +++ b/aplcore/include/apl/engine/uidobject.h @@ -35,7 +35,15 @@ namespace apl { */ class UIDObject { public: - UIDObject(const ContextPtr& context); + // Not ideal, but required to distinguish extending class without RTTI. + enum class UIDObjectType { + COMPONENT, + GRAPHIC, + GRAPHIC_ELEMENT, + GRAPHIC_PATTERN + }; + + UIDObject(const ContextPtr& context, apl::UIDObject::UIDObjectType type); virtual ~UIDObject(); /** @@ -43,6 +51,11 @@ class UIDObject { */ std::string getUniqueId() const { return mUniqueId; } + /** + * @return Type of the object. + */ + UIDObject::UIDObjectType objectType() const { return mType; } + /** * Equality operator override */ @@ -59,6 +72,9 @@ class UIDObject { protected: std::string mUniqueId; ContextPtr mContext; + +private: + UIDObject::UIDObjectType mType; }; } // namespace apl diff --git a/aplcore/include/apl/extension/extensioncomponent.h b/aplcore/include/apl/extension/extensioncomponent.h index 5029c6d..4bc50d4 100644 --- a/aplcore/include/apl/extension/extensioncomponent.h +++ b/aplcore/include/apl/extension/extensioncomponent.h @@ -47,8 +47,6 @@ class ExtensionComponent : public CoreComponent { */ std::string name() const override; - void release() override; - void initialize() override; const ComponentPropDefSet& propDefSet() const override { return mPropDefSet; }; @@ -90,6 +88,12 @@ class ExtensionComponent : public CoreComponent { int errorCode = 0, const std::string& error = "") override; + /** + * @param component Pointer to cast. + * @return Casted pointer to this type, nullptr if not possible. + */ + static std::shared_ptr cast(const std::shared_ptr& component); + protected: /** * Function which executes the handler associated with FatalError @@ -108,6 +112,8 @@ class ExtensionComponent : public CoreComponent { std::string getVisualContextType() const override; + void releaseSelf() override; + private: /** * Notify the extension the component has changed state or property. diff --git a/aplcore/include/apl/graphic/graphic.h b/aplcore/include/apl/graphic/graphic.h index 59edea4..cdaa7d7 100644 --- a/aplcore/include/apl/graphic/graphic.h +++ b/aplcore/include/apl/graphic/graphic.h @@ -17,11 +17,12 @@ #define _APL_GRAPHIC_H #include "apl/apl_config.h" -#include "apl/graphic/graphiccontent.h" -#include "apl/graphic/graphicelement.h" #include "apl/engine/jsonresource.h" #include "apl/engine/styles.h" #include "apl/engine/uidobject.h" +#include "apl/graphic/graphiccontent.h" +#include "apl/graphic/graphicelement.h" +#include "apl/primitives/objecttype.h" #include "apl/utils/noncopyable.h" #include "apl/utils/userdata.h" @@ -35,9 +36,9 @@ class VectorGraphicComponent; */ class Graphic : public UIDObject, public std::enable_shared_from_this, - public NonCopyable, public Counter, - public UserData { + public UserData, + public ObjectData { friend class GraphicElement; friend class GraphicDependant; friend class VectorGraphicComponent; @@ -169,7 +170,11 @@ class Graphic : public UIDObject, */ std::shared_ptr styles() const { return mStyles; } - rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const; + rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const override; + + std::string toDebugString() const override { + return "Graphic<>"; + } #ifdef SCENEGRAPH sg::NodePtr getSceneGraph(sg::SceneGraphUpdates& sceneGraph); @@ -182,6 +187,8 @@ class Graphic : public UIDObject, bool updateSceneGraph(sg::SceneGraphUpdates& sceneGraph); #endif // SCENEGRAPH + class ObjectType final : public PointerHolderObjectType {}; + private: /** * Assign this graphic to a VectorGraphicComponent diff --git a/aplcore/include/apl/graphic/graphicelement.h b/aplcore/include/apl/graphic/graphicelement.h index 1f7ffa7..ceb1bea 100644 --- a/aplcore/include/apl/graphic/graphicelement.h +++ b/aplcore/include/apl/graphic/graphicelement.h @@ -148,9 +148,9 @@ class GraphicElement : public UIDObject, sg::NodePtr getSceneGraph(sg::SceneGraphUpdates& sceneGraph); /** - * @return True if the scene graph needs to be redrawn + * @return The first scene graph node */ - bool needsRedraw() const; + sg::NodePtr getSceneGraphNode() const { return mSceneGraphNode; } /** * Update the scene graph based on dirty properties. diff --git a/aplcore/include/apl/graphic/graphicelementcontainer.h b/aplcore/include/apl/graphic/graphicelementcontainer.h index a54539d..aadc733 100644 --- a/aplcore/include/apl/graphic/graphicelementcontainer.h +++ b/aplcore/include/apl/graphic/graphicelementcontainer.h @@ -29,16 +29,8 @@ class GraphicElementContainer : public GraphicElement, GraphicElementType getType() const override { return kGraphicElementTypeContainer; } bool hasChildren() const override { return true; } - std::string toDebugString() const override - { - std::string result = "GraphicElementContainertoDebugString(); - } - result += ">"; - - return result; + std::string toDebugString() const override { + return std::string("GraphicElementContainer<") + mUniqueId + ">"; } #ifdef SCENEGRAPH diff --git a/aplcore/include/apl/graphic/graphicelementgroup.h b/aplcore/include/apl/graphic/graphicelementgroup.h index 33cb03c..671162e 100644 --- a/aplcore/include/apl/graphic/graphicelementgroup.h +++ b/aplcore/include/apl/graphic/graphicelementgroup.h @@ -29,16 +29,8 @@ class GraphicElementGroup : public GraphicElement, GraphicElementType getType() const override { return kGraphicElementTypeGroup; } bool hasChildren() const override { return true; } - std::string toDebugString() const override - { - std::string result = "GraphicElementGrouptoDebugString(); - } - result += ">"; - - return result; + std::string toDebugString() const override { + return std::string("GraphicElementGroup<") + mUniqueId + ">"; } protected: diff --git a/aplcore/include/apl/graphic/graphicelementpath.h b/aplcore/include/apl/graphic/graphicelementpath.h index 3df83ed..a30dbb4 100644 --- a/aplcore/include/apl/graphic/graphicelementpath.h +++ b/aplcore/include/apl/graphic/graphicelementpath.h @@ -27,7 +27,9 @@ class GraphicElementPath : public GraphicElement, GraphicElementPath(const GraphicPtr& graphic, const ContextPtr& context) : GraphicElement(graphic, context) {} GraphicElementType getType() const override { return kGraphicElementTypePath; } - std::string toDebugString() const override { return "GraphicElementPath<>"; } + std::string toDebugString() const override { + return std::string("GraphicElementPath<") + mUniqueId + ">"; + } protected: const GraphicPropDefSet& propDefSet() const override; diff --git a/aplcore/include/apl/graphic/graphicelementtext.h b/aplcore/include/apl/graphic/graphicelementtext.h index 809fe74..65dcf39 100644 --- a/aplcore/include/apl/graphic/graphicelementtext.h +++ b/aplcore/include/apl/graphic/graphicelementtext.h @@ -17,6 +17,7 @@ #define _APL_GRAPHIC_ELEMENT_TEXT_H #include "apl/graphic/graphicelement.h" +#include "apl/primitives/point.h" #ifdef SCENEGRAPH #include "apl/scenegraph/textproperties.h" #endif // SCENEGRAPH @@ -31,9 +32,8 @@ class GraphicElementText : public GraphicElement, GraphicElementText(const GraphicPtr& graphic, const ContextPtr& context) : GraphicElement(graphic, context) {} GraphicElementType getType() const override { return kGraphicElementTypeText; } - std::string toDebugString() const override - { - return "GraphicElementText"; + std::string toDebugString() const override { + return std::string("GraphicElementText<") + mUniqueId + ">"; } protected: diff --git a/aplcore/include/apl/graphic/graphicfilter.h b/aplcore/include/apl/graphic/graphicfilter.h index 74beef1..3d2334c 100644 --- a/aplcore/include/apl/graphic/graphicfilter.h +++ b/aplcore/include/apl/graphic/graphicfilter.h @@ -16,7 +16,7 @@ #ifndef _APL_GRAPHIC_FILTER_H #define _APL_GRAPHIC_FILTER_H -#include "apl/primitives/object.h" +#include "apl/primitives/objecttype.h" #include "apl/utils/bimap.h" namespace apl { @@ -80,6 +80,8 @@ class GraphicFilter { bool truthy() const { return true; } + class ObjectType final : public ReferenceHolderObjectType {}; + private: GraphicFilter(GraphicFilterType type, std::map&& data) : mType(type), mData(std::move(data)) {} diff --git a/aplcore/include/apl/graphic/graphicpattern.h b/aplcore/include/apl/graphic/graphicpattern.h index bb65684..d8e8220 100644 --- a/aplcore/include/apl/graphic/graphicpattern.h +++ b/aplcore/include/apl/graphic/graphicpattern.h @@ -18,7 +18,7 @@ #include "apl/common.h" #include "apl/engine/uidobject.h" -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" #include "apl/utils/counter.h" namespace apl { @@ -75,6 +75,8 @@ class GraphicPattern : public UIDObject, bool truthy() const override { return true; } std::uint64_t size() const override { return mItems.size(); } + class ObjectType final : public PointerHolderObjectType {}; + private: std::string mDescription; double mHeight; diff --git a/aplcore/include/apl/livedata/livearray.h b/aplcore/include/apl/livedata/livearray.h index d8fa3bd..f1617f9 100644 --- a/aplcore/include/apl/livedata/livearray.h +++ b/aplcore/include/apl/livedata/livearray.h @@ -95,7 +95,7 @@ class LiveArray : public LiveObject, public Counter { explicit LiveArray(ObjectArray&& array) : mArray(std::move(array)) {} // Override from LiveObject - Object::ObjectType getType() const override { return Object::ObjectType::kArrayType; } + LiveObject::ObjectType getType() const override { return LiveObject::ObjectType::kArrayType; } /** * Check to see if there are no elements in the array. diff --git a/aplcore/include/apl/livedata/livearrayobject.h b/aplcore/include/apl/livedata/livearrayobject.h index bd3b0e3..82df6fd 100644 --- a/aplcore/include/apl/livedata/livearrayobject.h +++ b/aplcore/include/apl/livedata/livearrayobject.h @@ -41,7 +41,7 @@ class LiveArrayObject : public LiveDataObject, Counter { using size_type = ObjectArray::size_type; static LiveArrayObject& cast(LiveDataObject& object) { - assert(object.getType() == Object::kArrayType); + assert(object.getType() == LiveObject::ObjectType::kArrayType); return reinterpret_cast(object); } @@ -58,7 +58,7 @@ class LiveArrayObject : public LiveDataObject, Counter { /** * Overrides from LiveDataObject */ - Object::ObjectType getType() const override { return Object::kArrayType; } + LiveObject::ObjectType getType() const override { return LiveObject::ObjectType::kArrayType; } std::shared_ptr asArray() override { return std::static_pointer_cast(shared_from_this()); @@ -98,6 +98,11 @@ class LiveArrayObject : public LiveDataObject, Counter { */ virtual bool isPaginating() const { return false; } + class ObjectType final : public ArrayObjectType { + public: + std::shared_ptr getLiveDataObject(const Object::DataHolder& dataHolder) const override; + }; + private: void handleArrayMessage(const LiveArrayChange& change); diff --git a/aplcore/include/apl/livedata/livedataobject.h b/aplcore/include/apl/livedata/livedataobject.h index d421c28..50ad77a 100644 --- a/aplcore/include/apl/livedata/livedataobject.h +++ b/aplcore/include/apl/livedata/livedataobject.h @@ -18,7 +18,7 @@ #include -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" #include "apl/engine/context.h" #include "apl/livedata/liveobject.h" @@ -58,7 +58,7 @@ class LiveDataObject : public BaseArrayData, /** * @return The object type contained */ - virtual Object::ObjectType getType() const = 0; + virtual LiveObject::ObjectType getType() const = 0; /** * @return This object as a live array object or nullptr if that is invalid. diff --git a/aplcore/include/apl/livedata/livemap.h b/aplcore/include/apl/livedata/livemap.h index 234b8c9..f0cc1d9 100644 --- a/aplcore/include/apl/livedata/livemap.h +++ b/aplcore/include/apl/livedata/livemap.h @@ -80,7 +80,7 @@ class LiveMap : public LiveObject, public Counter { explicit LiveMap(ObjectMap&& map) : mMap(std::move(map)) {} // Override from LiveObject - Object::ObjectType getType() const override { return Object::ObjectType::kMapType; } + LiveObject::ObjectType getType() const override { return LiveObject::ObjectType::kMapType; } /** * Check to see if there are no elements in the map diff --git a/aplcore/include/apl/livedata/livemapobject.h b/aplcore/include/apl/livedata/livemapobject.h index f791b77..a7d48a3 100644 --- a/aplcore/include/apl/livedata/livemapobject.h +++ b/aplcore/include/apl/livedata/livemapobject.h @@ -36,7 +36,7 @@ namespace apl { class LiveMapObject : public LiveDataObject, Counter { public: static LiveMapObject& cast(LiveDataObject& object) { - assert(object.getType() == Object::kMapType); + assert(object.getType() == LiveObject::ObjectType::kMapType); return reinterpret_cast(object); } @@ -53,7 +53,7 @@ class LiveMapObject : public LiveDataObject, Counter { /** * Overrides from LiveDataObject */ - Object::ObjectType getType() const override { return Object::kMapType; } + LiveObject::ObjectType getType() const override { return LiveObject::ObjectType::kMapType; } std::shared_ptr asMap() override { return std::static_pointer_cast(shared_from_this()); @@ -84,6 +84,11 @@ class LiveMapObject : public LiveDataObject, Counter { */ const std::set& getChanged(); + class ObjectType final : public MapObjectType { + public: + std::shared_ptr getLiveDataObject(const Object::DataHolder& dataHolder) const override; + }; + private: void handleMapMessage(const LiveMapChange& change); diff --git a/aplcore/include/apl/livedata/liveobject.h b/aplcore/include/apl/livedata/liveobject.h index ad7c816..dfc2ed9 100644 --- a/aplcore/include/apl/livedata/liveobject.h +++ b/aplcore/include/apl/livedata/liveobject.h @@ -26,7 +26,12 @@ namespace apl { */ class LiveObject { public: - virtual Object::ObjectType getType() const = 0; + enum class ObjectType { + kArrayType, + kMapType + }; + + virtual ObjectType getType() const = 0; virtual ~LiveObject() = default; }; diff --git a/aplcore/include/apl/media/mediaobject.h b/aplcore/include/apl/media/mediaobject.h index 8e0a122..973f035 100644 --- a/aplcore/include/apl/media/mediaobject.h +++ b/aplcore/include/apl/media/mediaobject.h @@ -112,6 +112,11 @@ class MediaObject : public NonCopyable, */ virtual void removeCallback(CallbackID callbackToken) = 0; + /** + * @return The JSON value of this media object (if it exists) + */ + virtual GraphicContentPtr graphic() { return nullptr; } + virtual rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const { auto out = rapidjson::Value(rapidjson::kObjectType); out.AddMember("url", rapidjson::Value(url().c_str(), allocator), allocator); diff --git a/aplcore/include/apl/primitives/accessibilityaction.h b/aplcore/include/apl/primitives/accessibilityaction.h index f0f67fe..c301dac 100644 --- a/aplcore/include/apl/primitives/accessibilityaction.h +++ b/aplcore/include/apl/primitives/accessibilityaction.h @@ -17,7 +17,7 @@ #define _APL_ACCESSIBILITY_ACTION_H #include "apl/engine/recalculatetarget.h" -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" #include "apl/utils/userdata.h" namespace apl { @@ -83,7 +83,7 @@ class AccessibilityAction : public ObjectData, // Standard ObjectData methods bool operator==(const ObjectData& other) const override { - return *this == dynamic_cast(other); + return *this == static_cast(other); } bool operator==(const AccessibilityAction& other) const { @@ -109,6 +109,13 @@ class AccessibilityAction : public ObjectData, AccessibilityAction(const CoreComponentPtr& component, std::string name, std::string label) : mComponent(component), mName(std::move(name)), mLabel(std::move(label)), mEnabled(true) {} + class ObjectType final : public PointerHolderObjectType { + public: + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *lhs.data == *rhs.data; + } + }; + protected: void initialize(const ContextPtr& context, const Object& object); void setEnabled(bool enabled, bool useDirtyFlag); diff --git a/aplcore/include/apl/primitives/color.h b/aplcore/include/apl/primitives/color.h index a663f46..9133a11 100644 --- a/aplcore/include/apl/primitives/color.h +++ b/aplcore/include/apl/primitives/color.h @@ -22,8 +22,9 @@ #include -#include "apl/utils/streamer.h" #include "apl/common.h" +#include "apl/utils/streamer.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -158,6 +159,40 @@ class Color { return { true, it->second }; } + class ObjectType final : public TrueObjectType { + public: + std::string asString(const Object::DataHolder& dataHolder) const override { + return Color(dataHolder.value).asString(); + } + + Color asColor(const Object::DataHolder& dataHolder, const SessionPtr&) const override { + return Color(dataHolder.value); + } + + uint32_t getColor(const Object::DataHolder& dataHolder) const override { + return static_cast(dataHolder.value); + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(dataHolder.value); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value(asString(dataHolder).c_str(), allocator); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return asString(dataHolder); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return lhs.value == rhs.value; + } + }; + private: /** * Convert from a color string representation to a color diff --git a/aplcore/include/apl/primitives/dimension.h b/aplcore/include/apl/primitives/dimension.h index 7242fee..8daddfc 100644 --- a/aplcore/include/apl/primitives/dimension.h +++ b/aplcore/include/apl/primitives/dimension.h @@ -18,6 +18,7 @@ #include #include "apl/utils/streamer.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -106,6 +107,181 @@ class Dimension friend streamer& operator<<(streamer&, const Dimension&); + class DimensionObjectType : public BaseObjectType { + public: + bool isDimension() const final { return true; } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return lhs.value == rhs.value; + } + }; + + class AutoDimensionObjectType final : public TrueObjectType { + public: + static ObjectTypeRef instance() { + static Dimension::AutoDimensionObjectType sType; + return &sType; + } + + bool isAutoDimension() const override { return true; } + + std::string asString(const Object::DataHolder& dataHolder) const override { return "auto"; } + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context&) const override { + return Dimension(DimensionType::Auto, 0); + } + + std::size_t hash(const Object::DataHolder&) const override { + return std::hash{}("auto"); + } + + rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value("auto", allocator); + } + + std::string toDebugString(const Object::DataHolder&) const override { return "AutoDim"; } + + bool equals(const Object::DataHolder&, const Object::DataHolder&) const override { + return true; + } + }; + + class RelativeDimensionObjectType final : public DimensionObjectType { + public: + static ObjectTypeRef instance() { + static Dimension::RelativeDimensionObjectType sType; + return &sType; + } + + bool isRelativeDimension() const override { return true; } + + bool isNonAutoDimension() const override { return true; } + + std::string asString(const Object::DataHolder& dataHolder) const override { + return doubleToAplFormattedString(dataHolder.value) + "%"; + } + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context&) const override { + return Dimension(DimensionType::Relative, dataHolder.value); + } + + Dimension asNonAutoDimension( + const Object::DataHolder& dataHolder, + const Context&) const override + { + return Dimension(DimensionType::Relative, dataHolder.value); + } + + Dimension asNonAutoRelativeDimension( + const Object::DataHolder& dataHolder, + const Context&) const override + { + return Dimension(DimensionType::Relative, dataHolder.value); + } + + double getRelativeDimension(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + bool truthy(const Object::DataHolder& dataHolder) const override { + return dataHolder.value != 0; + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(dataHolder.value); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value((doubleToAplFormattedString(dataHolder.value)+"%").c_str(), allocator); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return "RelDim<" + sutil::to_string(dataHolder.value) + ">"; + } + }; + + class AbsoluteDimensionObjectType final : public DimensionObjectType { + public: + static ObjectTypeRef instance() { + static Dimension::AbsoluteDimensionObjectType sType; + return &sType; + } + + bool isAbsoluteDimension() const override { return true; } + + bool isNonAutoDimension() const override { return true; } + + std::string asString(const Object::DataHolder& dataHolder) const override { + return doubleToAplFormattedString(dataHolder.value) + "dp"; + } + + double asNumber(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + int asInt(const Object::DataHolder& dataHolder, int base) const override { + return std::lround(dataHolder.value); + } + + int64_t asInt64(const Object::DataHolder& dataHolder, int base) const override { + return std::llround(dataHolder.value); + } + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context&) const override { + return Dimension(DimensionType::Absolute, dataHolder.value); + } + + Dimension asAbsoluteDimension( + const Object::DataHolder& dataHolder, + const Context&) const override + { + return Dimension(DimensionType::Absolute, dataHolder.value); + } + + Dimension asNonAutoDimension( + const Object::DataHolder& dataHolder, + const Context&) const override + { + return Dimension(DimensionType::Absolute, dataHolder.value); + } + + Dimension asNonAutoRelativeDimension( + const Object::DataHolder& dataHolder, + const Context&) const override + { + return Dimension(DimensionType::Absolute, dataHolder.value); + } + + double getAbsoluteDimension(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + bool truthy(const Object::DataHolder& dataHolder) const override { + return dataHolder.value != 0; + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(dataHolder.value); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value(std::isfinite(dataHolder.value) ? dataHolder.value : 0); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return "AbsDim<" + sutil::to_string(dataHolder.value) + ">"; + } + }; + private: DimensionType mType; double mValue; diff --git a/aplcore/include/apl/primitives/filter.h b/aplcore/include/apl/primitives/filter.h index d3d33a2..f8b9521 100644 --- a/aplcore/include/apl/primitives/filter.h +++ b/aplcore/include/apl/primitives/filter.h @@ -16,7 +16,8 @@ #ifndef _APL_FILTER_H #define _APL_FILTER_H -#include "objectbag.h" +#include "apl/primitives/objectbag.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -137,6 +138,8 @@ class Filter { bool truthy() const { return true; } + class ObjectType final : public ReferenceHolderObjectType {}; + private: Filter(FilterType type, std::map&& data) : mType(type), mData(std::move(data)) {} diff --git a/aplcore/include/apl/primitives/functions.h b/aplcore/include/apl/primitives/functions.h index 145f931..9ca8177 100644 --- a/aplcore/include/apl/primitives/functions.h +++ b/aplcore/include/apl/primitives/functions.h @@ -18,7 +18,7 @@ #include -#include "apl/primitives/objectdata.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -59,6 +59,22 @@ class Function : public ObjectData { return "function<" + mName + ">"; } + class ObjectType final : public PointerHolderObjectType { + public: + bool isCallable() const override { return true; } + + Object call(const Object::DataHolder& dataHolder, const ObjectArray& args) const override { + return dataHolder.data->call(args); + } + + rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value("FUNCTION", allocator); + } + }; + private: std::string mName; UserFunction mFunction; diff --git a/aplcore/include/apl/primitives/gradient.h b/aplcore/include/apl/primitives/gradient.h index b82439d..622ec9b 100644 --- a/aplcore/include/apl/primitives/gradient.h +++ b/aplcore/include/apl/primitives/gradient.h @@ -21,8 +21,8 @@ #include "rapidjson/document.h" +#include "apl/primitives/objecttype.h" #include "apl/primitives/color.h" -#include "apl/primitives/object.h" namespace apl { @@ -141,6 +141,8 @@ class Gradient { bool empty() const { return false; } bool truthy() const { return true; } + class ObjectType final : public ReferenceHolderObjectType {}; + private: Gradient(const Context& context, std::map&& properties); diff --git a/aplcore/include/apl/primitives/mediasource.h b/aplcore/include/apl/primitives/mediasource.h index 7bb87cf..c73f892 100644 --- a/aplcore/include/apl/primitives/mediasource.h +++ b/aplcore/include/apl/primitives/mediasource.h @@ -18,8 +18,8 @@ #include +#include "apl/primitives/objecttype.h" #include "apl/media/mediaobject.h" -#include "apl/primitives/object.h" #include "apl/primitives/urlrequest.h" namespace apl { @@ -81,6 +81,8 @@ class MediaSource { bool empty() const { return false; } bool truthy() const { return true; } + class ObjectType final : public ReferenceHolderObjectType {}; + private: MediaSource(URLRequest urlRequest, std::string description, diff --git a/aplcore/include/apl/primitives/object.h b/aplcore/include/apl/primitives/object.h index ec856ec..134b6e4 100644 --- a/aplcore/include/apl/primitives/object.h +++ b/aplcore/include/apl/primitives/object.h @@ -39,17 +39,14 @@ #include #include #include +#include +#include #include "rapidjson/document.h" #include "apl/common.h" #include "apl/utils/deprecated.h" #include "apl/utils/visitor.h" -#include "apl/primitives/color.h" -#include "apl/primitives/dimension.h" -#include "apl/primitives/radii.h" -#include "apl/primitives/rect.h" -#include "apl/primitives/transform2d.h" #ifdef APL_CORE_UWP #undef TRUE @@ -64,24 +61,14 @@ namespace datagrammar { } class Object; -class AccessibilityAction; -class ComponentEventWrapper; -class ContextWrapper; -class Filter; -class Function; -class Gradient; -class GraphicFilter; -class GraphicPattern; -class Transformation; -class MediaSource; +class Color; +class Dimension; class LiveDataObject; -class Range; class RangeGenerator; -class URLRequest; class SliceGenerator; -class StyledText; class SymbolReferenceMap; class ObjectData; +class ObjectType; class streamer; @@ -101,7 +88,7 @@ using ObjectArrayPtr = std::shared_ptr; * property. * * Note that certain types stored in Objects are treated as immutable and certain types - * are mutable. The immutable types are: + * are mutable. Examples of immutable types are: * * - Null * - Boolean @@ -109,66 +96,27 @@ using ObjectArrayPtr = std::shared_ptr; * - String * - Array * - Map (object) - * - Function * - Dimensions (absolute, relative, and auto) * - Colors - * - Filters - * - Gradients - * - Media sources - * - Range - * - Rectangles - * - Radii - * - Sources - * - Styled Text - * - 2D Transformations - * - Bound Symbol - * - ByteCode * - * The mutable types are: + * Example of mutable types are: * - Vector graphic * - Generalized transformation */ class Object { public: - enum ObjectType { - kNullType, - kBoolType, - kNumberType, - kStringType, - kArrayType, - kMapType, - kByteCodeType, - kFunctionType, - kAbsoluteDimensionType, - kRelativeDimensionType, - kAutoDimensionType, - kColorType, - kFilterType, - kGraphicFilterType, - kGradientType, - kGraphicPatternType, - kMediaSourceType, - kRectType, - kRadiiType, - kStyledTextType, - kRangeType, - kGraphicType, - kTransformType, - kTransform2DType, - kURLRequestType, - kEasingType, - kBoundSymbolType, - kComponentType, - kContextType, - kAccessibilityActionType, + /// ObjectData held types. + enum class StorageType { + kStorageTypeEmpty, + kStorageTypeValue, + kStorageTypeString, + kStorageTypeReference, + kStorageTypePointer }; - class Data; // Forward declaration - // Constructors Object(); - Object(ObjectType type); Object(bool b); Object(int i); Object(uint32_t u); @@ -179,34 +127,28 @@ class Object Object(double d); Object(const char *s); Object(const std::string& s); - Object(const std::shared_ptr& bc); Object(const ObjectMapPtr& m, bool isMutable=false); Object(const ObjectArrayPtr& v, bool isMutable=false); Object(ObjectArray&& v, bool isMutable=false); - Object(const rapidjson::Value& v); - Object(rapidjson::Document&& doc); - Object(const std::shared_ptr& f); Object(const Color& color); Object(const Dimension& dimension); - Object(Filter&& filter); - Object(GraphicFilter&& graphicFilter); - Object(Gradient&& gradient); - Object(MediaSource&& mediaSource); - Object(Rect&& rect); - Object(Radii&& radii); - Object(StyledText&& styledText); - Object(Range range); - Object(const GraphicPtr& graphic); - Object(const GraphicPatternPtr& graphicPattern); - Object(const std::shared_ptr& transform); - Object(Transform2D&& transform2d); - Object(URLRequest&& urlRequest); - Object(const EasingPtr& easing); - Object(const std::shared_ptr& b); - Object(const std::shared_ptr& d); - Object(const std::shared_ptr& component); - Object(const std::shared_ptr& context); - Object(const std::shared_ptr& accessibilityAction); + Object(const rapidjson::Value& v); + Object(rapidjson::Document&& doc); + + template< + class T, + StorageType ST = T::ObjectType::scStorageType, + typename = typename std::enable_if::type + > + Object(const std::shared_ptr& content) : mType(T::ObjectType::instance()), mU(content) {} + + template< + class T, + StorageType ST = T::ObjectType::scStorageType, + typename = typename std::enable_if::type + > + Object(T&& content) : mType(T::ObjectType::instance()), mU(T::ObjectType::createDirectObjectData(std::move(content))) {} + Object(const std::shared_ptr& range); Object(const std::shared_ptr& slice); @@ -215,16 +157,10 @@ class Object static const Object& FALSE_OBJECT(); static const Object& NULL_OBJECT(); static Object NAN_OBJECT(); - static Object AUTO_OBJECT(); static Object EMPTY_ARRAY(); static Object EMPTY_MUTABLE_ARRAY(); static Object EMPTY_MAP(); static Object EMPTY_MUTABLE_MAP(); - static Object ZERO_ABS_DIMEN(); - static Object EMPTY_RECT(); - static Object EMPTY_RADII(); - static Object IDENTITY_2D(); - static Object LINEAR_EASING(); // Destructor ~Object(); @@ -239,50 +175,40 @@ class Object bool operator==(const Object& rhs) const; bool operator!=(const Object& rhs) const; - bool isNull() const { return mType == kNullType; } - bool isBoolean() const { return mType == kBoolType; } - bool isString() const { return mType == kStringType; } - bool isNumber() const { return mType == kNumberType; } - bool isNaN() const { return mType == kNumberType && std::isnan(mU.value); } - bool isArray() const { return mType == kArrayType; } - bool isMap() const { return mType == kMapType || mType == kComponentType || mType == kContextType; } - bool isTrueMap() const { return mType == kMapType; } - bool isBoundSymbol() const { return mType == kBoundSymbolType; } - bool isCallable() const { return mType == kFunctionType || mType == kEasingType; } - bool isByteCode() const { return mType == kByteCodeType; } - bool isEvaluable() const { return mType == kByteCodeType || - mType == kBoundSymbolType; } - bool isFunction() const { return mType == kFunctionType; } - bool isAbsoluteDimension() const { return mType == kAbsoluteDimensionType; } - bool isRelativeDimension() const { return mType == kRelativeDimensionType; } - bool isAutoDimension() const { return mType == kAutoDimensionType; } - bool isNonAutoDimension() const { return mType == kAbsoluteDimensionType || - mType == kRelativeDimensionType; } - bool isDimension() const { return mType == kAutoDimensionType || - mType == kRelativeDimensionType || - mType == kAbsoluteDimensionType; } - bool isColor() const { return mType == kColorType; } - bool isFilter() const { return mType == kFilterType; } - bool isGraphicFilter() const { return mType == kGraphicFilterType; } - bool isGradient() const { return mType == kGradientType; } - bool isMediaSource() const { return mType == kMediaSourceType; } - bool isRect() const { return mType == kRectType; } - bool isRadii() const { return mType == kRadiiType; } - bool isURLRequest() const { return mType == kURLRequestType; } - bool isStyledText() const { return mType == kStyledTextType; } - bool isRange() const { return mType == kRangeType; } - bool isGraphic() const { return mType == kGraphicType; } - bool isGraphicPattern() const { return mType == kGraphicPatternType; } - bool isTransform() const { return mType == kTransformType; } - bool isTransform2D() const { return mType == kTransform2DType; } - bool isEasing() const { return mType == kEasingType; } - bool isComponentEventData() const { return mType == kComponentType; } - bool isContext() const { return mType == kContextType; } - bool isJson() const; - bool isAccessibilityAction() const { return mType == kAccessibilityActionType; } - - // These methods force a conversion to the appropriate type. We try to return - // plausible defaults whenever possible. + /** + * Check if object contains data of provided type. + * @tparam T type + * @return true if contains requested type, false otherwise. + */ + template bool is() const { return mType == T::ObjectType::instance(); } + + bool isNull() const; + bool isBoolean() const; + bool isString() const; + bool isNumber() const; + bool isNaN() const; + bool isArray() const; + bool isMap() const; + bool isTrueMap() const; + bool isDimension() const; + bool isAbsoluteDimension() const; + bool isRelativeDimension() const; + bool isAutoDimension() const; + bool isNonAutoDimension() const; + + bool isCallable() const; + bool isEvaluable() const; + + /// These methods force a conversion to the appropriate type. We try to return + /// plausible defaults whenever possible. + /** + * This method is used when coercing an object to a string. This can be used + * by an APL author to display information in a Text component, so we deliberately + * do not return values for many of the internal object types. Please use + * Object::toDebugString() to return strings suitable for writing to the system log. + * + * @return The user-friendly value of the object as a string. + */ std::string asString() const; bool asBoolean() const { return truthy(); } double asNumber() const; @@ -293,47 +219,66 @@ class Object Dimension asAbsoluteDimension(const Context&) const; Dimension asNonAutoDimension(const Context&) const; Dimension asNonAutoRelativeDimension(const Context&) const; - URLRequest asURLRequest() const; /// @deprecated This method will be removed soon. APL_DEPRECATED Color asColor() const; Color asColor(const SessionPtr&) const; Color asColor(const Context&) const; - // These methods return the actual contents of the Object - const std::string& getString() const { assert(mType == kStringType); return mU.string; } - bool getBoolean() const { assert(mType == kBoolType); return mU.value != 0; } - double getDouble() const { assert(mType == kNumberType); return mU.value; } - int getInteger() const { assert(mType == kNumberType); return static_cast(std::round(mU.value)); } - unsigned getUnsigned() const { assert(mType == kNumberType); return static_cast(mU.value);} - double getAbsoluteDimension() const { assert(mType == kAbsoluteDimensionType); return mU.value; } - double getRelativeDimension() const { assert(mType == kRelativeDimensionType); return mU.value; } - uint32_t getColor() const { assert(mType == kColorType); return static_cast(mU.value); } - - std::shared_ptr getFunction() const; - std::shared_ptr getBoundSymbol() const; - std::shared_ptr getLiveDataObject() const; - std::shared_ptr getAccessibilityAction() const; - std::shared_ptr getByteCode() const; + union DataHolder { + double value; + std::string string; + std::shared_ptr data; + + DataHolder() : value(0.0) {} + DataHolder(double v) : value(v) {} + DataHolder(const std::string& s) : string(s) {} + DataHolder(const std::shared_ptr& d) : data(d) {} + ~DataHolder() {} + }; + // These methods return the actual contents of the Object + /** + * Get data of provided type from the object. Applicable only to non-primitive types. + * @tparam T requested type. + * @return reference to data of requested type. + */ + template< + class T, + StorageType ST = T::ObjectType::scStorageType, + typename = typename std::enable_if::type + > + const T& get() const { + assert(is()); + return *static_cast(extractDataInner(mU)); + } + + /** + * Get data of provided type from the object. Applicable only to non-primitive types. + * @tparam T requested type. + * @return pointer to data of requested type. + */ + template< + class T, + StorageType ST = T::ObjectType::scStorageType, + typename = typename std::enable_if::type + > + std::shared_ptr get() const { + assert(is()); + return std::static_pointer_cast(mU.data); + } + + const std::string& getString() const; + bool getBoolean() const; + double getDouble() const; + int getInteger() const; + double getAbsoluteDimension() const; + double getRelativeDimension() const; + uint32_t getColor() const; const ObjectMap& getMap() const; ObjectMap& getMutableMap(); const ObjectArray& getArray() const; ObjectArray& getMutableArray(); - const Filter& getFilter() const; - const GraphicFilter& getGraphicFilter() const; - const Gradient& getGradient() const; - const MediaSource& getMediaSource() const; - GraphicPtr getGraphic() const; - GraphicPatternPtr getGraphicPattern() const; - Rect getRect() const; - Radii getRadii() const; - const URLRequest& getURLRequest() const; - const StyledText& getStyledText() const; - const Range& getRange() const; - std::shared_ptr getTransformation() const; - Transform2D getTransform2D() const; - EasingPtr getEasing() const; - const rapidjson::Value& getJson() const; + std::shared_ptr getLiveDataObject() const; bool truthy() const; @@ -345,7 +290,8 @@ class Object // ARRAY objects Object at(std::uint64_t index) const; - ObjectType getType() const; + // Get object type + const apl::ObjectType& type() const; // MAP, ARRAY, and STRING objects std::uint64_t size() const; @@ -369,7 +315,7 @@ class Object Object call(const ObjectArray& args) const; // Current object hash - size_t hash() const; + std::size_t hash() const; // Visitor pattern void accept(Visitor& visitor) const; @@ -385,25 +331,17 @@ class Object // Serialize just the dirty bits to JSON format rapidjson::Value serializeDirty(rapidjson::Document::AllocatorType& allocator) const; - template const T& as() const; template T asEnum() const { return static_cast(getInteger()); } private: - ObjectType mType; - union U { - double value; - std::string string; - std::shared_ptr data; + static const void* extractDataInner(const Object::DataHolder& dataHolder); + bool comparableWith(const Object& rhs) const; - U() : value(0.0) {} - U(double v) : value(v) {} - U(const std::string& s) : string(s) {} - U(const std::shared_ptr& d) : data(d) {} - ~U() {} - } mU; +private: + const apl::ObjectType* mType; + union DataHolder mU; }; - } // namespace apl namespace std { diff --git a/aplcore/include/apl/primitives/objectbag.h b/aplcore/include/apl/primitives/objectbag.h index a70bc83..35cf2fb 100644 --- a/aplcore/include/apl/primitives/objectbag.h +++ b/aplcore/include/apl/primitives/objectbag.h @@ -19,7 +19,7 @@ #include #include "apl/utils/bimap.h" -#include "object.h" +#include "apl/primitives/object.h" namespace apl { diff --git a/aplcore/include/apl/primitives/objectdata.h b/aplcore/include/apl/primitives/objectdata.h index 2cf4b49..46834a8 100644 --- a/aplcore/include/apl/primitives/objectdata.h +++ b/aplcore/include/apl/primitives/objectdata.h @@ -16,14 +16,12 @@ #ifndef _APL_OBJECT_DATA_H #define _APL_OBJECT_DATA_H -#include - #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "apl/primitives/object.h" -#include "apl/primitives/styledtext.h" #include "apl/utils/noncopyable.h" +#include "apl/utils/throw.h" namespace apl { @@ -106,7 +104,7 @@ class ObjectData : public NonCopyable { * @return The inner object. */ virtual const void *inner() const { - throw std::runtime_error("Illegal inner reference"); + aplThrow("Illegal inner reference"); } /** @@ -123,38 +121,30 @@ class ObjectData : public NonCopyable { } virtual const ObjectArray &getArray() const { - throw std::runtime_error("Illegal array"); + aplThrow("Illegal array"); } virtual ObjectArray &getMutableArray() { - throw std::runtime_error("Illegal mutable array"); + aplThrow("Illegal mutable array"); } virtual const ObjectMap &getMap() const { - throw std::runtime_error("Illegal map"); + aplThrow("Illegal map"); } virtual ObjectMap &getMutableMap() { - throw std::runtime_error("Illegal mutable map"); - } - - virtual GraphicPtr getGraphic() const { - throw std::runtime_error("Illegal graphic"); + aplThrow("Illegal mutable map"); } - virtual std::shared_ptr getTransform() const { - throw std::runtime_error("Illegal transform"); + virtual std::string toDebugString() const { + return "Unknown type"; } +protected: virtual const rapidjson::Value *getJson() const { return nullptr; } - virtual std::string toDebugString() const { - return "Unknown type"; - } - -protected: bool arrayCompare(const ObjectData& rhs) const { const auto len = size(); if (len != rhs.size()) @@ -233,7 +223,7 @@ class ArrayData : public BaseArrayData { std::vector& getMutableArray() override { if (!mIsMutable) - throw std::runtime_error("Attempted to retrieve mutable array for non-mutable object"); + aplThrow("Attempted to retrieve mutable array for non-mutable object"); return *mArray; } @@ -296,7 +286,7 @@ class FixedArrayData : public BaseArrayData { std::vector& getMutableArray() override { if (!mIsMutable) - throw std::runtime_error("Attempted to retrieve mutable array for non-mutable object"); + aplThrow("Attempted to retrieve mutable array for non-mutable object"); return mArray; } @@ -353,7 +343,7 @@ class MapData : public ObjectData { ObjectMap& getMutableMap() override { if (!mIsMutable) - throw std::runtime_error("Attempted to retrieve mutable map for non-mutable object"); + aplThrow("Attempted to retrieve mutable map for non-mutable object"); return *mMap; } @@ -485,10 +475,6 @@ class JSONData : public JSONBaseData { return mMap; } - const rapidjson::Value* getJson() const override { - return mValue; - } - std::string toDebugString() const override { rapidjson::StringBuffer buffer; buffer.Clear(); @@ -497,6 +483,11 @@ class JSONData : public JSONBaseData { return "JSON<" + std::string(buffer.GetString()) + ">"; } +protected: + const rapidjson::Value* getJson() const override { + return mValue; + } + private: const rapidjson::Value *mValue; const std::map mMap; @@ -582,14 +573,15 @@ class JSONDocumentData : public JSONBaseData { return mMap; } - const rapidjson::Value* getJson() const override { - return &mDoc; - } - std::string toDebugString() const override { return "JSONDoc"; } +protected: + const rapidjson::Value* getJson() const override { + return &mDoc; + } + private: const rapidjson::Document mDoc; const std::map mMap; @@ -598,36 +590,6 @@ class JSONDocumentData : public JSONBaseData { /****************************************************************************/ -class GraphicData : public ObjectData { -public: - GraphicData(const GraphicPtr& graphic) : mGraphic(graphic) {} - GraphicPtr getGraphic() const override { return mGraphic; } - - std::string toDebugString() const override { - return "Graphic<>"; - } - -private: - const GraphicPtr mGraphic; -}; - -/****************************************************************************/ - -class TransformData : public ObjectData { -public: - TransformData(const std::shared_ptr& transform) : mTransform(transform) {} - std::shared_ptr getTransform() const override { return mTransform; } - - std::string toDebugString() const override { - return "Transform<>"; - } - -private: - std::shared_ptr mTransform; -}; - -/****************************************************************************/ - /** * Objects stored inside of DirectObjectData should support the following methods: * @@ -668,11 +630,6 @@ class DirectObjectData : public ObjectData { return mData.serialize(allocator); } - /** - * Static type information for this direct object category. - */ - static const Object::ObjectType sType; - private: T mData; }; diff --git a/aplcore/include/apl/primitives/objecttype.h b/aplcore/include/apl/primitives/objecttype.h new file mode 100644 index 0000000..55c5f72 --- /dev/null +++ b/aplcore/include/apl/primitives/objecttype.h @@ -0,0 +1,549 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0/ +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifndef _APL_OBJECT_MAGIC_TYPE_H +#define _APL_OBJECT_MAGIC_TYPE_H + +#include "apl/primitives/objectdata.h" +#include "apl/utils/noncopyable.h" +#include "apl/utils/stringfunctions.h" +#include "apl/utils/throw.h" + +namespace apl { + +static const char *NOT_SUPPORTED_ERROR = "Operation not supported on this type."; + +/** + * Object type class. Should be extended by specific type/class implementations, which may be stored + * in the Object. + */ +class ObjectType : public NonCopyable { +public: + virtual ~ObjectType() = default; + + /// Type checks. + + /** + * Check Object-held type. + * @tparam T type. + * @return true if requested type/false otherwise. + */ + template bool is() const { return (T::ObjectType::instance() == this); } + + /// Complex type checks. + virtual bool isArray() const { return false; } + virtual bool isMap() const { return false; } + virtual bool isTrueMap() const { return false; } + virtual bool isCallable() const { return false; } + virtual bool isEvaluable() const { return false; } + virtual bool isAbsoluteDimension() const { return false; } + virtual bool isRelativeDimension() const { return false; } + virtual bool isAutoDimension() const { return false; } + virtual bool isNonAutoDimension() const { return false; } + virtual bool isDimension() const { return false; } + + /// All methods below that Object::DataHolder which is union holding actual object content. + /// Read as "data for Object of current type". + + // These methods force a conversion to the appropriate type. We try to return + // plausible defaults whenever possible. + virtual std::string asString(const Object::DataHolder&) const { return ""; } + virtual bool asBoolean(const Object::DataHolder& dataHolder) const { return truthy(dataHolder); } + virtual double asNumber(const Object::DataHolder&) const { + return std::numeric_limits::quiet_NaN(); + } + virtual int asInt(const Object::DataHolder&, int base) const { return 0; } + virtual int64_t asInt64(const Object::DataHolder&, int base) const { return 0; } + virtual Color asColor(const Object::DataHolder&, const SessionPtr&) const; + virtual Dimension asDimension(const Object::DataHolder&, const Context&) const; + virtual Dimension asAbsoluteDimension(const Object::DataHolder&, const Context&) const; + virtual Dimension asNonAutoDimension(const Object::DataHolder&, const Context&) const; + virtual Dimension asNonAutoRelativeDimension(const Object::DataHolder&, const Context&) const; + + // These methods return the actual contents of the Object + /// Primitive types + virtual const std::string& getString(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual bool getBoolean(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual double getDouble(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual double getAbsoluteDimension(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual double getRelativeDimension(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual uint32_t getColor(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + + /// ObjectData held types. + template + const T& get(const Object::DataHolder& dataHolder) const { + assert(is()); + assert(T::ObjectType::scStorageType == Object::StorageType::kStorageTypeReference); + return *static_cast(dataHolder.data->inner()); + } + + virtual const ObjectMap& getMap(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual ObjectMap& getMutableMap(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual const ObjectArray& getArray(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual ObjectArray& getMutableArray(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + + virtual std::shared_ptr getLiveDataObject(const Object::DataHolder&) const { + aplThrow(NOT_SUPPORTED_ERROR); + } + + virtual bool truthy(const Object::DataHolder&) const { return false; } + + // MAP objects + virtual Object get(const Object::DataHolder&, const std::string&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual bool has(const Object::DataHolder&, const std::string&) const { aplThrow(NOT_SUPPORTED_ERROR); } + virtual Object opt(const Object::DataHolder&, const std::string&, const Object&) const { + aplThrow(NOT_SUPPORTED_ERROR); + } + + // ARRAY objects + virtual Object at(const Object::DataHolder&, std::uint64_t) const { aplThrow(NOT_SUPPORTED_ERROR); } + + // MAP, ARRAY, and STRING objects + virtual std::uint64_t size(const Object::DataHolder&) const { return 0; } + + // NULL, MAP, ARRAY, RECT, and STRING objects + virtual bool empty(const Object::DataHolder&) const { return false; } + + // Mutable objects + virtual bool isMutable(const Object::DataHolder&) const { return false; } + + // BoundSymbol, and compiled ByteCodeInstruction objects + virtual Object eval(const Object::DataHolder&) const { aplThrow(NOT_SUPPORTED_ERROR); } + + // FUNCTION & Easing objects + virtual Object call(const Object::DataHolder&, const ObjectArray&) const { aplThrow(NOT_SUPPORTED_ERROR); } + + // Current object hash + virtual std::size_t hash(const Object::DataHolder&) const { return 0; } + + // Visitor pattern + virtual void accept(const Object::DataHolder&, Visitor& visitor) const {} + + // Serialize to JSON format + virtual rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType&) const { return {}; } + + // Convert this to a printable string. Not to be confused with "asString" or "getString" + virtual std::string toDebugString(const Object::DataHolder&) const { return ""; } + + /** + * Check if data in 2 objects are equal. + * @param lhs DataHolder of object of current type. + * @param rhs Other Object's DataHolder. + * @return + */ + virtual bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const { return false; } + +private: + friend class Object; + + virtual Object::StorageType storageType() const = 0; +}; + +using ObjectTypeRef = const ObjectType*; + +/// Base type "classes" + +#define STORAGE_TYPE(T) \ + static const Object::StorageType scStorageType = Object::StorageType::T; \ + private: \ + Object::StorageType storageType() const override { return Object::StorageType::T; } + +template +class BaseObjectType : public ObjectType { +public: + static ObjectTypeRef instance() { + static typename T::ObjectType sObjectType; + return &sObjectType; + } + + STORAGE_TYPE(kStorageTypeValue); +}; + +template +class TrueObjectType : public BaseObjectType { +public: + bool truthy(const Object::DataHolder&) const override { return true; } +}; + +template +class ReferenceHolderObjectType : public BaseObjectType { +public: + bool truthy(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->truthy(); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const final { + return dataHolder.data->serialize(allocator); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->toDebugString(); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *lhs.data == *rhs.data; + } + + bool empty(const Object::DataHolder& dataHolder) const override { + return dataHolder.data->empty(); + } + + static std::shared_ptr createDirectObjectData(T&& content) { + return DirectObjectData::create(std::move(content)); + } + + STORAGE_TYPE(kStorageTypeReference); +}; + +template +class PointerHolderObjectType : public TrueObjectType { +public: + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override { + return dataHolder.data->serialize(allocator); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return dataHolder.data->toDebugString(); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return *lhs.data == *rhs.data; + } + + bool empty(const Object::DataHolder& dataHolder) const override { + return dataHolder.data->empty(); + } + + std::uint64_t size(const Object::DataHolder& dataHolder) const override { + return dataHolder.data->size(); + } + + STORAGE_TYPE(kStorageTypePointer); +}; + +template +class ContainerObjectType : public PointerHolderObjectType { +public: + bool isMutable(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->isMutable(); + } + + void accept(const Object::DataHolder& dataHolder, Visitor& visitor) const final { + dataHolder.data->accept(visitor); + } +}; + +template +class MapLikeObjectType : public ContainerObjectType { +public: + bool isMap() const final { return true; } + + Object get(const Object::DataHolder& dataHolder, const std::string& key) const final { + return dataHolder.data->get(key); + } + + bool has(const Object::DataHolder& dataHolder, const std::string& key) const final { + return dataHolder.data->has(key); + } + + Object opt(const Object::DataHolder& dataHolder, const std::string& key, const Object& def) const final { + return dataHolder.data->opt(key, def); + } +}; + +template +class MapObjectType : public MapLikeObjectType { +public: + bool isTrueMap() const final { return true; } + + const ObjectMap& getMap(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->getMap(); + } + + ObjectMap& getMutableMap(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->getMutableMap(); + } + + std::shared_ptr + getLiveDataObject(const Object::DataHolder& dataHolder) const override { return nullptr; } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const final { + rapidjson::Value m(rapidjson::kObjectType); + for (auto& kv : dataHolder.data->getMap()) + m.AddMember(rapidjson::Value(kv.first.c_str(), allocator), + kv.second.serialize(allocator).Move(), allocator); + return m; + } +}; + +template +class ArrayObjectType : public ContainerObjectType { +public: + bool isArray() const final { return true; } + + const ObjectArray& getArray(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->getArray(); + } + + ObjectArray& getMutableArray(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->getMutableArray(); + } + + std::shared_ptr getLiveDataObject( + const Object::DataHolder& dataHolder) const override + { + return nullptr; + } + + Object at(const Object::DataHolder& dataHolder, std::uint64_t index) const final { + return dataHolder.data->at(index); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const final + { + rapidjson::Value v(rapidjson::kArrayType); + for (std::uint64_t i = 0 ; i < ContainerObjectType::size(dataHolder) ; i++) + v.PushBack(at(dataHolder, i).serialize(allocator), allocator); + return v; + } +}; + +template +class EvaluableObjectType : public PointerHolderObjectType { +public: + bool isEvaluable() const final { return true; } + + Object eval(const Object::DataHolder& dataHolder) const final { + return dataHolder.data->eval(); + } +}; + +/// Primitive types + +class Null { +public: + class ObjectType final : public BaseObjectType { + public: + bool empty(const Object::DataHolder&) const override { return true; } + + std::string toDebugString(const Object::DataHolder&) const override { return "null"; } + + bool equals(const Object::DataHolder&, const Object::DataHolder&) const override { return true; } + + STORAGE_TYPE(kStorageTypeEmpty); + }; +}; + +class Boolean { +public: + class ObjectType final : public BaseObjectType { + public: + std::string asString(const Object::DataHolder& dataHolder) const override { + return static_cast(dataHolder.value) ? "true": "false"; + } + + double asNumber(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + int asInt(const Object::DataHolder& dataHolder, int base) const override { + return static_cast(dataHolder.value); + } + + int64_t asInt64(const Object::DataHolder& dataHolder, int base) const override { + return static_cast(dataHolder.value); + } + + bool getBoolean(const Object::DataHolder& dataHolder) const override { + return dataHolder.value != 0; + } + + bool truthy(const Object::DataHolder& dataHolder) const override { + return dataHolder.value != 0; + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(truthy(dataHolder)); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value(static_cast(dataHolder.value)); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return (bool)(dataHolder.value) ? "true" : "false"; + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return lhs.value == rhs.value; + } + }; +}; + +class Number { +public: + class ObjectType final : public BaseObjectType { + public: + std::string asString(const Object::DataHolder& dataHolder) const override { + return doubleToAplFormattedString(dataHolder.value); + } + + double asNumber(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + int asInt(const Object::DataHolder& dataHolder, int base) const override { + return static_cast(std::lround(dataHolder.value)); + } + + int64_t asInt64(const Object::DataHolder& dataHolder, int base) const override { + return std::llround(dataHolder.value); + } + + Color asColor(const Object::DataHolder& dataHolder, const SessionPtr&) const override; + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context&) const override; + + Dimension asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context&) const override; + + Dimension asNonAutoDimension(const Object::DataHolder& dataHolder, const Context&) const override; + + Dimension asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context&) const override; + + double getDouble(const Object::DataHolder& dataHolder) const override { + return dataHolder.value; + } + + bool truthy(const Object::DataHolder& dataHolder) const override { + return dataHolder.value != 0; + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(dataHolder.value); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return std::isfinite(dataHolder.value) ? rapidjson::Value(dataHolder.value) + : rapidjson::Value(); + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return sutil::to_string(dataHolder.value); + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return lhs.value == rhs.value; + } + }; +}; + +class String { +public: + class ObjectType final : public BaseObjectType { + public: + std::string asString(const Object::DataHolder& dataHolder) const override { + return dataHolder.string; + } + + double asNumber(const Object::DataHolder& dataHolder) const override { + return aplFormattedStringToDouble(dataHolder.string); + } + + int asInt(const Object::DataHolder& dataHolder, int base) const override { + return sutil::stoi(dataHolder.string, nullptr, base); + } + + int64_t asInt64(const Object::DataHolder& dataHolder, int base) const override { + return sutil::stoll(dataHolder.string, nullptr, base); + } + + Color asColor(const Object::DataHolder& dataHolder, const SessionPtr& session) const override; + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asNonAutoDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + const std::string& getString(const Object::DataHolder& dataHolder) const override { + return dataHolder.string; + } + + bool truthy(const Object::DataHolder& dataHolder) const override { + return !dataHolder.string.empty(); + } + + std::uint64_t size(const Object::DataHolder& dataHolder) const override { + return dataHolder.string.size(); + } + + bool empty(const Object::DataHolder& dataHolder) const override { + return dataHolder.string.empty(); + } + + std::size_t hash(const Object::DataHolder& dataHolder) const override { + return std::hash{}(dataHolder.string); + } + + rapidjson::Value serialize( + const Object::DataHolder& dataHolder, + rapidjson::Document::AllocatorType& allocator) const override + { + return {dataHolder.string.c_str(), allocator}; + } + + std::string toDebugString(const Object::DataHolder& dataHolder) const override { + return "'" + dataHolder.string + "'"; + } + + bool equals(const Object::DataHolder& lhs, const Object::DataHolder& rhs) const override { + return lhs.string == rhs.string; + } + + STORAGE_TYPE(kStorageTypeString); + }; +}; + +class Map { +public: + class ObjectType : public MapObjectType {}; +}; + +class Array { +public: + class ObjectType : public ArrayObjectType {}; +}; + +} // namespace apl + +#endif // _APL_OBJECT_MAGIC_TYPE_H diff --git a/aplcore/include/apl/primitives/radii.h b/aplcore/include/apl/primitives/radii.h index c211f3f..6ee41d9 100644 --- a/aplcore/include/apl/primitives/radii.h +++ b/aplcore/include/apl/primitives/radii.h @@ -22,6 +22,7 @@ #include "rapidjson/document.h" +#include "apl/primitives/objecttype.h" #include "apl/utils/deprecated.h" namespace apl { @@ -104,7 +105,7 @@ class Radii { * @param corner The corner to return * @return The radius */ - float radius(Corner corner) { return mData[corner]; } + float radius(Corner corner) const { return mData[corner]; } /** * Compare two sets of radii for inequality @@ -159,6 +160,8 @@ class Radii { bool empty() const { return mData[0] == 0 && mData[1] == 0 && mData[2] == 0 && mData[3] == 0; } bool truthy() const { return !empty(); } + class ObjectType final : public ReferenceHolderObjectType {}; + private: void sanitize(); diff --git a/aplcore/include/apl/primitives/range.h b/aplcore/include/apl/primitives/range.h index 7b90474..48128d4 100644 --- a/aplcore/include/apl/primitives/range.h +++ b/aplcore/include/apl/primitives/range.h @@ -18,9 +18,13 @@ #include #include +#include #include + #include +#include "apl/primitives/objecttype.h" + namespace apl { /** @@ -293,6 +297,8 @@ class Range rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const; + class ObjectType final : public ReferenceHolderObjectType {}; + private: int mLowerBound; int mUpperBound; diff --git a/aplcore/include/apl/primitives/rect.h b/aplcore/include/apl/primitives/rect.h index f8bb45f..d9dda0a 100644 --- a/aplcore/include/apl/primitives/rect.h +++ b/aplcore/include/apl/primitives/rect.h @@ -18,12 +18,15 @@ #include "rapidjson/document.h" +#include "apl/primitives/objecttype.h" #include "apl/primitives/point.h" #include "apl/primitives/size.h" #include "apl/utils/deprecated.h" namespace apl { +class Transform2D; + /** * A simple rectangle class. A rectangle has a left, top, width, and height. The width and height * will always be non-negative. @@ -254,6 +257,13 @@ class Rect */ std::string toString() const; + /** + * Calculate the bounding box of the rectangle after transformation + * @param transform The transform to apply + * @return The axis-aligned bounding box + */ + Rect boundingBox(const Transform2D& transform) const; + /** * Serialize into JSON format * @param allocator RapidJSON memory allocator @@ -263,6 +273,8 @@ class Rect std::string toDebugString() const; + class ObjectType final : public ReferenceHolderObjectType {}; + private: float mX; float mY; diff --git a/aplcore/include/apl/primitives/styledtext.h b/aplcore/include/apl/primitives/styledtext.h index 430990e..381f02b 100644 --- a/aplcore/include/apl/primitives/styledtext.h +++ b/aplcore/include/apl/primitives/styledtext.h @@ -18,15 +18,13 @@ #ifndef _APL_STYLED_TEXT_H #define _APL_STYLED_TEXT_H -#include "apl/primitives/object.h" - #include #include #include #include -#include "apl/primitives/object.h" +#include "apl/primitives/objecttype.h" namespace apl { @@ -264,6 +262,31 @@ class StyledText { bool operator==(const StyledText& rhs) const { return mRawText == rhs.mRawText; } + class ObjectType final : public ReferenceHolderObjectType { + public: + std::string asString(const Object::DataHolder& dataHolder) const override; + + double asNumber(const Object::DataHolder& dataHolder) const override; + + int asInt(const Object::DataHolder& dataHolder, int base) const override; + + int64_t asInt64(const Object::DataHolder& dataHolder, int base) const override; + + Color asColor(const Object::DataHolder& dataHolder, const SessionPtr& session) const override; + + Dimension asDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asNonAutoDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + Dimension asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context& context) const override; + + std::uint64_t size(const Object::DataHolder& dataHolder) const override; + + std::size_t hash(const Object::DataHolder& dataHolder) const override; + }; + private: StyledText() = default; explicit StyledText(const std::string& raw); diff --git a/aplcore/include/apl/primitives/textmeasurerequest.h b/aplcore/include/apl/primitives/textmeasurerequest.h index 80d0ce5..d1213ed 100644 --- a/aplcore/include/apl/primitives/textmeasurerequest.h +++ b/aplcore/include/apl/primitives/textmeasurerequest.h @@ -36,7 +36,7 @@ struct TextMeasureRequest { YGMeasureMode heightMode; std::string paramHash; - size_t hash() const { + std::size_t hash() const { auto result = std::hash{}(paramHash); hashCombine(result, width); hashCombine(result, widthMode); diff --git a/aplcore/include/apl/primitives/timegrammar.h b/aplcore/include/apl/primitives/timegrammar.h index 3f0c0fb..04a9838 100644 --- a/aplcore/include/apl/primitives/timegrammar.h +++ b/aplcore/include/apl/primitives/timegrammar.h @@ -22,8 +22,9 @@ #include #include -#include "apl/utils/log.h" +#include "apl/datagrammar/grammarpolyfill.h" #include "apl/primitives/timefunctions.h" +#include "apl/utils/log.h" namespace apl { @@ -117,7 +118,7 @@ struct grammar : must< raw, pegtl::eof > {}; template struct action : pegtl::nothing {}; -struct time_state { +struct time_state : fail_state { time_state(double time) : mTime(static_cast(time)) {} void append(int number) { diff --git a/aplcore/include/apl/primitives/transform.h b/aplcore/include/apl/primitives/transform.h index e764960..d81e4e2 100644 --- a/aplcore/include/apl/primitives/transform.h +++ b/aplcore/include/apl/primitives/transform.h @@ -16,8 +16,8 @@ #ifndef _APL_TRANSFORM_H #define _APL_TRANSFORM_H -#include "object.h" -#include "transform2d.h" +#include "apl/primitives/objecttype.h" +#include "apl/primitives/transform2d.h" #include "apl/primitives/dimension.h" namespace apl { @@ -39,8 +39,6 @@ class Transform { */ virtual Transform2D evaluate(float width, float height) const = 0; - virtual bool canInterpolate(Transform& other) const = 0; - virtual Transform2D interpolate(Transform& other, float alpha, float width, float height) const = 0; virtual Type getType() const = 0; @@ -53,7 +51,7 @@ class Transform { * We rotate, scale, and skew about the origin of a component. We need the WIDTH and HEIGHT in order to interpret * relative dimensions. */ -class Transformation { +class Transformation : public ObjectData { public: virtual ~Transformation() {} @@ -65,6 +63,8 @@ class Transformation { */ static std::shared_ptr create(const Context& context, const std::vector& array); + // Disambiguate virtual below. + Object get(const std::string &key) const override { return Object::NULL_OBJECT(); } /** * Calculate the transformation, given a width and height of the component @@ -73,6 +73,20 @@ class Transformation { * @return The transformation */ virtual Transform2D get(float width, float height) = 0; + + std::string toDebugString() const override { + return "Transform<>"; + } + + class ObjectType final : public PointerHolderObjectType { + public: + rapidjson::Value serialize( + const Object::DataHolder&, + rapidjson::Document::AllocatorType& allocator) const override + { + return rapidjson::Value("TRANSFORM", allocator); + } + }; }; /** diff --git a/aplcore/include/apl/primitives/transform2d.h b/aplcore/include/apl/primitives/transform2d.h index dbb51f4..a6eca41 100644 --- a/aplcore/include/apl/primitives/transform2d.h +++ b/aplcore/include/apl/primitives/transform2d.h @@ -25,6 +25,7 @@ #include "apl/common.h" +#include "apl/primitives/objecttype.h" #include "apl/primitives/point.h" #include "apl/primitives/rect.h" #include "rapidjson/document.h" @@ -219,7 +220,7 @@ class Transform2D { * * @return A new rectangle representing the axis aligned bounding box. */ - Rect calculateAxisAlignedBoundingBox(const Rect& rect); + Rect calculateAxisAlignedBoundingBox(const Rect& rect) const; /** * Product of two transforms @@ -260,6 +261,27 @@ class Transform2D { lhs.mData[1] * rhs.getX() + lhs.mData[3] * rhs.getY() + lhs.mData[5]}; } + /** + * Apply this transform to a vector of the form [x1, y1, x2, y2, ..., xn, yn] + * @param points The vector of x,y pairs + * @return A vector where each x,y pair is transformed. + */ + friend std::vector operator*(const Transform2D& lhs, const std::vector& points) { + auto len = points.size(); + std::vector result(len); + auto *in = points.data(); + auto out = result.begin(); + + while (len > 1) { + *out++ = lhs.mData[0] * in[0] + lhs.mData[2] * in[1] + lhs.mData[4]; + *out++ = lhs.mData[1] * in[0] + lhs.mData[3] * in[1] + lhs.mData[5]; + in += 2; + len -= 2; + } + + return result; + } + /** * @return True if this transformation is singular */ @@ -277,7 +299,7 @@ class Transform2D { */ Transform2D inverse() const { if (empty()) - return Transform2D(); + return {}; float d = mData[0] * mData[3] - mData[1] * mData[2]; return Transform2D({mData[3] / d, -mData[1] / d, -mData[2] / d, mData[0] / d, @@ -323,7 +345,7 @@ class Transform2D { // Avoid serializing NaN that could be present in transforms by // serializing the singular transform [0, 0, 0, 0, 0, 0] instead // TODO: serialize the original transform instead, but using a different JSON encoding - for (int i = 0; i < mData.size(); i++) { + for (std::size_t i = 0; i < mData.size(); i++) { v.PushBack(0.0f, allocator); } } else { @@ -356,8 +378,21 @@ class Transform2D { return true; } + /** + * Calculate the bounding box of a unit square after transformation. + * @return The width and height the covering bounding box + */ + Point scaleExpansion() const { + return {std::max({0.0f, mData[0], mData[2], mData[0] + mData[2]}) - + std::min({0.0f, mData[0], mData[2], mData[0] + mData[2]}), + std::max({0.0f, mData[1], mData[3], mData[1] + mData[3]}) - + std::min({0.0f, mData[1], mData[3], mData[1] + mData[3]})}; + } + std::string toDebugString() const; + class ObjectType final : public ReferenceHolderObjectType {}; + private: std::array mData; }; diff --git a/aplcore/include/apl/primitives/urlrequest.h b/aplcore/include/apl/primitives/urlrequest.h index 7756640..d2fb2e6 100644 --- a/aplcore/include/apl/primitives/urlrequest.h +++ b/aplcore/include/apl/primitives/urlrequest.h @@ -16,7 +16,7 @@ #ifndef APL_URLREQUEST_H #define APL_URLREQUEST_H -#include "apl/primitives/object.h" +#include "apl/primitives/objecttype.h" #include #include @@ -69,16 +69,14 @@ class URLRequest { static Object create(const Context& context, const Object& object); /** - * Builds a URLRequest. - * - * This method should not be used directly, use URLRequest#create instead. + * Build a URLRequest from an URI string. * - * Using the constructor will not pre-filter headers. - * - * @param url with the location to obtain the source - * @param headers vector of key/value pairs with http/s headers to use for the header. + * @param url URI string. + * @return An object containing a source. */ - URLRequest(const std::string url, const HeaderArray headers); + static Object create(const std::string& url); + + static URLRequest asURLRequest(const Object& object); /** * @return Get request url. @@ -98,6 +96,21 @@ class URLRequest { bool empty() const { return false; } bool truthy() const { return true; } + class ObjectType final : public ReferenceHolderObjectType {}; + +private: + /** + * Builds a URLRequest. + * + * This method should not be used directly, use URLRequest#create instead. + * + * Using the constructor will not pre-filter headers. + * + * @param url with the location to obtain the source + * @param headers vector of key/value pairs with http/s headers to use for the header. + */ + URLRequest(const std::string& url, HeaderArray headers); + private: std::string mUrl; HeaderArray mHeaders; diff --git a/aplcore/include/apl/scenegraph/accessibility.h b/aplcore/include/apl/scenegraph/accessibility.h index 32a9cb9..cc5960b 100644 --- a/aplcore/include/apl/scenegraph/accessibility.h +++ b/aplcore/include/apl/scenegraph/accessibility.h @@ -70,7 +70,6 @@ class Accessibility { Role getRole() const { return mRole; } void appendAction(const std::string& name, const std::string& label, bool enabled); - void removeAllActions(); const std::vector& actions() const { return mActions; } diff --git a/aplcore/include/apl/scenegraph/builder.h b/aplcore/include/apl/scenegraph/builder.h index 8b8e09b..082d0b3 100644 --- a/aplcore/include/apl/scenegraph/builder.h +++ b/aplcore/include/apl/scenegraph/builder.h @@ -50,7 +50,6 @@ class stroke { LayerPtr layer(const std::string& name, Rect bounds, float opacity, Transform2D transform); -NodePtr generic(); NodePtr draw(PathPtr path, PathOpPtr op); NodePtr text(TextLayoutPtr textLayout, PathOpPtr op); NodePtr text(TextLayoutPtr textLayout, PathOpPtr op, Range range); diff --git a/aplcore/include/apl/scenegraph/layer.h b/aplcore/include/apl/scenegraph/layer.h index d3942dd..ea4936e 100644 --- a/aplcore/include/apl/scenegraph/layer.h +++ b/aplcore/include/apl/scenegraph/layer.h @@ -88,9 +88,8 @@ class Layer : public Counter, void appendChild(const LayerPtr& layer); const std::vector& children() const { return mChildren; } - void clearContent(); - void appendContent(const NodePtr& node); - const std::vector& content() const { return mContent; } + void setContent(const NodePtr& node); + const NodePtr& content() const { return mContent; } /** * The bounds of the layer are its outline and position relative to the containing layer. @@ -125,7 +124,6 @@ class Layer : public Counter, /** * The child transformation is relative to the center of the bounds - * TODO: This might be better as a Point offset */ bool setChildOffset(Point childOffset); Point getChildOffset() const { return mChildOffset; } @@ -150,7 +148,7 @@ class Layer : public Counter, private: std::string mName; std::vector mChildren; - std::vector mContent; + NodePtr mContent; Rect mBounds; // The bounds of the layer. The position is the top-left corner in the parent diff --git a/aplcore/include/apl/scenegraph/modifiednodelist.h b/aplcore/include/apl/scenegraph/modifiednodelist.h index 27ac7eb..3e45f77 100644 --- a/aplcore/include/apl/scenegraph/modifiednodelist.h +++ b/aplcore/include/apl/scenegraph/modifiednodelist.h @@ -34,8 +34,6 @@ class ModifiedNodeList { ~ModifiedNodeList(); void contentChanged(Node *node); - void childChanged(Node *node); - void clear(); private: diff --git a/aplcore/include/apl/scenegraph/node.h b/aplcore/include/apl/scenegraph/node.h index fa971dd..6429a71 100644 --- a/aplcore/include/apl/scenegraph/node.h +++ b/aplcore/include/apl/scenegraph/node.h @@ -24,6 +24,7 @@ #include "apl/component/componentproperties.h" #include "apl/component/textmeasurement.h" #include "apl/primitives/range.h" +#include "apl/primitives/rect.h" #include "apl/primitives/transform2d.h" #include "apl/utils/counter.h" #include "apl/utils/noncopyable.h" @@ -56,7 +57,6 @@ class Node : public Counter, public: enum Type { - kGeneric, kTransform, kClip, kOpacity, @@ -76,10 +76,16 @@ class Node : public Counter, Type type() const { return mType; } /** - * Add a child to this node. This is not a fast operation if there are many children - * @param child The child to add + * Set the child of this node. + * @param child The child */ - void appendChild(const NodePtr& child); + void setChild(const NodePtr& child); + + /** + * Set the sibling of this node + * @param sibling The new sibling + */ + NodePtr setNext(const NodePtr& sibling); /** * Remove all children from this node @@ -122,6 +128,19 @@ class Node : public Counter, */ virtual bool visible() const; + /** + * Calculate the bounding box of this node + * @return The bounding box of this node and all of its children + */ + virtual Rect boundingBox(const Transform2D& transform) const; + + /** + * Calculate the bounding box of a node and all siblings + * @param node The node to start with + * @return A rectangle that just encloses all of this node, siblings, and children + */ + static Rect calculateBoundingBox(const NodePtr& node, const Transform2D& transform = Transform2D()); + /** * Serialize this node * @param allocator RapidJSON memory allocator @@ -168,13 +187,6 @@ class Node : public Counter, std::string toDebugString() const override;\ rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const override -/** - * A generic node that holds children. - */ -class GenericNode : public Node { - NODE_SUBCLASS(GenericNode, kGeneric); -}; - /** * Draw geometry on the screen. Contains a path to draw and one or more drawing operations. * Normally doesn't have children. @@ -190,6 +202,7 @@ class DrawNode : public Node { bool needsRedraw() const override; bool visible() const override; + Rect boundingBox(const Transform2D& transform) const override; private: PathPtr mPath; @@ -215,6 +228,7 @@ class TextNode : public Node { bool needsRedraw() const override; bool visible() const override; + Rect boundingBox(const Transform2D& transform) const override; private: TextLayoutPtr mTextLayout; @@ -230,6 +244,7 @@ class TransformNode : public Node { bool setTransform(Transform2D transform); const Transform2D& getTransform() const { return mTransform; } + Rect boundingBox(const Transform2D& transform) const override; private: Transform2D mTransform; @@ -243,6 +258,7 @@ class ClipNode : public Node { bool setPath(PathPtr path); PathPtr getPath() const { return mPath; } + Rect boundingBox(const Transform2D& transform) const override; private: PathPtr mPath; @@ -282,6 +298,7 @@ class ImageNode : public Node { bool needsRedraw() const override; bool visible() const override; + Rect boundingBox(const Transform2D& transform) const override; private: FilterPtr mImage; @@ -306,6 +323,7 @@ class VideoNode : public Node { bool needsRedraw() const override; bool visible() const override; + Rect boundingBox(const Transform2D& transform) const override; private: MediaPlayerPtr mPlayer; @@ -321,6 +339,7 @@ class ShadowNode : public Node { bool setShadow(ShadowPtr shadow); ShadowPtr getShadow() const { return mShadow; } + Rect boundingBox(const Transform2D& transform) const override; private: ShadowPtr mShadow; @@ -346,6 +365,7 @@ class EditTextNode : public Node { bool needsRedraw() const override; bool visible() const override; + Rect boundingBox(const Transform2D& transform) const override; private: EditTextPtr mEditText; diff --git a/aplcore/include/apl/scenegraph/paint.h b/aplcore/include/apl/scenegraph/paint.h index fe32e6e..4db64db 100644 --- a/aplcore/include/apl/scenegraph/paint.h +++ b/aplcore/include/apl/scenegraph/paint.h @@ -22,6 +22,8 @@ #include "apl/utils/userdata.h" #include "apl/primitives/color.h" #include "apl/primitives/gradient.h" +#include "apl/primitives/size.h" +#include "apl/primitives/transform2d.h" namespace apl { namespace sg { diff --git a/aplcore/include/apl/scenegraph/path.h b/aplcore/include/apl/scenegraph/path.h index a0bfc8f..3f689d6 100644 --- a/aplcore/include/apl/scenegraph/path.h +++ b/aplcore/include/apl/scenegraph/path.h @@ -60,7 +60,8 @@ class Path : public Counter, } /** - * @return True if this path has nothing to draw + * @return True if this path has no segments. Rectangular paths ALWAYS have segments, + * even if the length of those segments are zero. */ virtual bool empty() const = 0; @@ -86,6 +87,13 @@ class Path : public Counter, */ virtual rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const = 0; + /** + * Calculate the axis-aligned bounding box of this path. + * @param A transformation to apply before calculating the bounding box + * @return An axis-aligned rectangle containing the path. + */ + virtual Rect boundingBox(const Transform2D& transform) const = 0; + protected: explicit Path(Type type) : mType(type) {} @@ -112,7 +120,8 @@ class Path : public Counter, static bool is_type(const PathPtr& path) { return is_type(path.get()); } \ std::string toDebugString() const override; \ bool empty() const override; \ - rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const override + Rect boundingBox(const Transform2D& transform) const override; \ + rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const override; /** * A rectangular path. @@ -162,17 +171,26 @@ class FramePath : public Path { /** * A path specified by the AVG path data string description + * + * The value string supports the following single-character codes: + * + * C: Cubic bezier with 6 data points: CP1x, CP1y, CP2x, CP2y, X, Y + * L: Line to with 2 data points: X, Y + * M: Move to with 2 data points: X, Y + * Q: Quadratic bezier with 4 data points: CPx, CPy, X, Y + * Z: Close path */ class GeneralPath : public Path { PATH_SUBCLASS(GeneralPath, kGeneral); - bool setPaths(const std::string value, const std::vector points); const std::string& getValue() const { return mValue; } const std::vector& getPoints() const { return mPoints; } + friend class MutablePath; + private: - std::string mValue; - std::vector mPoints; + std::string mValue = "M"; + std::vector mPoints = {0,0}; }; diff --git a/aplcore/include/apl/scenegraph/pathbounds.h b/aplcore/include/apl/scenegraph/pathbounds.h new file mode 100644 index 0000000..5cc77ad --- /dev/null +++ b/aplcore/include/apl/scenegraph/pathbounds.h @@ -0,0 +1,57 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#ifndef _APL_SG_PATH_BOUNDS_H +#define _APL_SG_PATH_BOUNDS_H + +#include "apl/primitives/rect.h" +#include "apl/primitives/transform2d.h" + +namespace apl { +namespace sg { + +/** + * Given a series of commands (M=Move, L=LineTo, Q=QuadraticTo, C=CubicTo, Z=Close path + * and a set of points used by those commands, calculate the bounding box of the path. + * If the path has no line segments, the bounding box will be an empty Rectangle. + * + * @param commands The path drawing commands + * @param points A vector of points used by the drawing commands. Each command uses 0, 2, 4, or 6 + * points. + * @return A rectangle that just encloses the path. + */ +Rect calculatePathBounds(const std::string& commands, const std::vector& points); + +/** + * Given a series of commands (M=Move, L=LineTo, Q=QuadraticTo, C=CubicTo, Z=Close path + * and a set of points used by those commands, calculate the bounding box of the path. + * If the path has no line segments, the bounding box will be an empty Rectangle. + * The transform is applied to each pair of points before the bounds are calculated. + * + * @param transform A transform to be pre-applied against the points. + * @param commands The path drawing commands + * @param points A vector of points used by the drawing commands. Each command uses 0, 2, 4, or 6 + * points. + * @return A rectangle that just encloses the path. + */ +Rect calculatePathBounds(const Transform2D& transform, + const std::string& commands, + const std::vector&points); + +} // namespace sg +} // namespace apl + +#endif //_APL_SG_PATH_BOUNDS_H diff --git a/aplcore/include/apl/scenegraph/pathop.h b/aplcore/include/apl/scenegraph/pathop.h index 9eae055..d0c9eaf 100644 --- a/aplcore/include/apl/scenegraph/pathop.h +++ b/aplcore/include/apl/scenegraph/pathop.h @@ -43,7 +43,8 @@ class PathOp : public NonCopyable { virtual ~PathOp() = default; virtual std::string toDebugString() const = 0; - bool visible() const { return paint && paint->visible(); } + virtual bool visible() const { return paint && paint->visible(); } + virtual float maxWidth() const { return 0; } const Type type; PaintPtr paint; @@ -79,6 +80,14 @@ class PathOp : public NonCopyable { class StrokePathOp : public PathOp { PATH_OP_SUBCLASS(StrokePathOp, kStroke); + bool visible() const override { return strokeWidth > 0 && PathOp::visible(); } + float maxWidth() const override { + // Miter joins stick out by a multiple of the line width + if (lineJoin == kGraphicLineJoinMiter) + return miterLimit * strokeWidth; + return strokeWidth; + } + float strokeWidth = 1.0; float miterLimit = 4.0; float pathLength = 0.0; diff --git a/aplcore/include/apl/scenegraph/pathparser.h b/aplcore/include/apl/scenegraph/pathparser.h index d1a6679..717cbba 100644 --- a/aplcore/include/apl/scenegraph/pathparser.h +++ b/aplcore/include/apl/scenegraph/pathparser.h @@ -22,6 +22,13 @@ namespace apl { namespace sg { +/** + * Given an AVG pathData string, calculate an optimal GeneralPath. If the path data is + * malformed the GeneralPath will be empty. + * + * @param path The AVG pathData string + * @return A GeneralPath + */ std::shared_ptr parsePathString(const std::string& path); } // namespace sg diff --git a/aplcore/include/apl/scenegraph/textmeasurement.h b/aplcore/include/apl/scenegraph/textmeasurement.h index 5014f95..e6ee6ff 100644 --- a/aplcore/include/apl/scenegraph/textmeasurement.h +++ b/aplcore/include/apl/scenegraph/textmeasurement.h @@ -47,6 +47,10 @@ class TextMeasurement : public apl::TextMeasurement { float height, MeasureMode heightMode) override { return {0,0}; } float baseline(Component *component, float width, float height) override { return 0; } + +#ifdef SCENEGRAPH + bool sceneGraphCompatible() const override { return true; } +#endif // SCENEGRAPH }; } // namespace sg diff --git a/aplcore/include/apl/touch/gestures/scrollgesture.h b/aplcore/include/apl/touch/gestures/scrollgesture.h index da80559..6017de5 100644 --- a/aplcore/include/apl/touch/gestures/scrollgesture.h +++ b/aplcore/include/apl/touch/gestures/scrollgesture.h @@ -17,6 +17,7 @@ #define _APL_SCROLL_GESTURE_H #include "apl/common.h" +#include "apl/component/scrollablecomponent.h" #include "apl/primitives/object.h" #include "apl/touch/gestures/flinggesture.h" #include "apl/touch/utils/autoscroller.h" @@ -47,7 +48,12 @@ class ScrollGesture : public FlingGesture, private: float toLocalThreshold(float threshold); bool isSlopeWithinTolerance(Point localPosition); - ScrollablePtr getScrollable() const { return std::dynamic_pointer_cast(mActionable); } + // This gesture may happen only on scrollable target. + ScrollablePtr getScrollable() const { + return mActionable->scrollable() + ? std::shared_ptr(mActionable, (ScrollableComponent*)mActionable.get()) + : nullptr; + } double getVelocityLimit(const Point& travel); Point mLastLocalPosition; diff --git a/aplcore/include/apl/utils/dump_object.h b/aplcore/include/apl/utils/dump_object.h index 46f3026..56ce15b 100644 --- a/aplcore/include/apl/utils/dump_object.h +++ b/aplcore/include/apl/utils/dump_object.h @@ -20,6 +20,8 @@ #include "apl/primitives/object.h" +#include "apl/utils/log.h" + namespace apl { class DumpVisitor : public Visitor { diff --git a/aplcore/include/apl/utils/stickyfunctions.h b/aplcore/include/apl/utils/stickyfunctions.h index 86d200c..9be7d97 100644 --- a/aplcore/include/apl/utils/stickyfunctions.h +++ b/aplcore/include/apl/utils/stickyfunctions.h @@ -43,11 +43,19 @@ void updateStickyOffset(const CoreComponentPtr &component); Point calculateStickyOffset(const CoreComponentPtr &component); /** - * Traverse up the ancestors and find the nearest horizontal and vertical scrollables if they exist + * Traverse up the ancestors and find the nearest horizontal and vertical scrollables if they exist, + * starting with parent of component */ std::pair getAncestorHorizontalAndVerticalScrollable(const CoreComponentPtr &component); +/** + * Traverse up the ancestors and find the nearest horizontal and vertical scrollables if they exist, + * starting with component + */ +std::pair +getHorizontalAndVerticalScrollable(const CoreComponentPtr &component); + } // namespace stickyfunctions } // namespace apl diff --git a/aplcore/include/apl/utils/stringfunctions.h b/aplcore/include/apl/utils/stringfunctions.h index 565ddce..dd8a807 100644 --- a/aplcore/include/apl/utils/stringfunctions.h +++ b/aplcore/include/apl/utils/stringfunctions.h @@ -79,6 +79,28 @@ std::string lpad(const std::string &str, std::size_t minWidth, char padChar = ' */ std::string tolower(const std::string& str); +/** + * Return a formatted double for display. The formatted double follows the APL syntax for + * floating-point numbers. Additionally, we drop trailing zeros for decimal numbers. If the number + * is an integer or rounds to an integer, we drop the decimal point as well. + * Scientific notation numbers are not handled attractively. + * + * @param value The value to format + * @return A suitable string + */ +std::string doubleToAplFormattedString(double value); + +/** + * Parse string into double. The formatted double follows the APL syntax for + * floating-point numbers. Additionally, we drop trailing zeros for decimal numbers. If the number + * is an integer or rounds to an integer, we drop the decimal point as well. + * Scientific notation numbers are not handled attractively. + * + * @param string string value + * @return A suitable double. + */ +double aplFormattedStringToDouble(const std::string& string); + /** * sutil:: functions are intended to be locale-independent versions of std:: functions of the same @@ -124,6 +146,29 @@ double stod(const std::string& str, std::size_t* pos = nullptr); */ long double stold(const std::string& str, std::size_t* pos = nullptr); +/** + * Internal utility to convert parse the textual representation of a simple number. + * Intended to be used as an exception-safe alternative to std::stoi. + * + * @param str The string value to parse + * @param pos If non-null, will be set to the index of the first non-parsed character in the input + * string. + * @param base The number base. + * @return the parsed value, or 0 if the string could not be parsed. + */ +int stoi(const std::string& str, std::size_t* pos = nullptr, int base = 10); + +/** + * Internal utility to convert parse the textual representation of a simple number. + * Intended to be used as an exception-safe alternative to std::stoll. + * + * @param str The string value to parse + * @param pos If non-null, will be set to the index of the first non-parsed character in the input + * string. + * @param base The number base. + * @return the parsed value, or 0 if the string could not be parsed. + */ +long long stoll(const std::string& str, std::size_t* pos = nullptr, int base = 10); /** * Internal utility to format a single-precision value as a string. Intended to be used as a diff --git a/aplcore/include/apl/utils/throw.h b/aplcore/include/apl/utils/throw.h new file mode 100644 index 0000000..af47f83 --- /dev/null +++ b/aplcore/include/apl/utils/throw.h @@ -0,0 +1,29 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef APL_THROW_H +#define APL_THROW_H + +namespace apl { + +#if defined( _WIN32 ) +__declspec(noreturn) void aplThrow(const char* message) noexcept; +#else +void aplThrow(const char* message) noexcept __attribute__((__noreturn__)); +#endif + +} + +#endif //APL_THROW_H diff --git a/aplcore/src/action/animatedscrollaction.cpp b/aplcore/src/action/animatedscrollaction.cpp index 6985ed6..5e112c3 100644 --- a/aplcore/src/action/animatedscrollaction.cpp +++ b/aplcore/src/action/animatedscrollaction.cpp @@ -48,9 +48,8 @@ AnimatedScrollAction::scroll(bool vertical, const Point& position) return; } - mScroller = AutoScroller::make(mContext->getRootConfig(), - std::dynamic_pointer_cast(mContainer), + ScrollableComponent::cast(mContainer), []() {}, position - mContainer->scrollPosition(), mDuration); @@ -103,10 +102,13 @@ AnimatedScrollAction::rehydrate(const RootContext& context) if (!ResourceHoldingAction::rehydrate(context)) return false; - mContainer = std::dynamic_pointer_cast(context.findComponentById(mFrozenContainerId)); + mContainer = CoreComponent::cast(context.findComponentById(mFrozenContainerId)); if (!mContainer) return false; - mScroller->replaceTarget(std::dynamic_pointer_cast(mContainer)); + auto scrollable = ScrollableComponent::cast(mContainer); + if (!scrollable) return false; + + mScroller->replaceTarget(scrollable); return mCurrentAction->rehydrate(context); } diff --git a/aplcore/src/action/animateitemaction.cpp b/aplcore/src/action/animateitemaction.cpp index fd4b252..3a8a08a 100644 --- a/aplcore/src/action/animateitemaction.cpp +++ b/aplcore/src/action/animateitemaction.cpp @@ -42,7 +42,7 @@ AnimateItemAction::AnimateItemAction(const TimersPtr& timers, mRepeatCount(command->getValue(kCommandPropertyRepeatCount).asInt()), mRepeatMode(command->getValue(kCommandPropertyRepeatMode).asInt()), mFastMode(fastMode), - mEasing(command->getValue(kCommandPropertyEasing).getEasing()) + mEasing(command->getValue(kCommandPropertyEasing).get()) {} void diff --git a/aplcore/src/action/controlmediaaction.cpp b/aplcore/src/action/controlmediaaction.cpp index d7adb3c..5538731 100644 --- a/aplcore/src/action/controlmediaaction.cpp +++ b/aplcore/src/action/controlmediaaction.cpp @@ -72,7 +72,7 @@ ControlMediaAction::start() auto mediaCommand = static_cast(mCommand->getValue(kCommandPropertyCommand).asInt()); auto value = mCommand->getValue(kCommandPropertyValue); - auto videoComponent = std::dynamic_pointer_cast(mTarget); + auto videoComponent = VideoComponent::cast(mTarget); assert(videoComponent); auto mediaPlayer = videoComponent->getMediaPlayer(); diff --git a/aplcore/src/action/playmediaaction.cpp b/aplcore/src/action/playmediaaction.cpp index ce21652..ef9e2b9 100644 --- a/aplcore/src/action/playmediaaction.cpp +++ b/aplcore/src/action/playmediaaction.cpp @@ -54,7 +54,7 @@ PlayMediaAction::start() auto audioTrack = mCommand->getValue(kCommandPropertyAudioTrack); auto source = mCommand->getValue(kCommandPropertySource); - auto videoComponent = std::dynamic_pointer_cast(mTarget); + auto videoComponent = VideoComponent::cast(mTarget); assert(videoComponent); mPlayer = videoComponent->getMediaPlayer(); @@ -68,6 +68,13 @@ PlayMediaAction::start() mPlayer->setTrackList(mediaSourcesToTracks(source)); mPlayer->setAudioTrack(static_cast(audioTrack.getInteger())); mPlayer->play(shared_from_this()); + // An early termination of the command (for example, by the user touching on the screen) + // will only stop the video playing if the audioTrack is set to foreground + if (audioTrack == kAudioTrackForeground) { + addTerminateCallback([this](const TimersPtr &) { + mPlayer->pause(); + }); + } } else { EventBag bag; @@ -84,7 +91,8 @@ PlayMediaAction::start() void PlayMediaAction::freeze() { - auto videoComponent = std::dynamic_pointer_cast(mTarget); + auto videoComponent = VideoComponent::cast(mTarget); + assert(videoComponent); mPlayingState = videoComponent->getProperty(kPropertyPlayingState); mSource = videoComponent->getProperty(kPropertySource); @@ -114,7 +122,8 @@ PlayMediaAction::rehydrate(const RootContext& context) mTarget = mCommand->target(); // Ensure that source AND playbackState preserved. If not - we recreate the player (as per spec). - auto videoComponent = std::dynamic_pointer_cast(mTarget); + auto videoComponent = VideoComponent::cast(mTarget); + if (!videoComponent) return false; if (mPlayingState != videoComponent->getProperty(kPropertyPlayingState) || mSource != videoComponent->getProperty(kPropertySource)) { mPlayer->release(); diff --git a/aplcore/src/action/scrollaction.cpp b/aplcore/src/action/scrollaction.cpp index 7af1479..e43b00d 100644 --- a/aplcore/src/action/scrollaction.cpp +++ b/aplcore/src/action/scrollaction.cpp @@ -55,7 +55,7 @@ void ScrollAction::start() { // Now set the position auto vertical = (mContainer->scrollType() == kScrollTypeVertical); - auto innerBounds = mContainer->getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = mContainer->getCalculated(kPropertyInnerBounds).get(); auto targetSize = (vertical ? innerBounds.getHeight() : innerBounds.getWidth()); float distance = 0; diff --git a/aplcore/src/action/scrolltoaction.cpp b/aplcore/src/action/scrolltoaction.cpp index c91a77a..9f2585b 100644 --- a/aplcore/src/action/scrolltoaction.cpp +++ b/aplcore/src/action/scrolltoaction.cpp @@ -140,7 +140,7 @@ ScrollToAction::make(const TimersPtr& timers, context, scrollToSubBounds, target, - std::dynamic_pointer_cast(container), + CoreComponent::cast(container), duration >= 0 ? duration : context->getRootConfig().getScrollCommandDuration()); context->sequencer().claimResource({kExecutionResourcePosition, container}, ptr); @@ -193,7 +193,7 @@ ScrollToAction::scrollTo() mSubBounds.getHeight()); } - Rect parentInnerBounds = mContainer->getCalculated(kPropertyInnerBounds).getRect(); + const auto& parentInnerBounds = mContainer->getCalculated(kPropertyInnerBounds).get(); bool vertical = (mContainer->scrollType() == kScrollTypeVertical); bool isLTR = mContainer->getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; @@ -271,7 +271,7 @@ ScrollToAction::pageTo() // the component WITHIN the pager that is either the target or the ancestor of the target. auto component = mTarget; while (component->getParent() != mContainer) - component = std::static_pointer_cast(component->getParent()); + component = CoreComponent::cast(component->getParent()); int targetPage = -1; for (int i = 0 ; i < mContainer->getChildCount() ; i++) { @@ -320,15 +320,15 @@ ScrollToAction::rehydrate(const RootContext& context) { if (!ResourceHoldingAction::rehydrate(context)) return false; - mTarget = std::dynamic_pointer_cast(context.findComponentById(mFrozenTargetId)); + mTarget = CoreComponent::cast(context.findComponentById(mFrozenTargetId)); - mContainer = std::dynamic_pointer_cast(context.findComponentById(mFrozenContainerId)); + mContainer = CoreComponent::cast(context.findComponentById(mFrozenContainerId)); if (!mContainer) { // If not here, rely on mTarget to get the container with usual "scrollable parent" rule, // if no mTarget id is here - we can't restore. if (!mTarget) return false; - mContainer = std::dynamic_pointer_cast(getScrollableParent(mTarget)); + mContainer = CoreComponent::cast(getScrollableParent(mTarget)); if (!mContainer) return false; } diff --git a/aplcore/src/action/speakitemaction.cpp b/aplcore/src/action/speakitemaction.cpp index ac35c67..b839dc4 100644 --- a/aplcore/src/action/speakitemaction.cpp +++ b/aplcore/src/action/speakitemaction.cpp @@ -78,7 +78,7 @@ class SpeakItemActionPrivate { action.mCommand->getValue(kCommandPropertyHighlightMode) == kCommandHighlightModeLine) { // Stash the raw text in lower-case for word comparisons mText = action.mTarget->getRootConfig().getLocaleMethods()->toLowerCase( - action.mTarget->getCalculated(kPropertyText).getStyledText().getText(), ""); + action.mTarget->getCalculated(kPropertyText).get().getText(), ""); } // Create an audio player and queue up the TTS as the track @@ -585,8 +585,7 @@ SpeakItemAction::SpeakItemAction(const TimersPtr& timers, const std::shared_ptr< #ifdef SCENEGRAPH // If a sg::TextMeasurement is installed, we use that to select the text to highlight // Otherwise we use an event mechanism to set highlighted lines - auto measure = std::dynamic_pointer_cast(rootConfig.getMeasure()); - if (measure) + if (rootConfig.getMeasure()->sceneGraphCompatible()) mPrivate = std::make_unique(); else #endif // SCENEGRAPH diff --git a/aplcore/src/action/speaklistaction.cpp b/aplcore/src/action/speaklistaction.cpp index 8ca925f..f9f5bff 100644 --- a/aplcore/src/action/speaklistaction.cpp +++ b/aplcore/src/action/speaklistaction.cpp @@ -124,8 +124,8 @@ SpeakListAction::rehydrate(const RootContext& context) if (start + count > len) mEndIndex = start + (len - start); - auto speakItem = std::dynamic_pointer_cast(mCurrentAction); - if (speakItem) { + if (mCurrentAction) { + auto speakItem = std::static_pointer_cast(mCurrentAction); speakItem->mTarget = mContainer->getCoreChildAt(mNextIndex - 1); if (!speakItem->rehydrate(context)) return false; } diff --git a/aplcore/src/animation/animatedproperty.cpp b/aplcore/src/animation/animatedproperty.cpp index 5a89907..7a44096 100644 --- a/aplcore/src/animation/animatedproperty.cpp +++ b/aplcore/src/animation/animatedproperty.cpp @@ -96,7 +96,7 @@ AnimatedTransform::update(const CoreComponentPtr& component, float alpha) { bool changed = mTransformation->interpolate(alpha); auto assigned = component->getCalculated(kPropertyTransformAssigned); - if (!assigned.isTransform() || assigned.getTransformation() != mTransformation) + if (!assigned.is() || assigned.get() != mTransformation) component->setProperty(kPropertyTransformAssigned, Object(mTransformation)); else if (changed) component->markProperty(kPropertyTransformAssigned); diff --git a/aplcore/src/animation/coreeasing.cpp b/aplcore/src/animation/coreeasing.cpp index 12960d8..11216b1 100644 --- a/aplcore/src/animation/coreeasing.cpp +++ b/aplcore/src/animation/coreeasing.cpp @@ -148,7 +148,7 @@ class PSegment { PSegment(const CoreEasing& easing, std::vector::const_iterator it) : easing(easing), mData(&easing.mPoints.at(it->offset)) {} - int dof() const { return ::abs(static_cast(easing.mPoints.at(0))); } + int dof() const { return std::abs(static_cast(easing.mPoints.at(0))); } int index() const { return static_cast(easing.mPoints.at(1)); } const float* start() const { return &mData[1]; } diff --git a/aplcore/src/animation/easing.cpp b/aplcore/src/animation/easing.cpp index a4f218f..a665a6c 100644 --- a/aplcore/src/animation/easing.cpp +++ b/aplcore/src/animation/easing.cpp @@ -53,17 +53,17 @@ Easing::parse(const SessionPtr& session, const std::string& easing) if (ptr) return ptr; - try { - easinggrammar::easing_state state; - pegtl::string_input<> in(s, ""); - pegtl::parse(in, state); - - auto easingCurve = CoreEasing::create(std::move(state.segments), - std::move(state.args), - s); + easinggrammar::easing_state state; + pegtl::string_input<> in(s, ""); + if (!pegtl::parse(in, state) || state.failed) { + CONSOLE(session) << "Parse error in " << easing << " - " << state.what(); + } else { + auto easingCurve = + CoreEasing::create(std::move(state.segments), std::move(state.args), s); if (!easingCurve) { CONSOLE(session) << "Unable to create easing curve " << easing; - } else { + } + else { if (sEasingCacheDirty) { sEasingCache.clean(); sEasingCacheDirty = false; @@ -73,9 +73,6 @@ Easing::parse(const SessionPtr& session, const std::string& easing) return easingCurve; } } - catch (pegtl::parse_error& e) { - CONSOLE(session) << "Parse error in " << easing << " - " << e.what(); - } return Easing::linear(); } @@ -111,7 +108,7 @@ Easing::call(const ObjectArray& args) const bool Easing::operator==(const ObjectData& other) const { - return *this == dynamic_cast(other); + return *this == static_cast(other); } } // namespace apl diff --git a/aplcore/src/animation/easingapproximation.cpp b/aplcore/src/animation/easingapproximation.cpp index 68c0c29..6c437d0 100644 --- a/aplcore/src/animation/easingapproximation.cpp +++ b/aplcore/src/animation/easingapproximation.cpp @@ -15,6 +15,8 @@ #include "apl/animation/easingapproximation.h" +#include "apl/utils/log.h" + namespace apl { static float cubic( float a, float b, float c, float d, float t) diff --git a/aplcore/src/audio/speechmark.cpp b/aplcore/src/audio/speechmark.cpp index d97f53b..c3e4089 100644 --- a/aplcore/src/audio/speechmark.cpp +++ b/aplcore/src/audio/speechmark.cpp @@ -19,6 +19,8 @@ #include #include +#include "apl/datagrammar/grammarpolyfill.h" + namespace apl { namespace pollygrammar { @@ -80,11 +82,18 @@ struct SMTemp { template struct action : nothing {}; +template +struct polly_control : apl_control< Rule > { + template + static void raise( const Input& /* unused */, States&&... /* unused */) { + // SpeechMarks parsing is not used in APL Core. Rely on return. + } +}; template <> struct action { template - static void apply(const Input& in, std::vector& speechMarks, SMTemp& temp) { + static void apply(const Input& in, SMTemp& temp, std::vector& speechMarks) { if (temp.keyword == "time") speechMarks.back().time = std::stoul(in.string()); else if (temp.keyword == "start") @@ -97,8 +106,10 @@ struct action { template <> struct action : unescape { template - static void success(const Input& /* unused */, std::string& s, - std::vector& speechMarks, SMTemp& temp) { + static void success(const Input& /* unused */, + std::string& s, + SMTemp& temp, + std::vector& speechMarks) { if (temp.keyword == "type") { if (s == "word") speechMarks.back().type = kSpeechMarkWord; @@ -117,8 +128,10 @@ struct action : unescape { template <> struct action : unescape { template - static void success(const Input& /* unused */, std::string& s, - std::vector& speechMarks, SMTemp& temp) { + static void success(const Input& /* unused */, + std::string& s, + SMTemp& temp, + std::vector& speechMarks) { temp.keyword = s; } }; @@ -127,7 +140,7 @@ struct action : unescape { template <> struct action { template - static void apply(const Input& in, std::vector& speechMarks, SMTemp& temp) { + static void apply(const Input& in, SMTemp& temp, std::vector& speechMarks) { speechMarks.emplace_back(SpeechMark{kSpeechMarkUnknown, 0, 0, 0, ""}); } }; @@ -137,11 +150,9 @@ parseData(const char* data, unsigned long length) { std::vector result; pegtl::memory_input<> in(data, length, ""); - try { - pollygrammar::SMTemp temp; - pegtl::parse(in, result, temp); - } - catch (const pegtl::parse_error& e) { + pollygrammar::SMTemp temp; + if (!pegtl::parse(in, temp, result)) { + result.clear(); } return result; } diff --git a/aplcore/src/command/animateitemcommand.cpp b/aplcore/src/command/animateitemcommand.cpp index b9b3354..c6cd7c5 100644 --- a/aplcore/src/command/animateitemcommand.cpp +++ b/aplcore/src/command/animateitemcommand.cpp @@ -23,7 +23,7 @@ AnimateItemCommand::propDefSet() const { static CommandPropDefSet sAnimateItemCommandProperties(CoreCommand::propDefSet(), { {kCommandPropertyComponentId, "", asString, kPropRequiredId}, {kCommandPropertyDuration, 0, asNonNegativeInteger, kPropRequired}, - {kCommandPropertyEasing, Object::LINEAR_EASING(), asEasing}, + {kCommandPropertyEasing, Easing::linear(), asEasing}, {kCommandPropertyRepeatCount, 0, asNonNegativeInteger}, {kCommandPropertyRepeatMode, kCommandRepeatModeRestart, sCommandRepeatModeMap}, {kCommandPropertyValue, Object::EMPTY_ARRAY(), asArray, kPropRequired} diff --git a/aplcore/src/command/corecommand.cpp b/aplcore/src/command/corecommand.cpp index 8a17718..412748f 100644 --- a/aplcore/src/command/corecommand.cpp +++ b/aplcore/src/command/corecommand.cpp @@ -152,11 +152,11 @@ CoreCommand::rehydrate(const RootContext& context) mContext->putConstant("event", Object(std::move(mFrozenEventContext))); if (!mBaseId.empty()) { - mBase = std::static_pointer_cast(context.findComponentById(mBaseId)); + mBase = CoreComponent::cast(context.findComponentById(mBaseId)); if (!mBase) return false; } if (!mTargetId.empty()) { - mTarget = std::static_pointer_cast(context.findComponentById(mTargetId)); + mTarget = CoreComponent::cast(context.findComponentById(mTargetId)); if (!mTarget) return false; } @@ -229,7 +229,7 @@ CoreCommand::calculateProperties() std::string id; if (p != mProperties.end()) { id = evaluate(*mContext, p->second).asString(); - mTarget = std::dynamic_pointer_cast(mContext->findComponentById(id)); + mTarget = CoreComponent::cast(mContext->findComponentById(id)); } if (mTarget == nullptr && (cpd->second.flags & kPropRequired)) { @@ -238,7 +238,7 @@ CoreCommand::calculateProperties() LOG(LogLevel::kWarn).session(mContext) << "Trying to scroll to uninflated component. Flushing pending layouts."; auto& lm = mContext->layoutManager(); lm.flushLazyInflation(); - mTarget = std::dynamic_pointer_cast(mContext->findComponentById(id)); + mTarget = CoreComponent::cast(mContext->findComponentById(id)); if (mTarget == nullptr) { CONSOLE(mContext) << "Illegal command " << name() << " - need to specify a target componentId"; return false; diff --git a/aplcore/src/command/documentcommand.cpp b/aplcore/src/command/documentcommand.cpp index 2be3e0a..a7f83ed 100644 --- a/aplcore/src/command/documentcommand.cpp +++ b/aplcore/src/command/documentcommand.cpp @@ -30,7 +30,7 @@ DocumentCommand::collectChildCommands(const ComponentPtr& base, { auto commands = base->getCalculated(mPropertyKey); if (commands.isArray() && !commands.empty()) { - auto core = std::static_pointer_cast(base); + auto core = CoreComponent::cast(base); auto ctx = core->createDefaultEventContext(mHandler); collection.emplace_back(ArrayCommand::create(ctx, commands, core, Properties(), "")); } diff --git a/aplcore/src/command/extensioneventcommand.cpp b/aplcore/src/command/extensioneventcommand.cpp index b85c956..b099964 100644 --- a/aplcore/src/command/extensioneventcommand.cpp +++ b/aplcore/src/command/extensioneventcommand.cpp @@ -70,7 +70,7 @@ ExtensionEventCommand::propDefSet() const std::string ExtensionEventCommand::getResourceID() const { - auto extensionComponent = std::dynamic_pointer_cast(mTarget); + auto extensionComponent = ExtensionComponent::cast(mTarget); if (extensionComponent) { return extensionComponent->getResourceID(); diff --git a/aplcore/src/command/sendeventcommand.cpp b/aplcore/src/command/sendeventcommand.cpp index fbeac51..71f5f10 100644 --- a/aplcore/src/command/sendeventcommand.cpp +++ b/aplcore/src/command/sendeventcommand.cpp @@ -63,9 +63,13 @@ SendEventCommand::execute(const TimersPtr& timers, bool fastMode) { // Calculate the component map auto componentsMap = std::make_shared(); for (auto& compId : mValues.at(kCommandPropertyComponents).getArray()) { - auto comp = std::dynamic_pointer_cast(mContext->findComponentById(compId.getString())); - if (comp) { - componentsMap->emplace(compId.getString(), comp->getValue()); + if (compId.isString()) { + auto comp = CoreComponent::cast(mContext->findComponentById(compId.getString())); + if (comp) { + componentsMap->emplace(compId.getString(), comp->getValue()); + } + } else { + CONSOLE(mContext) << "SendEvent components array can only contain strings."; } } diff --git a/aplcore/src/component/actionablecomponent.cpp b/aplcore/src/component/actionablecomponent.cpp index 33ffb4f..6cd1961 100644 --- a/aplcore/src/component/actionablecomponent.cpp +++ b/aplcore/src/component/actionablecomponent.cpp @@ -43,6 +43,12 @@ ActionableComponent::propDefSet() const { return sActionableComponentProperties; } +std::shared_ptr +ActionableComponent::cast(const std::shared_ptr& component) { + return component && CoreComponent::cast(component)->isActionable() + ? std::static_pointer_cast(component) : nullptr; +} + void ActionableComponent::executeOnBlur() { auto command = getCalculated(kPropertyOnBlur); @@ -119,7 +125,7 @@ ActionableComponent::executeIntrinsicKeyHandlers(KeyHandlerType type, const Keyb } void -ActionableComponent::release() +ActionableComponent::releaseSelf() { // Avoiding reference loop. if (mActiveGesture) { @@ -127,7 +133,7 @@ ActionableComponent::release() mActiveGesture = nullptr; } mGestureHandlers.clear(); - CoreComponent::release(); + CoreComponent::releaseSelf(); } bool @@ -208,7 +214,7 @@ ActionableComponent::getUserSpecifiedNextFocus(FocusDirection direction) if (it != focusDirectionToNextProperty().end()) { auto componentId = getCalculated(it->second).getString(); if (!componentId.empty()) { - return std::dynamic_pointer_cast(getContext()->findComponentById(componentId)); + return CoreComponent::cast(getContext()->findComponentById(componentId)); } } return nullptr; diff --git a/aplcore/src/component/component.cpp b/aplcore/src/component/component.cpp index 73da30d..3b8f1fd 100644 --- a/aplcore/src/component/component.cpp +++ b/aplcore/src/component/component.cpp @@ -20,7 +20,7 @@ namespace apl { Component::Component(const ContextPtr& context, const std::string& id) - : UIDObject(context), + : UIDObject(context, UIDObject::UIDObjectType::COMPONENT), mId(id) { } @@ -62,11 +62,11 @@ Component::clearDirty() { bool Component::getBoundsInParent(const ComponentPtr& ancestor, Rect& out) const { - out = mCalculated.get(kPropertyBounds).getRect(); + out = mCalculated.get(kPropertyBounds).get(); ComponentPtr parent = getParent(); while (parent && parent != ancestor) { - out.offset(parent->getCalculated(kPropertyBounds).getRect().getTopLeft() - parent->scrollPosition()); + out.offset(parent->getCalculated(kPropertyBounds).get().getTopLeft() - parent->scrollPosition()); parent = parent->getParent(); } return parent == ancestor; diff --git a/aplcore/src/component/corecomponent.cpp b/aplcore/src/component/corecomponent.cpp index 0ceeb1b..b2bb844 100644 --- a/aplcore/src/component/corecomponent.cpp +++ b/aplcore/src/component/corecomponent.cpp @@ -115,9 +115,14 @@ CoreComponent::CoreComponent(const ContextPtr& context, YGNodeSetContext(mYGNodeRef, this); } +std::shared_ptr +CoreComponent::cast( const std::shared_ptr& component) { + return std::static_pointer_cast(component); +} + void CoreComponent::scheduleTickHandler(const Object& handler, double delay) { - auto weak_ptr = std::weak_ptr(std::static_pointer_cast(shared_from_this())); + auto weak_ptr = std::weak_ptr(shared_from_corecomponent()); // Lambda capture takes care of handler here as it's a copy. mContext->getRootConfig().getTimeManager()->setTimeout([weak_ptr, handler, delay]() { auto self = weak_ptr.lock(); @@ -208,13 +213,18 @@ CoreComponent::initialize() void CoreComponent::release() +{ + traverse([](CoreComponent& c) {}, + [](CoreComponent& c) { c.releaseSelf(); }); +} + +void +CoreComponent::releaseSelf() { // TODO: Must remove this component from any dirty lists mContext->layoutManager().remove(shared_from_corecomponent()); RecalculateTarget::removeUpstreamDependencies(); mParent = nullptr; - for (auto& child : mChildren) - child->release(); mChildren.clear(); } @@ -249,6 +259,16 @@ CoreComponent::raccept(Visitor& visitor) const visitor.pop(); } +template +void +CoreComponent::traverse(const Pre& pre, const Post& post) +{ + pre(*this); + for (auto& child : mChildren) + child->traverse(pre, post); + post(*this); +} + ComponentPtr CoreComponent::findComponentById(const std::string& id) const { @@ -326,11 +346,11 @@ CoreComponent::update(UpdateType type, const std::string& value) { // Find the first accessibility action in the array that matches the requested name. const auto& array = accessibilityActions.getArray(); auto it = std::find_if(array.begin(), array.end(), [&](const Object& object) { - return object.getAccessibilityAction()->getName() == value; + return object.get()->getName() == value; }); if (it != array.end()) { - const auto& aa = it->getAccessibilityAction(); + const auto& aa = it->get(); if (aa->enabled()) { const auto& cmds = aa->getCommands(); if (cmds.isArray() && !cmds.empty()) @@ -349,7 +369,7 @@ CoreComponent::update(UpdateType type, const std::string& value) { bool CoreComponent::appendChild(const ComponentPtr& child, bool useDirtyFlag) { - return insertChild(std::dynamic_pointer_cast(child), mChildren.size(), useDirtyFlag); + return insertChild(CoreComponent::cast(child), mChildren.size(), useDirtyFlag); } void @@ -430,7 +450,7 @@ CoreComponent::ensureDisplayedChildren() // Identify this components local viewport using bounds and scroll position // top left of viewport is the most significant offset found when comparing // bounds offset and scroll offset - Rect bounds = getCalculated(kPropertyBounds).getRect(); + const Rect& bounds = getCalculated(kPropertyBounds).get(); // Get viewport offset by scroll Position Rect viewportRect = Rect(0,0,bounds.getWidth(),bounds.getHeight()); @@ -442,8 +462,8 @@ CoreComponent::ensureDisplayedChildren() // only visible children if (child->isDisplayable()) { // compare child rect, transformed as needed, against the viewport - auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - auto transform = child->getCalculated(kPropertyTransform).getTransform2D(); + auto childBounds = child->getCalculated(kPropertyBounds).get(); + const auto& transform = child->getCalculated(kPropertyTransform).get(); // The axis aligned bounding box is an approximation for checking bounds intersection. // The AABB test eliminates children that are guaranteed NOT to intersect. It does not // prove the parent and child do intersect. @@ -475,7 +495,7 @@ CoreComponent::ensureDisplayedChildren() bool CoreComponent::insertChild(const ComponentPtr& child, size_t index) { - return canInsertChild() && insertChild(std::dynamic_pointer_cast(child), index, true); + return canInsertChild() && insertChild(CoreComponent::cast(child), index, true); } bool @@ -490,7 +510,7 @@ CoreComponent::insertChild(const CoreComponentPtr& child, size_t index, bool use if (!child || child->getParent()) return false; - auto coreChild = std::static_pointer_cast(child); + auto coreChild = CoreComponent::cast(child); if (index > mChildren.size()) index = mChildren.size(); @@ -547,11 +567,8 @@ CoreComponent::remove() } void -CoreComponent::removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) +CoreComponent::removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) { - // Release focus for this child and descendants. Also remove them from the dirty set - child->markRemoved(); - YGNodeRemoveChild(mYGNodeRef, child->getNode()); mChildren.erase(mChildren.begin() + index); @@ -565,7 +582,7 @@ CoreComponent::removeChild(const CoreComponentPtr& child, size_t index, bool use setVisualContextDirty(); // Update the position: sticky components tree - auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(child); + auto p = stickyfunctions::getHorizontalAndVerticalScrollable(shared_from_corecomponent()); auto horizontalScrollable = std::get<0>(p); auto verticalScrollable = std::get<1>(p); if (horizontalScrollable && horizontalScrollable->getStickyTree()) @@ -580,7 +597,8 @@ CoreComponent::removeChild(const CoreComponentPtr& child, bool useDirtyFlag) auto it = std::find(mChildren.begin(), mChildren.end(), child); assert(it != mChildren.end()); size_t index = std::distance(mChildren.begin(), it); - removeChild(child, index, useDirtyFlag); + child->traverse([](CoreComponent& c) { c.markSelfRemoved(); }); + removeChildAfterMarkedRemoved(child, index, useDirtyFlag); } void @@ -589,17 +607,18 @@ CoreComponent::removeChildAt(size_t index, bool useDirtyFlag) if (index >= mChildren.size()) return; auto child = mChildren.at(index); - removeChild(child, index, useDirtyFlag); - child->release(); + + child->traverse( + [](CoreComponent& c) { c.markSelfRemoved(); }, + [](CoreComponent& c) { c.releaseSelf(); }); + removeChildAfterMarkedRemoved(child, index, useDirtyFlag); } /** - * This component has been removed from the DOM. Walk the hierarchy and make sure that - * it and its children are not focused and not dirty. Note that we don't clear dirty - * flags from the component hierarchy. + * This component has been removed from the DOM. Make sure that it is not focused and not dirty. */ void -CoreComponent::markRemoved() +CoreComponent::markSelfRemoved() { auto self = shared_from_corecomponent(); @@ -612,9 +631,6 @@ CoreComponent::markRemoved() else // If nothing suitable is found - clear focus forcefully as we don't have another choice. fm.clearFocus(true, kFocusDirectionNone, true); } - - for (auto& child : mChildren) - std::static_pointer_cast(child)->markRemoved(); } /** @@ -629,7 +645,7 @@ CoreComponent::markAdded() mContext->setDirty(self); for (auto& child : mChildren) - std::static_pointer_cast(child)->markAdded(); + CoreComponent::cast(child)->markAdded(); } void @@ -679,7 +695,7 @@ CoreComponent::isLaidOut() const if (YGNodeIsDirty(node)) return false; - auto parent = std::static_pointer_cast(component->getParent()); + auto parent = CoreComponent::cast(component->getParent()); if (!parent) return true; @@ -744,9 +760,9 @@ CoreComponent::updateSceneGraph(sg::SceneGraphUpdates& sceneGraph) isDirty(kPropertyTransform) || isDirty(kPropertyBounds); if (needsRebuild) { - layer->setBounds(getCalculated(kPropertyBounds).getRect()); + layer->setBounds(getCalculated(kPropertyBounds).get()); layer->setOpacity(static_cast(getCalculated(kPropertyOpacity).asNumber())); - layer->setTransform(getCalculated(kPropertyTransform).getTransform2D()); + layer->setTransform(getCalculated(kPropertyTransform).get()); } // Check if the shadow changed @@ -798,9 +814,9 @@ CoreComponent::updateSceneGraph(sg::SceneGraphUpdates& sceneGraph) sg::LayerPtr CoreComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) { - auto layer = sg::layer(mUniqueId, getCalculated(kPropertyBounds).getRect(), + auto layer = sg::layer(mUniqueId, getCalculated(kPropertyBounds).get(), static_cast(getCalculated(kPropertyOpacity).asNumber()), - getCalculated(kPropertyTransform).getTransform2D()); + getCalculated(kPropertyTransform).get()); if (getCalculated(kPropertyDisabled).truthy()) layer->setInteraction(sg::Layer::kInteractionDisabled); @@ -840,7 +856,7 @@ CoreComponent::assignProperties(const ComponentPropDefSet& propDefSet) if (p->second.isString()) { auto tmp = parseDataBinding(*mContext, p->second.getString()); // Expand data-binding if (tmp.isEvaluable()) { - auto self = std::static_pointer_cast(shared_from_this()); + auto self = shared_from_corecomponent(); ComponentDependant::create(self, pd.key, tmp, mContext, pd.getBindingFunction()); } value = pd.calculate(*mContext, evaluate(*mContext, tmp)); // Calculate the final value @@ -849,7 +865,7 @@ CoreComponent::assignProperties(const ComponentPropDefSet& propDefSet) // Explicitly marked for evaluation, so do it. // Will not attach dependant if no valid symbols. auto tmp = parseDataBindingRecursive(*mContext, p->second); - auto self = std::static_pointer_cast(shared_from_this()); + auto self = shared_from_corecomponent(); ComponentDependant::create(self, pd.key, tmp, mContext, pd.getBindingFunction()); value = pd.calculate(*mContext, p->second); } @@ -1404,7 +1420,7 @@ CoreComponent::inheritsStateFrom(const CoreComponentPtr& component) { // compare to ancestor components while (stateOwner && stateOwner->getInheritParentState()) { - stateOwner = std::static_pointer_cast(stateOwner->getParent()); + stateOwner = CoreComponent::cast(stateOwner->getParent()); if (stateOwner == component) { return true; } @@ -1417,7 +1433,7 @@ CoreComponentPtr CoreComponent::findStateOwner() { auto ptr = shared_from_corecomponent(); while (ptr && ptr->getInheritParentState()) { - ptr = std::static_pointer_cast(ptr->getParent()); + ptr = CoreComponent::cast(ptr->getParent()); } return ptr; } @@ -1538,7 +1554,7 @@ CoreComponent::processLayoutChanges(bool useDirtyFlag, bool first) fixLayoutDirection(useDirtyFlag); Rect rect(left, top, width, height); - changed |= rect != mCalculated.get(kPropertyBounds).getRect(); + changed |= rect != mCalculated.get(kPropertyBounds).get(); if (changed) { mCalculated.set(kPropertyBounds, std::move(rect)); @@ -1566,7 +1582,7 @@ CoreComponent::processLayoutChanges(bool useDirtyFlag, bool first) borderTop + paddingTop, width - (borderLeft + paddingLeft + borderRight + paddingRight), height - (borderTop + paddingTop + borderBottom + paddingBottom)); - changed = inner != mCalculated.get(kPropertyInnerBounds).getRect(); + changed = inner != mCalculated.get(kPropertyInnerBounds).get(); if (changed) { mCalculated.set(kPropertyInnerBounds, std::move(inner)); @@ -1575,7 +1591,7 @@ CoreComponent::processLayoutChanges(bool useDirtyFlag, bool first) setDirty(kPropertyInnerBounds); } - if (!mCalculated.get(kPropertyLaidOut).asBoolean() && !mCalculated.get(kPropertyBounds).getRect().empty()) { + if (!mCalculated.get(kPropertyLaidOut).asBoolean() && !mCalculated.get(kPropertyBounds).get().empty()) { mCalculated.set(kPropertyLaidOut, true); if (useDirtyFlag) setDirty(kPropertyLaidOut); @@ -1707,7 +1723,7 @@ CoreComponent::getHierarchySignature() const void CoreComponent::fixTransform(bool useDirtyFlag) { - LOG_IF(DEBUG_TRANSFORM).session(mContext) << mCalculated.get(kPropertyTransform).getTransform2D(); + LOG_IF(DEBUG_TRANSFORM).session(mContext) << mCalculated.get(kPropertyTransform).get(); Transform2D updated; @@ -1718,13 +1734,13 @@ CoreComponent::fixTransform(bool useDirtyFlag) mCalculated.set(kPropertyTransformAssigned, transform); } - if (transform.isTransform()) { + if (transform.is()) { float width = YGNodeLayoutGetWidth(mYGNodeRef); float height = YGNodeLayoutGetHeight(mYGNodeRef); - updated = transform.getTransformation()->get(width, height); + updated = transform.get()->get(width, height); } - auto value = getCalculated(kPropertyTransform).getTransform2D(); + auto value = getCalculated(kPropertyTransform).get(); if (updated != value) { mCalculated.set(kPropertyTransform, Object(std::move(updated))); markGlobalToLocalTransformStale(); @@ -1736,7 +1752,7 @@ CoreComponent::fixTransform(bool useDirtyFlag) if (useDirtyFlag) setDirty(kPropertyTransform); - LOG_IF(DEBUG_TRANSFORM).session(mContext) << "updated to " << mCalculated.get(kPropertyTransform).getTransform2D(); + LOG_IF(DEBUG_TRANSFORM).session(mContext) << "updated to " << mCalculated.get(kPropertyTransform).get(); } } @@ -1971,7 +1987,7 @@ CoreComponent::serializeVisualContextInternal(rapidjson::Value &outArray, rapidj } // Transform - auto transform = getCalculated(kPropertyTransform).getTransform2D(); + auto transform = getCalculated(kPropertyTransform).get(); if(!transform.isIdentity()) { visualContext.AddMember("transform", transform.serialize(allocator).Move(), allocator); } @@ -2174,14 +2190,14 @@ CoreComponent::processKeyPress(KeyHandlerType type, const Keyboard& keyboard) { static inline void inlineFixTransform(Component& component) { - auto& core = dynamic_cast(component); + auto& core = (CoreComponent&)component; core.fixTransform(true); } static inline void inlineFixPadding(Component& component) { - auto& core = dynamic_cast(component); + auto& core = (CoreComponent&)component; core.fixPadding(); } @@ -2360,7 +2376,7 @@ CoreComponent::propDefSet() const { {kPropertyAccessibilityLabel, "", asString, kPropInOut | kPropDynamic}, {kPropertyAccessibilityActions, Object::EMPTY_ARRAY(), asArray, kPropInOut}, - {kPropertyBounds, Object::EMPTY_RECT(), nullptr, kPropOut | + {kPropertyBounds, Rect(0,0,0,0), nullptr, kPropOut | kPropVisualContext | kPropVisualHash}, {kPropertyChecked, false, asBoolean, kPropInOut | @@ -2383,7 +2399,7 @@ CoreComponent::propDefSet() const { {kPropertyHeight, Dimension(), asDimension, kPropIn | kPropDynamic | kPropStyled, yn::setHeight, defaultHeight}, - {kPropertyInnerBounds, Object::EMPTY_RECT(), nullptr, kPropOut | + {kPropertyInnerBounds, Rect(0,0,0,0), nullptr, kPropOut | kPropVisualContext | kPropVisualHash}, {kPropertyLayoutDirectionAssigned, kLayoutDirectionInherit, sLayoutDirectionMap, kPropIn | @@ -2457,7 +2473,7 @@ CoreComponent::propDefSet() const { kPropDynamic | kPropEvaluated | kPropVisualContext, inlineFixTransform}, - {kPropertyTransform, Object::IDENTITY_2D(), nullptr, kPropOut | + {kPropertyTransform, Transform2D(), nullptr, kPropOut | kPropVisualContext}, {kPropertyUser, Object::NULL_OBJECT(), nullptr, kPropOut}, {kPropertyWidth, Dimension(), asDimension, kPropIn | @@ -2476,7 +2492,7 @@ CoreComponent::propDefSet() const { bool CoreComponent::containsLocalPosition(const Point &position) const { - auto bounds = getCalculated(kPropertyBounds).getRect(); + const auto& bounds = getCalculated(kPropertyBounds).get(); Rect localBounds(0, 0, bounds.getWidth(), bounds.getHeight()); return localBounds.contains(position); } @@ -2511,8 +2527,8 @@ CoreComponent::inParentViewport() const { return false; } - Rect bounds = getCalculated(kPropertyBounds).getRect(); - auto parentBounds = mParent->getCalculated(kPropertyBounds).getRect(); + const auto& bounds = getCalculated(kPropertyBounds).get(); + auto parentBounds = mParent->getCalculated(kPropertyBounds).get(); // Reset to "viewport" parentBounds = Rect(0, 0, parentBounds.getWidth(), parentBounds.getHeight()); // Shift by scroll position if any @@ -2558,11 +2574,11 @@ CoreComponent::ensureGlobalToLocalTransform() { Transform2D newLocalTransform; - auto componentTransform = getCalculated(kPropertyTransform).getTransform2D(); + auto componentTransform = getCalculated(kPropertyTransform).get(); // To transform from the coordinate space of the parent component to the // coordinate space of the child component, first offset by the position of // the child in the parent, then undo the child transformation: - auto boundsInParent = getCalculated(kPropertyBounds).getRect(); + const auto& boundsInParent = getCalculated(kPropertyBounds).get(); auto offsetInParent = boundsInParent.getTopLeft(); newLocalTransform = componentTransform.inverse() * Transform2D::translate(-offsetInParent); diff --git a/aplcore/src/component/edittextcomponent.cpp b/aplcore/src/component/edittextcomponent.cpp index 1dd08be..dec95d0 100644 --- a/aplcore/src/component/edittextcomponent.cpp +++ b/aplcore/src/component/edittextcomponent.cpp @@ -392,8 +392,8 @@ EditTextComponent::measureEditText(MeasureRequest&& request) if (!mEditTextBox) { ensureEditTextProperties(); - auto measure = std::dynamic_pointer_cast(mContext->measure()); - assert(measure); + assert(mContext->measure()->sceneGraphCompatible()); + auto measure = std::static_pointer_cast(mContext->measure()); mEditTextBox = measure->box( getCalculated(kPropertySize).getInteger(), @@ -477,34 +477,34 @@ EditTextComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) }); // Build the scene graph - auto layer = CoreComponent::constructSceneGraphLayer(sceneGraph); + auto layer = ActionableComponent::constructSceneGraphLayer(sceneGraph); assert(layer); - auto size = getCalculated(kPropertyBounds).getRect().getSize(); + // The first content node draws the outline + auto size = getCalculated(kPropertyBounds).get().getSize(); auto outline = Rect{0, 0, size.getWidth(), size.getHeight()}; auto strokeWidth = getCalculated(kPropertyDrawnBorderWidth).asFloat(); - - // The first content node draws the border - layer->appendContent( - sg::draw(sg::path(RoundedRect{outline, 0}, strokeWidth), - sg::fill(sg::paint(getCalculated(kPropertyBorderColor))))); - - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); - auto hintSize = mHintLayout->getSize(); + auto content = sg::draw(sg::path(RoundedRect{outline, 0}, strokeWidth), + sg::fill(sg::paint(getCalculated(kPropertyBorderColor)))); // The second content node draws the hint. The hint is transparent if the edit control // has text to display. // TODO: Fix auto for RTOL text based on language Color hintColor = getCalculated(kPropertyText).empty() - ? getCalculated(kPropertyHintColor).getColor() : Color::TRANSPARENT; - layer->appendContent(sg::transform( + ? getCalculated(kPropertyHintColor).getColor() : Color::TRANSPARENT; + auto innerBounds = getCalculated(kPropertyInnerBounds).get(); + auto hintSize = mHintLayout->getSize(); + + content->setNext(sg::transform( Point{innerBounds.getLeft(), innerBounds.getCenterY() - hintSize.getHeight() / 2}, sg::text(mHintLayout, sg::fill(sg::paint(hintColor))))); + layer->setContent(content); + // Construct an inner layer for the actual edit text auto innerLayer = sg::layer(mUniqueId + "-innerEditText", innerBounds, 1.0, Transform2D()); - innerLayer->appendContent(sg::editText(mEditText.getPtr(), mEditTextBox, - mEditTextConfig, getCalculated(kPropertyText).getString())); + innerLayer->setContent(sg::editText(mEditText.getPtr(), mEditTextBox, + mEditTextConfig, getCalculated(kPropertyText).getString())); innerLayer->clearFlags(); layer->appendChild(innerLayer); @@ -524,11 +524,11 @@ EditTextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) const auto borderColorChanged = isDirty(kPropertyBorderColor); if (outlineChanged || borderWidthChanged || drawnBorderWidthChanged || borderColorChanged) { - auto* draw = sg::DrawNode::cast(mSceneGraphLayer->content().at(0)); + auto* draw = sg::DrawNode::cast(mSceneGraphLayer->content()); auto* path = sg::FramePath::cast(draw->getPath()); if (outlineChanged || borderWidthChanged || drawnBorderWidthChanged) { - auto size = getCalculated(kPropertyBounds).getRect().getSize(); + auto size = getCalculated(kPropertyBounds).get().getSize(); auto outline = RoundedRect(Rect{0,0,size.getWidth(), size.getHeight()}, 0); result |= path->setRoundedRect(outline); result |= path->setInset(getCalculated(kPropertyDrawnBorderWidth).asFloat()); @@ -541,7 +541,7 @@ EditTextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) } const bool fixInnerBounds = isDirty(kPropertyInnerBounds); - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); const auto fixText = isDirty(kPropertyText); @@ -550,7 +550,7 @@ EditTextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) const bool fixHintColor = isDirty(kPropertyHintColor) || fixText; if (fixInnerBounds || fixHintLayout || fixHintColor) { - auto* transform = sg::TransformNode::cast(mSceneGraphLayer->content().at(1)); + auto* transform = sg::TransformNode::cast(mSceneGraphLayer->content()->next()); auto* text = sg::TextNode::cast(transform->child()); if (fixInnerBounds || fixHintLayout) { @@ -576,7 +576,7 @@ EditTextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) if (fixInnerBounds || fixEditConfig || fixText || fixEditBox) { auto innerLayer = mSceneGraphLayer->children().at(0); - auto *editNode = sg::EditTextNode::cast(innerLayer->content().at(0)); + auto *editNode = sg::EditTextNode::cast(innerLayer->content()); if (fixInnerBounds) innerLayer->setBounds(innerBounds); @@ -606,10 +606,10 @@ EditTextComponent::ensureEditTextBox() ensureEditTextProperties(); - auto measure = std::dynamic_pointer_cast(mContext->measure()); - assert(measure); + assert(mContext->measure()->sceneGraphCompatible()); + auto measure = std::static_pointer_cast(mContext->measure()); - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); mEditTextBox = measure->box(getCalculated(kPropertySize).getInteger(), mEditTextProperties, @@ -692,9 +692,9 @@ EditTextComponent::ensureHintLayout() ); auto borderWidth = getCalculated(kPropertyBorderWidth).asFloat(); - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect().inset(borderWidth); - auto measure = std::dynamic_pointer_cast(context->measure()); - assert(measure); + auto innerBounds = getCalculated(kPropertyInnerBounds).get().inset(borderWidth); + assert(mContext->measure()->sceneGraphCompatible()); + auto measure = std::static_pointer_cast(mContext->measure()); mHintLayout = measure->layout(mHintText, mHintTextProperties, innerBounds.getWidth(), MeasureMode::AtMost, diff --git a/aplcore/src/component/framecomponent.cpp b/aplcore/src/component/framecomponent.cpp index 1f15648..37b2e27 100644 --- a/aplcore/src/component/framecomponent.cpp +++ b/aplcore/src/component/framecomponent.cpp @@ -16,6 +16,7 @@ #include "apl/component/componentpropdef.h" #include "apl/component/framecomponent.h" #include "apl/component/yogaproperties.h" +#include "apl/primitives/radii.h" #ifdef SCENEGRAPH #include "apl/scenegraph/builder.h" #include "apl/scenegraph/node.h" @@ -59,7 +60,7 @@ FrameComponent::propDefSet() const kPropStyled | kPropDynamic | kPropVisualHash}, - {kPropertyBorderRadii, Object::EMPTY_RADII(), nullptr, kPropOut | + {kPropertyBorderRadii, Radii(), nullptr, kPropOut | kPropVisualHash}, {kPropertyBorderColor, Color(), asColor, kPropInOut | kPropStyled | @@ -144,7 +145,7 @@ FrameComponent::fixBorder(bool useDirtyFlag) Radii radii(std::move(result)); - if (radii != mCalculated.get(kPropertyBorderRadii).getRadii()) { + if (radii != mCalculated.get(kPropertyBorderRadii).get()) { mCalculated.set(kPropertyBorderRadii, Object(std::move(radii))); if (useDirtyFlag) setDirty(kPropertyBorderRadii); @@ -159,11 +160,8 @@ FrameComponent::fixBorder(bool useDirtyFlag) * - Outline Path [If the outline isn't a rectangle] * - Child Clip Path [If the borderWidth != 0] * - Content - * Fill DrawNode - * Border DrawNode - * - * For now we always create the two drawing nodes to support animation without inserting and - * deleting new nodes. + * Background DrawNode [optional] + * Border DrawNode [optional] */ sg::LayerPtr FrameComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) @@ -171,10 +169,10 @@ FrameComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) auto layer = CoreComponent::constructSceneGraphLayer(sceneGraph); assert(layer); - auto radii = getCalculated(kPropertyBorderRadii).getRadii(); + const auto& radii = getCalculated(kPropertyBorderRadii).get(); auto borderWidth = static_cast(getCalculated(kPropertyBorderWidth).asNumber()); auto strokeWidth = static_cast(getCalculated(kPropertyDrawnBorderWidth).asNumber()); - auto size = getCalculated(kPropertyBounds).getRect().getSize(); + auto size = getCalculated(kPropertyBounds).get().getSize(); auto outline = RoundedRect(Rect{0, 0, size.getWidth(), size.getHeight()}, radii); // Set the outline only if it isn't a rectangle that matches the size @@ -185,16 +183,13 @@ FrameComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) if (borderWidth > 0) layer->setChildClip(sg::path(outline.inset(borderWidth))); - // The frame always puts two content nodes in - one for the fill and one for the border - // In the future we may change this to make the content nodes more dynamic - layer->appendContent( - sg::draw(sg::path(outline.inset(strokeWidth)), - sg::fill(sg::paint(getCalculated(kPropertyBackgroundColor))))); - - layer->appendContent( - sg::draw(sg::path(outline, strokeWidth), - sg::fill(sg::paint(getCalculated(kPropertyBorderColor))))); + auto background = sg::draw(sg::path(outline.inset(strokeWidth)), + sg::fill(sg::paint(getCalculated(kPropertyBackgroundColor)))); + auto border = sg::draw(sg::path(outline, strokeWidth), + sg::fill(sg::paint(getCalculated(kPropertyBorderColor)))); + background->setNext(border); + layer->setContent(background); return layer; } @@ -215,10 +210,10 @@ FrameComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) !backgroundChanged && !borderColorChanged && !strokeWidthChanged) return false; - auto radii = getCalculated(kPropertyBorderRadii).getRadii(); + const auto& radii = getCalculated(kPropertyBorderRadii).get(); auto borderWidth = getCalculated(kPropertyBorderWidth).asFloat(); auto strokeWidth = getCalculated(kPropertyDrawnBorderWidth).asFloat(); - auto size = getCalculated(kPropertyBounds).getRect().getSize(); + auto size = getCalculated(kPropertyBounds).get().getSize(); auto outline = RoundedRect(Rect{0, 0, size.getWidth(), size.getHeight()}, radii); auto *layer = mSceneGraphLayer.get(); @@ -232,11 +227,13 @@ FrameComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) if (outlineChanged || borderWidthChanged) result |= layer->setChildClip(borderWidth > 0 ? sg::path(outline.inset(borderWidth)) : nullptr); - assert(layer->content().size() == 2); - auto background = layer->content().at(0); - auto border = layer->content().at(1); + assert(layer->content()); + auto background = layer->content(); + assert(background->next()); + auto border = background->next(); + assert(!border->next()); - // Fix the background. Actually, this should already be clipped, so we could a rectangular fill + // Fix the background. Actually, this should already be clipped, so we could use a rectangular fill if (outlineChanged || borderWidthChanged || backgroundChanged) { auto *draw = sg::DrawNode::cast(background); diff --git a/aplcore/src/component/gridsequencecomponent.cpp b/aplcore/src/component/gridsequencecomponent.cpp index b87124f..8db99c2 100644 --- a/aplcore/src/component/gridsequencecomponent.cpp +++ b/aplcore/src/component/gridsequencecomponent.cpp @@ -81,7 +81,7 @@ GridSequenceComponent::processLayoutChanges(bool useDirtyFlag, bool first) CoreComponent::processLayoutChanges(useDirtyFlag, first); //Calculate child sizes if needed. - auto bounds = getCalculated(kPropertyBounds).getRect(); + const auto& bounds = getCalculated(kPropertyBounds).get(); auto childHeight = getCalculated(kPropertyChildHeight); auto childWidth = getCalculated(kPropertyChildWidth); if (mLastParentBounds != bounds || mLastChildHeight != childHeight || mLastChildWidth != childWidth) { @@ -140,7 +140,7 @@ GridSequenceComponent::handlePropertyChange(const ComponentPropDef& def, const O MultiChildScrollableComponent::handlePropertyChange(def, value); if (def.key == kPropertyChildHeight || def.key == kPropertyChildWidth) { - auto gridComponent = std::dynamic_pointer_cast(shared_from_this()); + auto gridComponent = std::static_pointer_cast(shared_from_corecomponent()); gridComponent->processLayoutChanges(true, false); } } diff --git a/aplcore/src/component/imagecomponent.cpp b/aplcore/src/component/imagecomponent.cpp index bb8302f..2c75347 100644 --- a/aplcore/src/component/imagecomponent.cpp +++ b/aplcore/src/component/imagecomponent.cpp @@ -45,9 +45,9 @@ ImageComponent::ImageComponent(const ContextPtr& context, } void -ImageComponent::release() +ImageComponent::releaseSelf() { - CoreComponent::release(); + CoreComponent::releaseSelf(); MediaComponentTrait::release(); } @@ -55,22 +55,22 @@ const ComponentPropDefSet& ImageComponent::propDefSet() const { static auto resetMediaState = [](Component& component) { - auto& comp = dynamic_cast(component); + auto& comp = (ImageComponent&)component; comp.mOnLoadOnFailReported = false; comp.resetMediaFetchState(); }; static ComponentPropDefSet sImageComponentProperties = ComponentPropDefSet( CoreComponent::propDefSet(), MediaComponentTrait::propDefList()).add({ - {kPropertyAlign, kImageAlignCenter, sAlignMap, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, // Doesn't match 1.0 spec - {kPropertyBorderRadius, Object::ZERO_ABS_DIMEN(), asAbsoluteDimension, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, - {kPropertyFilters, Object::EMPTY_ARRAY(), asFilterArray, kPropInOut | kPropVisualHash}, // Takes part in hash even though it's not dynamic. - {kPropertyOnFail, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyOnLoad, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyOverlayColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, - {kPropertyOverlayGradient, Object::NULL_OBJECT(), asGradient, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, - {kPropertyScale, kImageScaleBestFit, sScaleMap, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, - {kPropertySource, "", asImageSourceArray, kPropInOut | kPropDynamic | kPropVisualHash | kPropEvaluated, resetMediaState}, + {kPropertyAlign, kImageAlignCenter, sAlignMap, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, // Doesn't match 1.0 spec + {kPropertyBorderRadius, Dimension(DimensionType::Absolute, 0), asAbsoluteDimension, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, + {kPropertyFilters, Object::EMPTY_ARRAY(), asFilterArray, kPropInOut | kPropVisualHash}, // Takes part in hash even though it's not dynamic. + {kPropertyOnFail, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyOnLoad, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyOverlayColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, + {kPropertyOverlayGradient, Object::NULL_OBJECT(), asGradient, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, + {kPropertyScale, kImageScaleBestFit, sScaleMap, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash}, + {kPropertySource, "", asImageSourceArray, kPropInOut | kPropDynamic | kPropVisualHash | kPropEvaluated, resetMediaState}, }); return sImageComponentProperties; @@ -88,14 +88,14 @@ ImageComponent::getSources() } if (sourceProp.isString()) { // Single source - sources.emplace_back(sourceProp.asURLRequest()); + sources.emplace_back(URLRequest::asURLRequest(sourceProp)); } else if (sourceProp.isArray()) { auto& filters = getCalculated(kPropertyFilters); if (filters.empty()) { // If no filters use last - sources.emplace_back(sourceProp.at(sourceProp.size() - 1).asURLRequest()); + sources.emplace_back(URLRequest::asURLRequest(sourceProp.at(sourceProp.size() - 1))); } else { // Else request everything for (auto& source : sourceProp.getArray()) { - sources.emplace_back(source.asURLRequest()); + sources.emplace_back(URLRequest::asURLRequest(source)); } } } @@ -140,7 +140,7 @@ ImageComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) // TODO: Shadow - adjust the boundary of the object to put the shadow in the right spot // TODO: Use a child layer instead? - layer->appendContent( + layer->setContent( sg::clip( sg::path(rects.target, getCalculated(kPropertyBorderRadius).asFloat()), @@ -165,7 +165,7 @@ ImageComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) return false; auto *layer = mSceneGraphLayer.get(); - auto* clip = sg::ClipNode::cast(layer->content().front()); + auto* clip = sg::ClipNode::cast(layer->content()); auto* image = sg::ImageNode::cast(clip->child()); if (fixFilter || fixMediaState) { @@ -208,7 +208,7 @@ ImageComponent::getFilteredImage() auto filters = getCalculated(kPropertyFilters); for (int i = 0; i < filters.size(); i++) { - const auto& filter = filters.at(i).getFilter(); + const auto& filter = filters.at(i).get(); switch (filter.getType()) { case kFilterTypeBlend: stack.push_back(sg::blend( @@ -270,7 +270,7 @@ ImageComponent::getImageRects(const sg::FilterPtr& filter) if (!filter) return {}; - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); // Calculate the scaled size of the image fit to the component. This is in PIXELS and must be // CONVERTED to DP. diff --git a/aplcore/src/component/mediacomponenttrait.cpp b/aplcore/src/component/mediacomponenttrait.cpp index c9c52b1..02fcf91 100644 --- a/aplcore/src/component/mediacomponenttrait.cpp +++ b/aplcore/src/component/mediacomponenttrait.cpp @@ -71,17 +71,14 @@ MediaComponentTrait::ensureMediaRequested() auto mediaObject = context->mediaManager().request(m.getUrl(), mediaType(), m.getHeaders()); MediaObject::CallbackID callbackToken = 0; if (mediaObject->state() == MediaObject::kPending) { - auto weak = std::weak_ptr(component); - callbackToken = mediaObject->addCallback([weak](const MediaObjectPtr& mediaObjectPtr) { - auto self = weak.lock(); - if (self) - std::dynamic_pointer_cast(self)->pendingMediaReturned(mediaObjectPtr); + callbackToken = mediaObject->addCallback([this](const MediaObjectPtr& mediaObjectPtr) { + pendingMediaReturned(mediaObjectPtr); }); } mMediaObjectHolders.emplace_back(MediaObjectHolder{mediaObject, callbackToken}); } - // Some of the media objects may have been ready or in error state + // Some media objects may have been ready or in error state updateMediaState(); } diff --git a/aplcore/src/component/multichildscrollablecomponent.cpp b/aplcore/src/component/multichildscrollablecomponent.cpp index 19783d3..648c56b 100644 --- a/aplcore/src/component/multichildscrollablecomponent.cpp +++ b/aplcore/src/component/multichildscrollablecomponent.cpp @@ -48,7 +48,7 @@ using VisChildResult = std::pair; template Object getScrollAlignId(const CoreComponent& component) { - const auto& m = dynamic_cast(component); + const auto& m = (const TOwner&)component; auto p = (m.*func)(); return ObjectArray{p.first ? p.first->getId() : "", p.second}; } @@ -56,7 +56,7 @@ Object getScrollAlignId(const CoreComponent& component) template Object getScrollAlignIndex(const CoreComponent& component) { - const auto& m = dynamic_cast(component); + const auto& m = (const TOwner&)component; auto p = (m.*func)(); return ObjectArray{component.getChildIndex(p.first), p.second}; }; @@ -70,15 +70,15 @@ void setScrollAlignId(CoreComponent& component, const Object& value) } auto id = value.at(0).asString(); - auto child = std::dynamic_pointer_cast(component.findComponentById(id)); + auto child = CoreComponent::cast(component.findComponentById(id)); if (!child) { CONSOLE(component.getContext()) << "Unable to find child with id " << id; return; } - auto& self = dynamic_cast(component); - self.setScrollPositionDirectlyByChild(child, align, value.at(1).asNumber()); -}; + ((MultiChildScrollableComponent&)component) + .setScrollPositionDirectlyByChild(child, align, (float)value.at(1).asNumber()); +} template void setScrollAlignIndex(CoreComponent& component, const Object& value) @@ -94,10 +94,10 @@ void setScrollAlignIndex(CoreComponent& component, const Object& value) return; } - auto child = std::dynamic_pointer_cast(component.getChildAt(index)); - auto& self = dynamic_cast(component); - self.setScrollPositionDirectlyByChild(child, align, value.at(1).asNumber()); -}; + auto child = CoreComponent::cast(component.getChildAt(index)); + ((MultiChildScrollableComponent&)component) + .setScrollPositionDirectlyByChild(child, align, (float)value.at(1).asNumber()); +} const ComponentPropDefSet& MultiChildScrollableComponent::propDefSet() const @@ -144,8 +144,8 @@ MultiChildScrollableComponent::allowForward() const // otherwise get the last child and calculate the bounds of // all children auto lastChild = getChildAt(getChildCount() - 1); - auto lastChildBounds = lastChild->getCalculated(kPropertyBounds).getRect(); - auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); + const auto& lastChildBounds = lastChild->getCalculated(kPropertyBounds).get(); + const auto& innerBounds = mCalculated.get(kPropertyInnerBounds).get(); auto currentPosition = mCalculated.get(kPropertyScrollPosition).asNumber(); auto vertical = isVertical(); bool isLTR = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; @@ -189,8 +189,8 @@ MultiChildScrollableComponent::setScrollPositionDirectlyByChild(const CoreCompon assert(child); ensureChildLayout(child, true); // TODO: Should this set dirty? With the initial expansion this should not be set... - const auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - const auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); + const auto& childBounds = child->getCalculated(kPropertyBounds).get(); + const auto& innerBounds = mCalculated.get(kPropertyInnerBounds).get(); const auto horiz = isHorizontal(); const auto childSize = horiz ? childBounds.getWidth() : childBounds.getHeight(); @@ -219,11 +219,11 @@ MultiChildScrollableComponent::ensureChildLayout(const CoreComponentPtr& child, // TODO: Search for child and if direct - attach if required. auto oldLayoutDirection = static_cast(mCalculated.get(kPropertyLayoutDirection).asInt()); auto paddedScrollPosition = getPaddedScrollPosition(oldLayoutDirection); - auto anchor = std::static_pointer_cast(findChildCloseToPosition(paddedScrollPosition)); + auto anchor = CoreComponent::cast(findChildCloseToPosition(paddedScrollPosition)); Rect oldAnchorBounds; if (anchor) { - oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + oldAnchorBounds = anchor->getCalculated(kPropertyBounds).get(); } auto it = std::find(mChildren.begin(), mChildren.end(), child); @@ -237,7 +237,7 @@ MultiChildScrollableComponent::ensureChildLayout(const CoreComponentPtr& child, if (anchor) { // Adjust scroll position if changed at the beginning of sequence - fixScrollPosition(oldAnchorBounds, anchor->getCalculated(kPropertyBounds).getRect()); + fixScrollPosition(oldAnchorBounds, anchor->getCalculated(kPropertyBounds).get()); } } @@ -295,8 +295,8 @@ MultiChildScrollableComponent::getFirstChildInViewInternal() const // Calculate the percentage shift in the child from the top-left corner of the inner bounds auto child = mChildren.at(mFirstChildInView); - auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - auto topLeft = mCalculated.get(kPropertyInnerBounds).getRect().getTopLeft(); + const auto& childBounds = child->getCalculated(kPropertyBounds).get(); + auto topLeft = mCalculated.get(kPropertyInnerBounds).get().getTopLeft(); auto scrollPosition = mCalculated.get(kPropertyScrollPosition).asNumber(); float offset = 0.0; @@ -320,13 +320,13 @@ MultiChildScrollableComponent::getCenterChildInViewInternal() const return {nullptr, 0.0}; // Find the closest child by distance to the center point - auto center = mCalculated.get(kPropertyInnerBounds).getRect().getCenter() + scrollPosition(); + auto center = mCalculated.get(kPropertyInnerBounds).get().getCenter() + scrollPosition(); auto bestDistance = std::numeric_limits::max(); CoreComponentPtr bestChild = nullptr; for (size_t index = mFirstChildInView; index <= mLastChildInView; index++) { auto child = mChildren.at(index); - auto distance = child->getCalculated(kPropertyBounds).getRect().distanceTo(center); + auto distance = child->getCalculated(kPropertyBounds).get().distanceTo(center); if (distance < bestDistance) { bestChild = child; bestDistance = distance; @@ -334,8 +334,8 @@ MultiChildScrollableComponent::getCenterChildInViewInternal() const } // Calculate the percentage shift of the center of the child from the center of the innerBounds - auto childBounds = bestChild->getCalculated(kPropertyBounds).getRect(); - center = mCalculated.get(kPropertyInnerBounds).getRect().getCenter() + scrollPosition(); // Switch to innerBounds + const auto& childBounds = bestChild->getCalculated(kPropertyBounds).get(); + center = mCalculated.get(kPropertyInnerBounds).get().getCenter() + scrollPosition(); // Switch to innerBounds float offset = 0.0; if (!childBounds.empty()) { @@ -366,9 +366,9 @@ MultiChildScrollableComponent::accept(Visitor& visitor) const if (!mEnsuredChildren.empty()) { for (int i = mEnsuredChildren.lowerBound(); i <= mEnsuredChildren.upperBound() && !visitor.isAborted(); i++) { - auto child = std::dynamic_pointer_cast(mChildren.at(i)); + auto child = CoreComponent::cast(mChildren.at(i)); if (child != nullptr && child->isAttached() && - !child->getCalculated(kPropertyBounds).getRect().empty()) + !child->getCalculated(kPropertyBounds).get().empty()) child->accept(visitor); } } @@ -383,9 +383,9 @@ MultiChildScrollableComponent::raccept(Visitor& visitor) const if (!mEnsuredChildren.empty()) { for (int i = mEnsuredChildren.upperBound(); i >= mEnsuredChildren.lowerBound() && !visitor.isAborted(); i--) { - auto child = std::dynamic_pointer_cast(mChildren.at(i)); + auto child = CoreComponent::cast(mChildren.at(i)); if (child != nullptr && child->isAttached() && - !child->getCalculated(kPropertyBounds).getRect().empty()) + !child->getCalculated(kPropertyBounds).get().empty()) child->raccept(visitor); } } @@ -500,12 +500,12 @@ MultiChildScrollableComponent::trimScroll(const Point& point) getCalculated(kPropertyBounds).empty()) return Point(); - auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); + const auto& innerBounds = mCalculated.get(kPropertyInnerBounds).get(); // We treat this component as 0 point of sequence. All calculation happens in relation to it. auto zeroAnchorIdx = mEnsuredChildren.empty() ? 0 : mEnsuredChildren.lowerBound(); auto zeroAnchor = mChildren.at(zeroAnchorIdx); if (!zeroAnchor->isAttached()) layoutChildIfRequired(zeroAnchor, zeroAnchorIdx, true, false); - auto oldZeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect(); + auto oldZeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).get(); auto zeroAnchorPos = oldZeroAnchorPos.getTopLeft() - innerBounds.getTopLeft(); // We should disregard spacing in limit calculation as it's not part of inner bounds really. @@ -521,10 +521,10 @@ MultiChildScrollableComponent::trimScroll(const Point& point) while (y + zeroAnchorPos.getY() < 0 && mEnsuredChildren.lowerBound() > 0) { auto idx = mEnsuredChildren.lowerBound() - 1; layoutChildIfRequired(mChildren.at(idx), idx, true, false); - zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().getTopLeft() - innerBounds.getTopLeft(); + zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).get().getTopLeft() - innerBounds.getTopLeft(); reportLoaded(idx); } - fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).get()); y += zeroAnchorPos.getY() - spacing; @@ -538,7 +538,7 @@ MultiChildScrollableComponent::trimScroll(const Point& point) for (int i = zeroAnchorIdx ; igetCalculated(kPropertyBounds).getRect().getBottom() - bottom)); + maxY = std::max(maxY, nonNegative(child->getCalculated(kPropertyBounds).get().getBottom() - bottom)); if (y <= maxY) { reportLoaded(i); return Point(0, y); @@ -552,10 +552,10 @@ MultiChildScrollableComponent::trimScroll(const Point& point) while (x + zeroAnchorPos.getX() < 0 && mEnsuredChildren.lowerBound() > 0) { auto idx = mEnsuredChildren.lowerBound() - 1; layoutChildIfRequired(mChildren.at(idx), idx, true, false); - zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().getTopLeft() - innerBounds.getTopLeft(); + zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).get().getTopLeft() - innerBounds.getTopLeft(); reportLoaded(idx); } - fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).get()); x += zeroAnchorPos.getX() - spacing; @@ -569,7 +569,7 @@ MultiChildScrollableComponent::trimScroll(const Point& point) for (int i = zeroAnchorIdx ; igetCalculated(kPropertyBounds).getRect().getRight() - right)); + maxX = std::max(maxX, nonNegative(child->getCalculated(kPropertyBounds).get().getRight() - right)); if (x <= maxX) { reportLoaded(i); return Point(x, 0); @@ -579,15 +579,15 @@ MultiChildScrollableComponent::trimScroll(const Point& point) return Point(maxX, 0); } else { // Horizontal RTL auto x = point.getX(); - zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().getTopRight() - innerBounds.getTopRight(); + zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).get().getTopRight() - innerBounds.getTopRight(); while (x > zeroAnchorPos.getX() && mEnsuredChildren.lowerBound() > 0) { auto idx = mEnsuredChildren.lowerBound() - 1; layoutChildIfRequired(mChildren.at(idx), idx, true, false); - zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().getTopRight() - innerBounds.getTopRight(); + zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).get().getTopRight() - innerBounds.getTopRight(); reportLoaded(idx); } - fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).get()); x -= zeroAnchorPos.getX() - spacing; @@ -601,7 +601,7 @@ MultiChildScrollableComponent::trimScroll(const Point& point) for (int i = zeroAnchorIdx ; igetCalculated(kPropertyBounds).getRect().getLeft() - left)); + maxX = std::min(maxX, nonPositive(child->getCalculated(kPropertyBounds).get().getLeft() - left)); if (x >= maxX) { reportLoaded(i); return Point(x, 0); @@ -737,7 +737,7 @@ MultiChildScrollableComponent::relayoutInPlace(bool useDirtyFlag, bool first) { APL_TRACE_BLOCK("MultiChildScrollableComponent:relayoutInPlace"); auto root = getLayoutRoot(); - auto rootBounds = root->getCalculated(kPropertyBounds).getRect(); + const auto& rootBounds = root->getCalculated(kPropertyBounds).get(); APL_TRACE_BEGIN("MultiChildScrollableComponent:YGNodeCalculateLayout:root"); YGNodeCalculateLayout(root->getNode(), rootBounds.getWidth(), rootBounds.getHeight(), root->getLayoutDirection()); APL_TRACE_END("MultiChildScrollableComponent:YGNodeCalculateLayout:root"); @@ -755,9 +755,9 @@ MultiChildScrollableComponent::maxScroll() const return 0; } bool horizontal = isHorizontal(); - auto sequenceBounds = mCalculated.get(kPropertyInnerBounds).getRect(); + const auto& sequenceBounds = mCalculated.get(kPropertyInnerBounds).get(); float end = horizontal ? sequenceBounds.getRight() : sequenceBounds.getBottom(); - auto lastChildBounds = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).getRect(); + const auto& lastChildBounds = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).get(); float childEnd = horizontal ? lastChildBounds.getRight() : lastChildBounds.getBottom(); return nonNegative(childEnd - end); } @@ -777,9 +777,9 @@ MultiChildScrollableComponent::insertChild(const CoreComponentPtr& child, size_t } void -MultiChildScrollableComponent::removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) +MultiChildScrollableComponent::removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) { - CoreComponent::removeChild(child, index, useDirtyFlag); + CoreComponent::removeChildAfterMarkedRemoved(child, index, useDirtyFlag); if (mEnsuredChildren.contains(index)) { mEnsuredChildren.remove(index); @@ -860,8 +860,8 @@ Point MultiChildScrollableComponent::getPaddedScrollPosition(LayoutDirection layoutDirection) const { auto startPoint = layoutDirection == kLayoutDirectionLTR - ? getCalculated(kPropertyInnerBounds).getRect().getTopLeft() - : getCalculated(kPropertyInnerBounds).getRect().getTopRight(); + ? getCalculated(kPropertyInnerBounds).get().getTopLeft() + : getCalculated(kPropertyInnerBounds).get().getTopRight(); return scrollPosition() + startPoint; } @@ -905,11 +905,11 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b bool horizontal = isHorizontal(); auto oldLayoutDirection = static_cast(mCalculated.get(kPropertyLayoutDirection).asInt()); auto paddedScrollPosition = getPaddedScrollPosition(oldLayoutDirection); - auto anchor = std::static_pointer_cast(findChildCloseToPosition(paddedScrollPosition)); + auto anchor = CoreComponent::cast(findChildCloseToPosition(paddedScrollPosition)); Rect oldAnchorBounds; if (anchor) { - oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + oldAnchorBounds = anchor->getCalculated(kPropertyBounds).get(); } if (reProcess) CoreComponent::processLayoutChanges(useDirtyFlag, first); @@ -933,12 +933,12 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b anchorIdx = mEnsuredChildren.empty() ? 0 : mEnsuredChildren.lowerBound(); anchor = mChildren.at(anchorIdx); layoutChildIfRequired(anchor, anchorIdx, useDirtyFlag, first); - oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + oldAnchorBounds = anchor->getCalculated(kPropertyBounds).get(); } else { auto it = std::find(mChildren.begin(), mChildren.end(), anchor); anchorIdx = std::distance(mChildren.begin(), it); if (oldLayoutDirection != layoutDirection) { - oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + oldAnchorBounds = anchor->getCalculated(kPropertyBounds).get(); auto mirroredScrollPosition = getCalculated(kPropertyScrollPosition).asNumber() * -1.0f; mCalculated.set(kPropertyScrollPosition, Dimension(DimensionType::Absolute, mirroredScrollPosition)); setDirty(kPropertyScrollPosition); @@ -959,7 +959,7 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b } } - auto sequenceBounds = mCalculated.get(kPropertyBounds).getRect(); + const auto& sequenceBounds = mCalculated.get(kPropertyBounds).get(); float childCache = mContext->getRootConfig().getSequenceChildCache(); float pageSize = horizontal ? sequenceBounds.getWidth() : sequenceBounds.getHeight(); @@ -977,7 +977,7 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b // runLayoutHeuristics(anchorIdx, childCache, pageSize, useDirtyFlag, first); // // Anchor bounds may have shifted - Rect anchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + Rect anchorBounds = anchor->getCalculated(kPropertyBounds).get(); float anchorPosition = horizontal ? (layoutDirection == kLayoutDirectionLTR ? anchorBounds.getX() : anchorBounds.getRight()) : anchorBounds.getY(); @@ -992,7 +992,7 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b for (; lastLoaded < mChildren.size(); lastLoaded++) { auto child = mChildren.at(lastLoaded); layoutChildIfRequired(child, lastLoaded, useDirtyFlag, first); - auto childBounds = child->getCalculated(kPropertyBounds).getRect(); + const auto& childBounds = child->getCalculated(kPropertyBounds).get(); float childCoveredPosition = horizontal ? (layoutDirection == kLayoutDirectionLTR ? childBounds.getRight() : childBounds.getLeft()) @@ -1022,8 +1022,8 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b for (; firstLoaded >= 0; firstLoaded--) { auto child = mChildren.at(firstLoaded); layoutChildIfRequired(child, firstLoaded, useDirtyFlag, first); - auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - anchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + const auto& childBounds = child->getCalculated(kPropertyBounds).get(); + anchorBounds = anchor->getCalculated(kPropertyBounds).get(); float distance = (horizontal ? anchorBounds.getLeft() : anchorBounds.getTop()) - (horizontal ? childBounds.getLeft() : childBounds.getTop()); targetCovered = (layoutDirection == kLayoutDirectionRTL && horizontal) @@ -1056,8 +1056,8 @@ MultiChildScrollableComponent::processLayoutChangesInternal(bool useDirtyFlag, b } // Record current range of available motion to avoid re-calculating during scroll trimming. - auto firstLoadedChildBoundary = mChildren.at(mEnsuredChildren.lowerBound())->getCalculated(kPropertyBounds).getRect(); - auto lastLoadedChildBoundary = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).getRect(); + const auto& firstLoadedChildBoundary = mChildren.at(mEnsuredChildren.lowerBound())->getCalculated(kPropertyBounds).get(); + const auto& lastLoadedChildBoundary = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).get(); auto lastEdge = horizontal ? (layoutDirection == kLayoutDirectionLTR ? lastLoadedChildBoundary.getRight() : firstLoadedChildBoundary.getRight()) : lastLoadedChildBoundary.getBottom(); auto startEdge = horizontal ? (layoutDirection == kLayoutDirectionLTR ? firstLoadedChildBoundary.getLeft() : @@ -1072,7 +1072,7 @@ MultiChildScrollableComponent::onScrollPositionUpdated() ScrollableComponent::onScrollPositionUpdated(); for (int i = mEnsuredChildren.lowerBound(); i <= mEnsuredChildren.upperBound(); i++) { - auto child = std::dynamic_pointer_cast(mChildren.at(i)); + auto child = CoreComponent::cast(mChildren.at(i)); if (child != nullptr && child->isAttached()) { child->markGlobalToLocalTransformStale(); } @@ -1093,7 +1093,7 @@ MultiChildScrollableComponent::getSnapOffsetForChild( { float scrollTo = 0; float childStart, childEnd, currentPosition; - auto childBoundsInParent = child->getCalculated(kPropertyBounds).getRect(); + const auto& childBoundsInParent = child->getCalculated(kPropertyBounds).get(); bool isLTR = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; if (isVertical()) { @@ -1133,8 +1133,8 @@ float MultiChildScrollableComponent::childFractionOnScreenWithProposedScrollOffset(const ComponentPtr& child, float scrollOffset) const { const auto vertical = isVertical(); - auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - auto parentBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& childBounds = child->getCalculated(kPropertyBounds).get(); + auto parentBounds = getCalculated(kPropertyInnerBounds).get(); parentBounds.offset(vertical ? Point(0, scrollOffset) : Point(scrollOffset, 0)); auto intersectionRect = parentBounds.intersect(childBounds); @@ -1166,7 +1166,7 @@ MultiChildScrollableComponent::findChildCloseToPosition(const Point& position, b for (int i = mEnsuredChildren.upperBound(); i >= mEnsuredChildren.lowerBound(); i--) { auto child = mChildren.at(i); - auto bounds = child->getCalculated(kPropertyBounds).getRect(); + const auto& bounds = child->getCalculated(kPropertyBounds).get(); if (bounds.empty()) continue; if (bounds.contains(position)) @@ -1203,8 +1203,8 @@ MultiChildScrollableComponent::getSnapOffset() const // It's unidirectional on purpose, snap on multidirectional scrolling is quite unclear const auto vertical = isVertical(); - Rect parentInnerBounds = getCalculated(kPropertyInnerBounds).getRect(); - auto lastChildBounds = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).getRect(); + const auto& parentInnerBounds = getCalculated(kPropertyInnerBounds).get(); + const auto& lastChildBounds = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).get(); bool isRTL = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL; auto lastChildBottom = isRTL ? lastChildBounds.getBottomLeft() : lastChildBounds.getBottomRight(); diff --git a/aplcore/src/component/pagercomponent.cpp b/aplcore/src/component/pagercomponent.cpp index c682663..a453ba6 100644 --- a/aplcore/src/component/pagercomponent.cpp +++ b/aplcore/src/component/pagercomponent.cpp @@ -48,10 +48,10 @@ PagerComponent::PagerComponent(const ContextPtr& context, {} void -PagerComponent::release() +PagerComponent::releaseSelf() { mCurrentAnimation = nullptr; - ActionableComponent::release(); + ActionableComponent::releaseSelf(); } inline Object @@ -86,7 +86,7 @@ PagerComponent::propDefSet() const { for (size_t index = 0 ; index < component.getChildCount() ; index++) { auto child = component.getChildAt(index); if (child->getId() == id || child->getUniqueId() == id) { - dynamic_cast(component).setPageImmediate(index); + ((PagerComponent&)component).setPageImmediate((int)index); return; } } @@ -97,7 +97,7 @@ PagerComponent::propDefSet() const { }; static auto setPageIndex = [](CoreComponent& component, const Object& value) -> void { - dynamic_cast(component).setPageImmediate(value.asInt()); + ((PagerComponent&)component).setPageImmediate(value.asInt()); }; static ComponentPropDefSet sPagerComponentProperties(ActionableComponent::propDefSet(), { @@ -116,6 +116,12 @@ PagerComponent::propDefSet() const { return sPagerComponentProperties; } +std::shared_ptr +PagerComponent::cast(const std::shared_ptr& component) { + return component && component->getType() == ComponentType::kComponentTypePager + ? std::static_pointer_cast(component) : nullptr; +} + void PagerComponent::initialize() { CoreComponent::initialize(); @@ -125,7 +131,7 @@ PagerComponent::initialize() { mCalculated.set(kPropertyCurrentPage, currentPage); // If native gestures enabled - register them. - mGestureHandlers.emplace_back(PagerFlingGesture::create(std::static_pointer_cast(shared_from_this()))); + mGestureHandlers.emplace_back(PagerFlingGesture::create(ActionableComponent::cast(shared_from_this()))); } void @@ -182,7 +188,10 @@ PagerComponent::setPageUtil( const ActionRef& ref, bool skipDefaultAnimation) { - std::dynamic_pointer_cast(target)->handleSetPage(index, direction, ref, skipDefaultAnimation); + if (target->getType() == ComponentType::kComponentTypePager) { + std::static_pointer_cast(target)->handleSetPage(index, direction, ref, + skipDefaultAnimation); + } } void @@ -209,7 +218,7 @@ PagerComponent::handleSetPage(int index, PageDirection direction, const ActionRe startPageMove(direction, currentPage, index); // Animate if required. - std::weak_ptr weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + std::weak_ptr weak_ptr(std::static_pointer_cast(shared_from_this())); disableGestures(); if (mPageMoveHandler && !(mPageMoveHandler->isDefault() && skipDefaultAnimation)) { auto duration = getRootConfig().getDefaultPagerAnimationDuration(); @@ -310,7 +319,7 @@ isOnPage(const CoreComponentPtr& pager, const CoreComponentPtr& child, const Cor auto comp = targetComponent; while (comp && comp != pager) { if (comp == child) return true; - comp = std::static_pointer_cast(comp->getParent()); + comp = CoreComponent::cast(comp->getParent()); } return false; } @@ -597,9 +606,9 @@ PagerComponent::insertChild(const CoreComponentPtr& child, size_t index, bool us } void -PagerComponent::removeChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) +PagerComponent::removeChildAfterMarkedRemoved(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) { - CoreComponent::removeChild(child, index, useDirtyFlag); + CoreComponent::removeChildAfterMarkedRemoved(child, index, useDirtyFlag); mContext->layoutManager().removeAsTopNode(child); int currentPage = getCalculated(kPropertyCurrentPage).asInt(); if (currentPage >= index && currentPage != 0) { @@ -768,7 +777,7 @@ PagerComponent::takeFocusFromChild(FocusDirection direction, const Rect& origin) { auto targetDirection = focusDirectionToPage(direction); if (targetDirection != kPageDirectionNone) { - auto bounds = getCalculated(kPropertyBounds).getRect(); + const auto& bounds = getCalculated(kPropertyBounds).get(); Rect offsetRect = origin.empty() ? Rect(0, 0, bounds.getWidth(), bounds.getHeight()) : origin; switch(direction) { case kFocusDirectionLeft: @@ -792,9 +801,9 @@ PagerComponent::takeFocusFromChild(FocusDirection direction, const Rect& origin) auto allowedPageDirection = pageDirection(); if (allowedPageDirection == kPageDirectionBoth || targetDirection == allowedPageDirection) { - const int childCount = getChildCount(); + const auto childCount = getChildCount(); const auto delta = targetDirection == kPageDirectionForward ? 1 : -1; - const auto targetPage = (pagePosition() + delta + childCount) % childCount; + const auto targetPage = (int)((pagePosition() + delta + childCount) % childCount); // Reset any running commands that may affect page position. getContext()->sequencer().releaseResource({kExecutionResourcePosition, shared_from_this()}); setPageUtil(getContext(), shared_from_corecomponent(), targetPage, targetDirection, ActionRef(nullptr)); diff --git a/aplcore/src/component/scrollablecomponent.cpp b/aplcore/src/component/scrollablecomponent.cpp index 7979eb8..a05d233 100644 --- a/aplcore/src/component/scrollablecomponent.cpp +++ b/aplcore/src/component/scrollablecomponent.cpp @@ -33,7 +33,13 @@ namespace apl { ScrollableComponent::ScrollableComponent(const ContextPtr& context, Properties&& properties, const Path& path) : ActionableComponent(context, std::move(properties), path), - mStickyTree(std::make_shared(*this)) {}; + mStickyTree(std::make_shared(*this)) {} + +std::shared_ptr +ScrollableComponent::cast(const std::shared_ptr& component) { + return component && CoreComponent::cast(component)->scrollable() + ? std::static_pointer_cast(component) : nullptr; +} const ComponentPropDefSet& ScrollableComponent::propDefSet() const @@ -43,7 +49,7 @@ ScrollableComponent::propDefSet() const }; static auto setScrollOffset = [](CoreComponent& component, const Object& value) -> void { - dynamic_cast(component).setScrollPositionDirectly(value.asNumber()); + ((ScrollableComponent&)component).setScrollPositionDirectly((float)value.asNumber()); }; static auto getScrollPercent = [](const CoreComponent& component) -> Object { @@ -57,7 +63,7 @@ ScrollableComponent::propDefSet() const } else { scrollSize = YGNodeLayoutGetHeight(component.getNode()); } - dynamic_cast(component).setScrollPositionDirectly(scrollSize * value.asNumber()); + ((ScrollableComponent&)component).setScrollPositionDirectly(scrollSize * (float)value.asNumber()); }; static ComponentPropDefSet sScrollableComponentProperties(ActionableComponent::propDefSet(), { @@ -148,7 +154,7 @@ void ScrollableComponent::initialize() { ActionableComponent::initialize(); - mGestureHandlers.emplace_back(ScrollGesture::create(std::static_pointer_cast(shared_from_this()))); + mGestureHandlers.emplace_back(ScrollGesture::create(ScrollableComponent::cast(shared_from_this()))); } void @@ -191,7 +197,7 @@ ScrollableComponent::canScroll(FocusDirection direction) CoreComponentPtr ScrollableComponent::takeFocusFromChild(FocusDirection direction, const Rect& origin) { - auto bounds = getCalculated(kPropertyBounds).getRect(); + const auto& bounds = getCalculated(kPropertyBounds).get(); Rect offsetRect = origin; if (origin.empty()) { // If empty - we simulate entrance from edge opposite to movement direction. diff --git a/aplcore/src/component/scrollviewcomponent.cpp b/aplcore/src/component/scrollviewcomponent.cpp index 0b2f0d7..076d82f 100644 --- a/aplcore/src/component/scrollviewcomponent.cpp +++ b/aplcore/src/component/scrollviewcomponent.cpp @@ -74,12 +74,12 @@ ScrollViewComponent::trimScroll(const Point& point) { float ScrollViewComponent::maxScroll() const { - float bottom = mCalculated.get(kPropertyInnerBounds).getRect().getBottom(); + float bottom = mCalculated.get(kPropertyInnerBounds).get().getBottom(); if (mChildren.empty()) { return bottom; } auto child = mChildren.at(mChildren.size() - 1); - return nonNegative(child->getCalculated(kPropertyBounds).getRect().getBottom() - bottom); + return nonNegative(child->getCalculated(kPropertyBounds).get().getBottom() - bottom); } bool @@ -88,8 +88,8 @@ ScrollViewComponent::allowForward() const { return false; auto child = getChildAt(0); - auto innerScrollSize = child->getCalculated(kPropertyBounds).getRect().getHeight(); - auto scrollSize = mCalculated.get(kPropertyInnerBounds).getRect().getHeight(); + auto innerScrollSize = child->getCalculated(kPropertyBounds).get().getHeight(); + auto scrollSize = mCalculated.get(kPropertyInnerBounds).get().getHeight(); auto currentPosition = mCalculated.get(kPropertyScrollPosition).asNumber(); return currentPosition + scrollSize < innerScrollSize; } diff --git a/aplcore/src/component/sequencecomponent.cpp b/aplcore/src/component/sequencecomponent.cpp index 5a2f3cf..6e2cd74 100644 --- a/aplcore/src/component/sequencecomponent.cpp +++ b/aplcore/src/component/sequencecomponent.cpp @@ -63,7 +63,7 @@ SequenceComponent::layoutPropDefSet() const { size_t SequenceComponent::estimateChildrenToCover(float distance, size_t baseChild) { - auto size = mChildren.at(baseChild)->getCalculated(kPropertyBounds).getRect(); + const auto& size = mChildren.at(baseChild)->getCalculated(kPropertyBounds).get(); auto vertical = isVertical(); return std::ceil(std::abs(distance) / (vertical ? size.getHeight() : size.getWidth())); } diff --git a/aplcore/src/component/textcomponent.cpp b/aplcore/src/component/textcomponent.cpp index 383f448..6d0beb2 100644 --- a/aplcore/src/component/textcomponent.cpp +++ b/aplcore/src/component/textcomponent.cpp @@ -58,7 +58,7 @@ TextComponent::TextComponent(const ContextPtr& context, return self->baselineText(width, height); }; - if (dynamic_cast(mContext->measure().get())) { + if (mContext->measure()->sceneGraphCompatible()) { YGNodeSetMeasureFunc(mYGNodeRef, sgTextMeasureFunc); YGNodeSetBaselineFunc(mYGNodeRef, sgTextBaselineFunc); } else { @@ -89,12 +89,12 @@ TextComponent::propDefSet() const }; static auto karaokeTargetColorTrigger = [](Component& component) -> void { - auto& text = dynamic_cast(component); + auto& text = (TextComponent&)component; text.checkKaraokeTargetColor(); }; static auto fixTextAlignTrigger = [](Component& component) -> void { - auto& coreComp = dynamic_cast(component); + auto& coreComp = (TextComponent&)component; coreComp.updateTextAlign(true); #ifdef SCENEGRAPH coreComp.mTextProperties = nullptr; @@ -104,7 +104,7 @@ TextComponent::propDefSet() const static auto fixTextTrigger = [](Component& component) -> void { #ifdef SCENEGRAPH - auto& coreComp = dynamic_cast(component); + auto& coreComp = (TextComponent&)component; coreComp.mTextProperties = nullptr; coreComp.mLayout = nullptr; #endif // SCENEGRAPH @@ -112,7 +112,7 @@ TextComponent::propDefSet() const static auto fixTextChunkTrigger = [](Component& component) -> void { #ifdef SCENEGRAPH - auto& coreComp = dynamic_cast(component); + auto& coreComp = (TextComponent&)component; coreComp.mTextChunk = nullptr; coreComp.mLayout = nullptr; #endif // SCENEGRAPH @@ -146,7 +146,7 @@ TextComponent::eventPropertyMap() const static EventPropertyMap sTextEventProperties = eventPropertyMerge( CoreComponent::eventPropertyMap(), { - {"text", [](const CoreComponent *c) { return c->getCalculated(kPropertyText).getStyledText().getText(); }}, + {"text", [](const CoreComponent *c) { return c->getCalculated(kPropertyText).get().getText(); }}, {"color", [](const CoreComponent *c) { return c->getCalculated(kPropertyColor); }}, }); @@ -312,7 +312,7 @@ TextComponent::getVisualContextType() const Point TextComponent::calcSceneGraphOffset() const { - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); auto textSize = mLayout ? mLayout->getSize() : Size{}; float dx = 0; @@ -367,7 +367,7 @@ TextComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) { auto transform = sg::transform(calcSceneGraphOffset(), nullptr); populateTextNodes(sg::TransformNode::cast(transform)); - layer->appendContent(transform); + layer->setContent(transform); return layer; } @@ -403,7 +403,7 @@ TextComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) { bool TextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) { - auto* transform = sg::TransformNode::cast(mSceneGraphLayer->content().front()); + auto* transform = sg::TransformNode::cast(mSceneGraphLayer->content()); auto* text = sg::TextNode::cast(transform->child()); ensureSGTextLayout(); @@ -441,43 +441,45 @@ TextComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) void TextComponent::populateTextNodes(sg::Node *transform) { - const auto range = getCalculated(kPropertyRangeKaraokeTarget).getRange(); + const auto range = getCalculated(kPropertyRangeKaraokeTarget).get(); const auto lineCount = mLayout->getLineCount(); const auto lineRange = lineCount > 0 ? Range(0, lineCount - 1) : Range(); // Calculate Karaoke strips if needed if (!range.empty() && lineCount > 0) { + sg::NodePtr child = nullptr; - // Identify lines BEFORE the karaoke range - auto subrange = lineRange.subsetBelow(range.lowerBound()); + // Build up the child list in reverse order. Start with lines beyond the karaoke range + auto subrange = lineRange.subsetAbove(range.upperBound()); if (!subrange.empty()) - transform->appendChild(sg::text( - mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))), subrange)); + child = sg::text(mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))), subrange); // Lines within the karaoke range subrange = lineRange.intersectWith(range); if (!subrange.empty()) - transform->appendChild(sg::text( - mLayout, sg::fill(sg::paint(getCalculated(kPropertyColorKaraokeTarget))), - subrange)); + child = + sg::text(mLayout, sg::fill(sg::paint(getCalculated(kPropertyColorKaraokeTarget))), + subrange) + ->setNext(child); - // Lines beyond the karaoke range - subrange = lineRange.subsetAbove(range.upperBound()); + // Identify lines BEFORE the karaoke range + subrange = lineRange.subsetBelow(range.lowerBound()); if (!subrange.empty()) - transform->appendChild(sg::text( - mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))), subrange)); + child = sg::text(mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))), subrange) + ->setNext(child); + + transform->setChild(child); } else { - transform->appendChild(sg::text( - mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))))); + transform->setChild(sg::text(mLayout, sg::fill(sg::paint(getCalculated(kPropertyColor))))); } } YGSize TextComponent::measureText(float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) { - auto *tm = dynamic_cast(mContext->measure().get()); - assert(tm); + assert(mContext->measure()->sceneGraphCompatible()); + auto *tm = (sg::TextMeasurement *)(mContext->measure().get()); // TODO: Hash and check for cache hits ensureTextProperties(); @@ -495,8 +497,8 @@ TextComponent::baselineText(float width, float height) { // Make the large assumption that Yoga needs baseline information with a text layout if (!mLayout) { - auto *tm = dynamic_cast(mContext->measure().get()); - assert(tm); + assert(mContext->measure()->sceneGraphCompatible()); + auto *tm = (sg::TextMeasurement *)(mContext->measure().get()); ensureTextProperties(); mLayout = tm->layout(mTextChunk, mTextProperties, width, MeasureMode::Undefined, height, MeasureMode::Undefined); @@ -523,10 +525,10 @@ TextComponent::ensureSGTextLayout() return; } - auto measure = std::dynamic_pointer_cast(mContext->measure()); - assert(measure); + assert(mContext->measure()->sceneGraphCompatible()); + auto measure = std::static_pointer_cast(mContext->measure()); - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); ensureTextProperties(); mLayout = measure->layout(mTextChunk, mTextProperties, innerBounds.getWidth(), @@ -537,7 +539,7 @@ void TextComponent::ensureTextProperties() { if (!mTextChunk) - mTextChunk = sg::TextChunk::create(getCalculated(kPropertyText).getStyledText()); + mTextChunk = sg::TextChunk::create(getCalculated(kPropertyText).get()); if (!mTextProperties) mTextProperties = sg::TextProperties::create( @@ -557,7 +559,7 @@ TextComponent::ensureTextProperties() bool TextComponent::setKaraokeLine(Range byteRange) { - const auto& previousLineRange = mCalculated.get(kPropertyRangeKaraokeTarget).getRange(); + const auto& previousLineRange = mCalculated.get(kPropertyRangeKaraokeTarget).get(); if (byteRange.empty()) { if (!previousLineRange.empty()) { @@ -573,7 +575,7 @@ TextComponent::setKaraokeLine(Range byteRange) auto lineRange = mLayout->getLineRangeFromByteRange(byteRange); if (previousLineRange != lineRange) { - mCalculated.set(kPropertyRangeKaraokeTarget, lineRange); + mCalculated.set(kPropertyRangeKaraokeTarget, std::move(lineRange)); setDirty(kPropertyRangeKaraokeTarget); return true; } @@ -584,7 +586,7 @@ TextComponent::setKaraokeLine(Range byteRange) Rect TextComponent::getKaraokeBounds() { - const auto& range = mCalculated.get(kPropertyRangeKaraokeTarget).getRange(); + const auto& range = mCalculated.get(kPropertyRangeKaraokeTarget).get(); if (range.empty()) return {}; diff --git a/aplcore/src/component/touchablecomponent.cpp b/aplcore/src/component/touchablecomponent.cpp index 9281f97..c06e9cd 100644 --- a/aplcore/src/component/touchablecomponent.cpp +++ b/aplcore/src/component/touchablecomponent.cpp @@ -44,7 +44,7 @@ TouchableComponent::setGestureHandlers() { auto handlers = getCalculated(kPropertyGestures).getArray(); for (auto& handler : handlers) { - auto gesture = Gesture::create(std::dynamic_pointer_cast(shared_from_this()), handler); + auto gesture = Gesture::create(std::static_pointer_cast(shared_from_this()), handler); if (gesture) { mGestureHandlers.emplace_back(gesture); } diff --git a/aplcore/src/component/touchwrappercomponent.cpp b/aplcore/src/component/touchwrappercomponent.cpp index 7c33ce9..db1df6e 100644 --- a/aplcore/src/component/touchwrappercomponent.cpp +++ b/aplcore/src/component/touchwrappercomponent.cpp @@ -36,6 +36,12 @@ TouchWrapperComponent::TouchWrapperComponent(const ContextPtr& context, : TouchableComponent(context, std::move(properties), path) { } +std::shared_ptr +TouchWrapperComponent::cast(const std::shared_ptr& component) { + return component && component->getType() == ComponentType::kComponentTypeTouchWrapper + ? std::static_pointer_cast(component) : nullptr; +} + const ComponentPropDefSet& TouchWrapperComponent::propDefSet() const { static ComponentPropDefSet sTouchWrapperComponentProperties(TouchableComponent::propDefSet()); @@ -44,7 +50,7 @@ TouchWrapperComponent::propDefSet() const { void TouchWrapperComponent::injectReplaceComponent(const CoreComponentPtr& child, bool above) { - auto bounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& bounds = getCalculated(kPropertyInnerBounds).get(); insertChild(child, above ? 1 : 0, true); diff --git a/aplcore/src/component/vectorgraphiccomponent.cpp b/aplcore/src/component/vectorgraphiccomponent.cpp index b0dcf81..5193c75 100644 --- a/aplcore/src/component/vectorgraphiccomponent.cpp +++ b/aplcore/src/component/vectorgraphiccomponent.cpp @@ -47,13 +47,13 @@ VectorGraphicComponent::VectorGraphicComponent(const ContextPtr& context, } void -VectorGraphicComponent::release() +VectorGraphicComponent::releaseSelf() { auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) - graphic.getGraphic()->release(); + if (graphic.is()) + graphic.get()->release(); - ActionableComponent::release(); + ActionableComponent::releaseSelf(); MediaComponentTrait::release(); } @@ -62,13 +62,11 @@ VectorGraphicComponent::propDefSet() const { static auto checkLayout = [](Component& component) { - auto& vg = static_cast(component); - vg.processLayoutChanges(true, false); + ((VectorGraphicComponent&)component).processLayoutChanges(true, false); }; - static auto resetOnLoadOnFailFlag = [](Component& component) { - auto& comp = dynamic_cast(component); - comp.mOnLoadOnFailReported = false; + static auto resetMediaState = [](Component& component) { + ((VectorGraphicComponent&)component).sourcePropertyChanged(); }; static auto sVectorGraphicComponentProperties = ComponentPropDefSet(TouchableComponent::propDefSet(), MediaComponentTrait::propDefList()) @@ -79,7 +77,7 @@ VectorGraphicComponent::propDefSet() const {kPropertyOnFail, Object::EMPTY_ARRAY(), asCommand, kPropIn}, {kPropertyOnLoad, Object::EMPTY_ARRAY(), asCommand, kPropIn}, {kPropertyScale, kVectorGraphicScaleNone, sVectorGraphicScaleMap, kPropInOut | kPropStyled | kPropDynamic | kPropVisualHash, checkLayout}, - {kPropertySource, "", asVectorGraphicSource, kPropInOut | kPropDynamic | kPropVisualHash | kPropEvaluated, resetOnLoadOnFailFlag}, + {kPropertySource, "", asVectorGraphicSource, kPropInOut | kPropDynamic | kPropVisualHash | kPropEvaluated, resetMediaState}, }); return sVectorGraphicComponentProperties; @@ -102,7 +100,7 @@ VectorGraphicComponent::initialize() auto graphic = Graphic::create(mContext, graphicResource, Properties(mProperties), - std::static_pointer_cast(shared_from_this())); + shared_from_corecomponent()); if (graphic) { mCalculated.set(kPropertyGraphic, graphic); height = graphic->getIntrinsicHeight(); @@ -132,7 +130,7 @@ VectorGraphicComponent::updateStyle() // Changing the style is likely to change the graphic auto graphic = mCalculated.get(kPropertyGraphic); auto stylePtr = getStyle(); - if (graphic.isGraphic() && graphic.getGraphic()->updateStyle(stylePtr)) + if (graphic.is() && graphic.get()->updateStyle(stylePtr)) setDirty(kPropertyGraphic); // Changing the style may result in a size change or a position change @@ -155,25 +153,18 @@ VectorGraphicComponent::eventPropertyMap() const bool VectorGraphicComponent::updateGraphic(const GraphicContentPtr& json) { - if (!json) { + if (!json) return false; - } - // Remove any existing graphic - auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) { - graphic.getGraphic()->release(); - setDirty(kPropertyGraphic); -#ifdef SCENEGRAPH - mSceneGraphLayer.reset(); // TODO: Will this kill the media layer appropriately? -#endif // SCENEGRAPH - } + + // Release any existing graphic + releaseGraphic(); auto url = encodeUrl(getCalculated(kPropertySource).asString()); auto path = Path("_url").addObject(url); auto g = Graphic::create(mContext, json->get(), Properties(mProperties), - std::static_pointer_cast(shared_from_this()), + shared_from_corecomponent(), path, getStyle()); if (!g) @@ -181,6 +172,7 @@ VectorGraphicComponent::updateGraphic(const GraphicContentPtr& json) mCalculated.set(kPropertyGraphic, g); setDirty(kPropertyGraphic); + mGraphicReplaced = true; // Recalculate the media bounds. This will internally do a layout processLayoutChanges(true, false); @@ -188,16 +180,60 @@ VectorGraphicComponent::updateGraphic(const GraphicContentPtr& json) return true; } +void +VectorGraphicComponent::releaseGraphic() +{ + auto graphic = mCalculated.get(kPropertyGraphic); + if (!graphic.isNull()) { + if (graphic.is()) + graphic.get()->release(); + setDirty(kPropertyGraphic); + mCalculated.set(kPropertyGraphic, Object::NULL_OBJECT()); + mGraphicReplaced = true; + } +} + +void +VectorGraphicComponent::sourcePropertyChanged() +{ + mOnLoadOnFailReported = false; + releaseGraphic(); + + // Load a local graphic if it exists + auto source = mCalculated.get(kPropertySource); + if (source.isString()) { + auto graphicResource = mContext->getGraphic(source.getString()); + if (!graphicResource.empty()) { + auto graphic = Graphic::create(mContext, + graphicResource, + Properties(mProperties), + shared_from_corecomponent()); + if (graphic) { + mCalculated.set(kPropertyGraphic, graphic); + setDirty(kPropertyGraphic); + mGraphicReplaced = true; + + // Recalculate the media bounds. This will internally run a layout + processLayoutChanges(true, false); + graphic->clearDirty(); // First use of the graphic; clear dirty flags + } + } + } + + // Fix the media state and toggle a download for a URL-based graphic + resetMediaFetchState(); +} + void VectorGraphicComponent::processLayoutChanges(bool useDirtyFlag, bool first) { CoreComponent::processLayoutChanges(useDirtyFlag, first); auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) { - auto g = graphic.getGraphic(); + if (graphic.is()) { + auto g = graphic.get(); - auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); + const auto& innerBounds = mCalculated.get(kPropertyInnerBounds).get(); double width = g->getIntrinsicWidth(); double height = g->getIntrinsicHeight(); auto scaleProp = static_cast(mCalculated.get(kPropertyScale).getInteger()); @@ -276,7 +312,7 @@ VectorGraphicComponent::processLayoutChanges(bool useDirtyFlag, bool first) Rect r(x, y, width, height); auto mediaBounds = mCalculated.get(kPropertyMediaBounds); - if (!mediaBounds.isRect() || r != mediaBounds.getRect()) { + if (!mediaBounds.is() || r != mediaBounds.get()) { mCalculated.set(kPropertyMediaBounds, std::move(r)); if (useDirtyFlag) setDirty(kPropertyMediaBounds); @@ -298,8 +334,8 @@ bool VectorGraphicComponent::setPropertyInternal(const std::string& key, const apl::Object& value) { auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) { - auto g = graphic.getGraphic(); + if (graphic.is()) { + auto g = graphic.get(); return g->setProperty(key, value); } @@ -310,8 +346,8 @@ std::pair VectorGraphicComponent::getPropertyInternal(const std::string& key) const { auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) - return graphic.getGraphic()->getProperty(key); + if (graphic.is()) + return graphic.get()->getProperty(key); return { Object::NULL_OBJECT(), false }; } @@ -319,10 +355,10 @@ VectorGraphicComponent::getPropertyInternal(const std::string& key) const void VectorGraphicComponent::clearDirty() { // order is important. clear the component before the graphic - Component::clearDirty(); + CoreComponent::clearDirty(); auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) { - auto g = graphic.getGraphic(); + if (graphic.is()) { + auto g = graphic.get(); g->clearDirty(); } } @@ -331,9 +367,9 @@ std::shared_ptr VectorGraphicComponent::createTouchEventProperties(const Point &localPoint) const { std::shared_ptr eventProperties = TouchableComponent::createTouchEventProperties(localPoint); - auto graphic = mCalculated.get(kPropertyGraphic).getGraphic(); + auto graphic = mCalculated.get(kPropertyGraphic).get(); - const auto mediaBounds = mCalculated.get(kPropertyMediaBounds).getRect(); + const auto& mediaBounds = mCalculated.get(kPropertyMediaBounds).get(); auto viewportWidth = graphic->getViewportWidth(); auto viewportHeight = graphic->getViewportHeight(); @@ -387,7 +423,7 @@ VectorGraphicComponent::getSources() { std::vector sources; auto graphic = mCalculated.get(kPropertyGraphic); - if (graphic.isGraphic()) { + if (graphic.is()) { // Graphic is already present, nothing to load return sources; } @@ -397,10 +433,10 @@ VectorGraphicComponent::getSources() { auto graphicResource = mContext->getGraphic(source.getString()); if (graphicResource.empty()) { // Graphic is not a local resource, treat as URI - sources.emplace_back(source.asURLRequest()); + sources.emplace_back(URLRequest::asURLRequest(source)); } - } else if (source.isURLRequest()) { - sources.emplace_back(source.getURLRequest()); + } else if (source.is()) { + sources.emplace_back(source.get()); } return sources; @@ -432,7 +468,7 @@ VectorGraphicComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGra // Build the basic data structures auto mediaLayer = sg::layer(mUniqueId + "-mediaLayer", Rect{0,0,1,1}, 1.0f, Transform2D()); auto transform = sg::transform(); - mediaLayer->appendContent(transform); + mediaLayer->setContent(transform); layer->appendChild(mediaLayer); fixMediaLayer(sceneGraph, mediaLayer); @@ -445,9 +481,9 @@ bool VectorGraphicComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) { auto boundsChanged = isDirty(kPropertyMediaBounds); // The media bounds changed (for example, due to layout changes) - auto graphicChanged = isDirty(kPropertyGraphic); // The current graphic had some property changes + auto graphicDirty = isDirty(kPropertyGraphic); // The current graphic had some property changes or it is a new graphic - if (!boundsChanged && !graphicChanged) + if (!boundsChanged && !graphicDirty && !mGraphicReplaced) return false; auto mediaLayer = mSceneGraphLayer->children().at(0); @@ -459,9 +495,13 @@ VectorGraphicComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGra if (mediaLayer->isFlagSet(sg::Layer::kFlagSizeChanged)) mediaLayer->setFlag(sg::Layer::kFlagRedrawContent); - // If the graphic changed (and is valid), let the update routine decide if we need a redraw of the content - if (graphicChanged && validGraphic) { - auto graphic = getCalculated(kPropertyGraphic).getGraphic(); + // If we changed the graphic, we definitely need to redraw the content + if (mGraphicReplaced) { + mediaLayer->setFlag(sg::Layer::kFlagRedrawContent); + mGraphicReplaced = false; + } + else if (graphicDirty && validGraphic) { // If it didn't change but was dirty, let the update routine decide + auto graphic = getCalculated(kPropertyGraphic).get(); if (graphic->updateSceneGraph(sceneGraph)) mediaLayer->setFlag(sg::Layer::kFlagRedrawContent); } @@ -482,24 +522,24 @@ bool VectorGraphicComponent::fixMediaLayer(sg::SceneGraphUpdates& sceneGraph, const sg::LayerPtr& mediaLayer) { - auto* transform = sg::TransformNode::cast(mediaLayer->content().at(0)); + auto* transform = sg::TransformNode::cast(mediaLayer->content()); transform->removeAllChildren(); auto object = getCalculated(kPropertyGraphic); - if (!object.isGraphic()) + if (!object.is()) return false; - auto graphic = object.getGraphic(); + auto graphic = object.get(); if (!graphic || !graphic->isValid()) return false; - auto mediaBounds = getCalculated(kPropertyMediaBounds).getRect(); + const auto& mediaBounds = getCalculated(kPropertyMediaBounds).get(); mediaLayer->setBounds(mediaBounds); sg::TransformNode::cast(transform)->setTransform( Transform2D::scale(mediaBounds.getWidth() / graphic->getViewportWidth(), mediaBounds.getHeight() / graphic->getViewportHeight())); - transform->appendChild(graphic->getSceneGraph(sceneGraph)); + transform->setChild(graphic->getSceneGraph(sceneGraph)); return true; } @@ -530,6 +570,10 @@ VectorGraphicComponent::onLoad() { if (mOnLoadOnFailReported) return; + + assert(mMediaObjectHolders.size() == 1); + updateGraphic(mMediaObjectHolders.at(0).getMediaObject()->graphic()); + auto component = getComponent(); auto& commands = component->getCalculated(kPropertyOnLoad); component->getContext()->sequencer().executeCommands( diff --git a/aplcore/src/component/videocomponent.cpp b/aplcore/src/component/videocomponent.cpp index e4f24e9..792f61e 100644 --- a/aplcore/src/component/videocomponent.cpp +++ b/aplcore/src/component/videocomponent.cpp @@ -59,6 +59,12 @@ VideoComponent::create(const ContextPtr& context, return ptr; } +std::shared_ptr +VideoComponent::cast(const std::shared_ptr& component) { + return component && component->getType() == ComponentType::kComponentTypeVideo + ? std::static_pointer_cast(component) : nullptr; +} + void VideoComponent::playerCallback(MediaPlayerEventType eventType, const MediaState& mediaState) { @@ -218,7 +224,7 @@ VideoComponent::propDefSet() const return; } - auto& self = dynamic_cast(component); + auto& self = (VideoComponent&)component; for (size_t index = 0 ; index < PLAYING_STATE.size() ; index++) self.mCalculated.set(PLAYING_STATE.at(index), value.at(index)); @@ -226,14 +232,14 @@ VideoComponent::propDefSet() const }; static auto resetMediaState = [](Component& component) { - auto& comp = dynamic_cast(component); + auto& comp = (VideoComponent&)component; auto mediaPlayer = comp.getMediaPlayer(); if (mediaPlayer) mediaPlayer->setTrackList(mediaSourcesToTracks(comp.getCalculated(kPropertySource))); }; static auto setMute = [](Component& component) -> void { - auto& self = dynamic_cast(component); + auto& self = (VideoComponent&)component; auto mediaPlayer = self.getMediaPlayer(); if (mediaPlayer) mediaPlayer->setMute(self.getCalculated(kPropertyMuted).asBoolean()); @@ -452,13 +458,12 @@ VideoComponent::getCurrentUrl() const auto trackIndex = getCalculated(kPropertyTrackIndex).asInt(); if (trackIndex < 0 || trackIndex >= source.size()) return ""; - return source.at(trackIndex).getMediaSource().getUrl(); + return source.at(trackIndex).get().getUrl(); } static inline Object inlineGetCurrentURL(const CoreComponent *c) { - auto comp = dynamic_cast(c); - return comp->getCurrentUrl(); + return ((const VideoComponent*)c)->getCurrentUrl(); } const EventPropertyMap& @@ -507,7 +512,7 @@ VideoComponent::getTags(rapidjson::Value& outMap, rapidjson::Document::Allocator state = "playing"; } - auto currentSource = sources.getArray().at(trackIndex).getMediaSource(); + auto currentSource = sources.getArray().at(trackIndex).get(); rapidjson::Value media(rapidjson::kObjectType); media.AddMember("allowAdjustSeekPositionForward", allowAdjustSeekPositionForward, allocator); @@ -549,9 +554,9 @@ VideoComponent::constructSceneGraphLayer(sg::SceneGraphUpdates& sceneGraph) assert(top); // Find the target bounding box of the image. This is where we will be drawing the image. This is in DP. - auto innerBounds = getCalculated(kPropertyInnerBounds).getRect(); + const auto& innerBounds = getCalculated(kPropertyInnerBounds).get(); auto scale = static_cast(getCalculated(kPropertyScale).getInteger()); - top->appendContent(sg::video(mMediaPlayer, innerBounds, scale)); + top->setContent(sg::video(mMediaPlayer, innerBounds, scale)); return top; } @@ -560,8 +565,9 @@ VideoComponent::updateSceneGraphInternal(sg::SceneGraphUpdates& sceneGraph) { const auto dirty = isDirty(kPropertyInnerBounds) || isDirty(kPropertyScale); if (dirty) { - auto *video = sg::VideoNode::cast(mSceneGraphLayer->content().front()); - video->setTarget(getCalculated(kPropertyInnerBounds).getRect()); + auto *video = sg::VideoNode::cast(mSceneGraphLayer->content()); + video->setTarget(getCalculated(kPropertyInnerBounds).get()); + video->setScale(static_cast(getCalculated(kPropertyScale).getInteger())); } diff --git a/aplcore/src/content/rootconfig.cpp b/aplcore/src/content/rootconfig.cpp index 140a877..65c579c 100644 --- a/aplcore/src/content/rootconfig.cpp +++ b/aplcore/src/content/rootconfig.cpp @@ -274,4 +274,12 @@ RootConfig::setEnvironmentValue(const std::string& name, const Object& value) { return *this; } +/** + * @return Animation easing for SwipeAway gesture. + */ +EasingPtr +RootConfig::getSwipeAwayAnimationEasing() const { + return getProperty(RootProperty::kSwipeAwayAnimationEasing).get(); +} + } // namespace apl diff --git a/aplcore/src/datagrammar/bytecode.cpp b/aplcore/src/datagrammar/bytecode.cpp index 54687fc..d339010 100644 --- a/aplcore/src/datagrammar/bytecode.cpp +++ b/aplcore/src/datagrammar/bytecode.cpp @@ -100,7 +100,7 @@ ByteCode::symbols(SymbolReferenceMap& symbols) if (!ref.first.empty()) symbols.emplace(ref); - ref = mData[cmd.value].getBoundSymbol()->getSymbol(); + ref = mData[cmd.value].get()->getSymbol(); operand = Object::NULL_OBJECT(); break; diff --git a/aplcore/src/datagrammar/bytecodeassembler.cpp b/aplcore/src/datagrammar/bytecodeassembler.cpp index a9ed458..55c3967 100644 --- a/aplcore/src/datagrammar/bytecodeassembler.cpp +++ b/aplcore/src/datagrammar/bytecodeassembler.cpp @@ -71,18 +71,22 @@ ByteCodeAssembler::parse(const Context& context, const std::string& value) return value; pegtl::string_input<> in(value, ""); - try { - datagrammar::ByteCodeAssembler assembler(context); + datagrammar::ByteCodeAssembler assembler(context); + fail_state failState; + + if (!pegtl::parse(in, failState, assembler) || failState.failed) { + if (failState.failed) { + const auto p = failState.positions().front(); + CONSOLE(context) << "Syntax error: " << failState.what(); + CONSOLE(context) << in.line_at(p); + CONSOLE(context) << std::string(p.byte_in_line, ' ') << "^"; + } else { + CONSOLE(context) << "Syntax error in: " << value; + } - pegtl::parse(in, assembler); + } else { return assembler.retrieve(); } - catch (const pegtl::parse_error& e) { - const auto p = e.positions.front(); - CONSOLE(context) << "Syntax error: " << e.what(); - CONSOLE(context) << in.line_at(p); - CONSOLE(context) << std::string(p.byte_in_line, ' ') << "^"; - } return value; } diff --git a/aplcore/src/datagrammar/bytecodeoptimizer.cpp b/aplcore/src/datagrammar/bytecodeoptimizer.cpp index f6dfe0b..192311b 100644 --- a/aplcore/src/datagrammar/bytecodeoptimizer.cpp +++ b/aplcore/src/datagrammar/bytecodeoptimizer.cpp @@ -18,6 +18,7 @@ #include "apl/datagrammar/functions.h" #include "apl/datagrammar/boundsymbol.h" #include "apl/engine/context.h" +#include "apl/primitives/functions.h" #include "apl/utils/log.h" namespace apl { @@ -249,7 +250,7 @@ ByteCodeOptimizer::simplifyOperations() if (out_constants >= item_count) {// Number of arguments + the function name auto offset = -item_count; // Offset to the name of the function auto f = getValueOffsetFromEnd(offset++); - if (f.isFunction() && f.isPure()) { + if (f.is() && f.isPure()) { LOG_IF(DEBUG_OPTIMIZER) << "Reducing function at " << pc; std::vector args(cmd.value); // Reserve enough space for (int i = 0; i < cmd.value; i++) diff --git a/aplcore/src/datagrammar/functions.cpp b/aplcore/src/datagrammar/functions.cpp index 17d06c3..95d5578 100644 --- a/aplcore/src/datagrammar/functions.cpp +++ b/aplcore/src/datagrammar/functions.cpp @@ -18,6 +18,7 @@ #include "apl/datagrammar/boundsymbol.h" #include "apl/engine/context.h" +#include "apl/primitives/color.h" #include "apl/primitives/dimension.h" #include "apl/primitives/functions.h" #include "apl/utils/log.h" @@ -226,7 +227,7 @@ Compare(const Object& a, const Object& b) if (a.isBoolean() && b.isBoolean() && a.asBoolean() == b.asBoolean()) return 0; - if (a.isColor() && b.isColor() && a.getColor() == b.getColor()) + if (a.is() && b.is() && a.getColor() == b.getColor()) return 0; if (a.isNull() && b.isNull()) diff --git a/aplcore/src/datasource/dynamicindexlistdatasourceprovider.cpp b/aplcore/src/datasource/dynamicindexlistdatasourceprovider.cpp index 9120452..64664d4 100644 --- a/aplcore/src/datasource/dynamicindexlistdatasourceprovider.cpp +++ b/aplcore/src/datasource/dynamicindexlistdatasourceprovider.cpp @@ -395,7 +395,7 @@ DynamicIndexListDataSourceProvider::process(const Object& responseMap) { return false; } - auto connection = std::dynamic_pointer_cast(dataSourceConnection); + auto connection = std::static_pointer_cast(dataSourceConnection); auto context = connection->getContext(); if (!context) return false; @@ -458,15 +458,14 @@ DynamicIndexListDataSourceProvider::process(const Object& responseMap) { } if (result) - context->setDirtyDataSourceContext( - std::dynamic_pointer_cast(shared_from_this())); + context->setDirtyDataSourceContext(connection); return result; } std::pair DynamicIndexListDataSourceProvider::getBounds(const std::string& listId) { - auto connection = std::dynamic_pointer_cast(getConnection(listId)); + auto connection = std::static_pointer_cast(getConnection(listId)); if(!connection) return {}; diff --git a/aplcore/src/datasource/dynamictokenlistdatasourceprovider.cpp b/aplcore/src/datasource/dynamictokenlistdatasourceprovider.cpp index 43e1f8d..123a205 100644 --- a/aplcore/src/datasource/dynamictokenlistdatasourceprovider.cpp +++ b/aplcore/src/datasource/dynamictokenlistdatasourceprovider.cpp @@ -182,13 +182,12 @@ DynamicTokenListDataSourceProvider::process(const Object& responseMap) { if(!dataSourceConnection) return false; - auto connection = std::dynamic_pointer_cast(dataSourceConnection); + auto connection = std::static_pointer_cast(dataSourceConnection); bool result = processLazyLoadInternal(connection, responseMap); auto context = connection->getContext(); if (result && context != nullptr) - context->setDirtyDataSourceContext( - std::dynamic_pointer_cast(shared_from_this())); + context->setDirtyDataSourceContext(connection); return result; } diff --git a/aplcore/src/engine/binding.cpp b/aplcore/src/engine/binding.cpp index c7fe0a5..552dc76 100644 --- a/aplcore/src/engine/binding.cpp +++ b/aplcore/src/engine/binding.cpp @@ -16,6 +16,7 @@ #include "apl/engine/binding.h" #include "apl/primitives/dimension.h" #include "apl/primitives/object.h" +#include "apl/primitives/color.h" namespace apl { diff --git a/aplcore/src/engine/builder.cpp b/aplcore/src/engine/builder.cpp index eaf06ec..4fbcfdb 100644 --- a/aplcore/src/engine/builder.cpp +++ b/aplcore/src/engine/builder.cpp @@ -16,7 +16,6 @@ */ -#include #include #include "apl/component/containercomponent.h" @@ -264,7 +263,7 @@ Builder::expandSingleComponent(const ContextPtr& context, CoreComponentPtr oldComponent; if(mOld) { - oldComponent = std::dynamic_pointer_cast( + oldComponent = CoreComponent::cast( mOld->findComponentById(component->getId())); copyPreservedBindings(component, oldComponent); } diff --git a/aplcore/src/engine/evaluate.cpp b/aplcore/src/engine/evaluate.cpp index b88005f..f4bdd86 100644 --- a/aplcore/src/engine/evaluate.cpp +++ b/aplcore/src/engine/evaluate.cpp @@ -35,7 +35,7 @@ parseDataBinding(const Context& context, const std::string& value) { auto result = datagrammar::ByteCodeAssembler::parse(context, value); if (result.isEvaluable()) - return result.getByteCode()->simplify(); + return result.get()->simplify(); return result; } diff --git a/aplcore/src/engine/hovermanager.cpp b/aplcore/src/engine/hovermanager.cpp index f5cff4a..1f51467 100644 --- a/aplcore/src/engine/hovermanager.cpp +++ b/aplcore/src/engine/hovermanager.cpp @@ -89,7 +89,7 @@ HoverManager::findHoverByPosition(const Point& position) const { auto target = top->findComponentAtPosition(position); - return std::static_pointer_cast(target); + return CoreComponent::cast(target); } diff --git a/aplcore/src/engine/keyboardmanager.cpp b/aplcore/src/engine/keyboardmanager.cpp index b7e0797..edf5fda 100644 --- a/aplcore/src/engine/keyboardmanager.cpp +++ b/aplcore/src/engine/keyboardmanager.cpp @@ -71,7 +71,7 @@ KeyboardManager::handleKeyboard(KeyHandlerType type, const CoreComponentPtr& com LOG_IF(DEBUG_KEYBOARD_MANAGER).session(rootContext) << target->getUniqueId() << " " << type << " consumed."; } else { // propagate - target = std::static_pointer_cast(target->getParent()); + target = CoreComponent::cast(target->getParent()); } } diff --git a/aplcore/src/engine/layoutmanager.cpp b/aplcore/src/engine/layoutmanager.cpp index f6c8969..5c1fe69 100644 --- a/aplcore/src/engine/layoutmanager.cpp +++ b/aplcore/src/engine/layoutmanager.cpp @@ -188,7 +188,7 @@ LayoutManager::layoutComponent(const CoreComponentPtr& component, bool useDirtyF << " dirty_flag=" << useDirtyFlag << " parent=" << (parent ? parent->toDebugSimpleString() : "none"); - Size size = parent ? parent->getCalculated(kPropertyInnerBounds).getRect().getSize() : mConfiguredSize; + Size size = parent ? parent->getCalculated(kPropertyInnerBounds).get().getSize() : mConfiguredSize; if (size.empty()) return; @@ -264,7 +264,7 @@ LayoutManager::ensure(const CoreComponentPtr& component) bool attachedYogaNodeNeedsLayout = false; auto child = component; while (child->getParent()) { - auto parent = std::dynamic_pointer_cast(child->getParent()); + auto parent = CoreComponent::cast(child->getParent()); // If the child is attached to its parent, we don't need to do anything if (!child->getNode()->getOwner()) { diff --git a/aplcore/src/engine/rootcontext.cpp b/aplcore/src/engine/rootcontext.cpp index a964dc1..758e6f4 100644 --- a/aplcore/src/engine/rootcontext.cpp +++ b/aplcore/src/engine/rootcontext.cpp @@ -612,7 +612,7 @@ void RootContext::scrollToRectInComponent(const ComponentPtr& component, const Rect &bounds, CommandScrollAlign align) { auto scrollToAction = ScrollToAction::make( - mTimeManager, align, bounds, mContext, std::static_pointer_cast(component)); + mTimeManager, align, bounds, mContext, CoreComponent::cast(component)); if (scrollToAction && scrollToAction->isPending()) { mCore->sequencer().attachToSequencer(scrollToAction, SCROLL_TO_RECT_SEQUENCER); } @@ -953,9 +953,9 @@ RootContext::findComponentById(const std::string& id) const assert(mCore); // Fast path search for uid value - auto *ptr = dynamic_cast(mCore->uniqueIdManager().find(id)); - if (ptr) - return ptr->shared_from_this(); + auto *ptr = findByUniqueId(id); + if (ptr && ptr->objectType() == UIDObject::UIDObjectType::COMPONENT) + return static_cast(ptr)->shared_from_this(); // Depth-first search auto top = mCore->top(); @@ -981,7 +981,7 @@ RootContext::setFocus(FocusDirection direction, const Rect& origin, const std::s { assert(mCore); auto top = mCore->top(); - auto target = std::dynamic_pointer_cast(findComponentById(targetId)); + auto target = CoreComponent::cast(findComponentById(targetId)); if (!target) { LOG(LogLevel::kWarn).session(getSession()) << "Don't have component: " << targetId; @@ -1063,7 +1063,7 @@ RootContext::getSceneGraph() if (mSceneGraph->getLayer()) { mSceneGraph->updates().clear(); for (auto& component : mCore->dirty) - std::static_pointer_cast(component)->updateSceneGraph(mSceneGraph->updates()); + CoreComponent::cast(component)->updateSceneGraph(mSceneGraph->updates()); } else { auto top = mCore->top(); if (top) diff --git a/aplcore/src/engine/uidobject.cpp b/aplcore/src/engine/uidobject.cpp index 36be865..6c7d16e 100644 --- a/aplcore/src/engine/uidobject.cpp +++ b/aplcore/src/engine/uidobject.cpp @@ -19,9 +19,10 @@ namespace apl { -UIDObject::UIDObject(const ContextPtr& context) +UIDObject::UIDObject(const ContextPtr& context, UIDObject::UIDObjectType type) : mUniqueId(context->uniqueIdManager().create(this)), - mContext(context) + mContext(context), + mType(type) {} UIDObject::~UIDObject() diff --git a/aplcore/src/extension/extensionclient.cpp b/aplcore/src/extension/extensionclient.cpp index d57f2d0..aa62773 100644 --- a/aplcore/src/extension/extensionclient.cpp +++ b/aplcore/src/extension/extensionclient.cpp @@ -451,7 +451,7 @@ ExtensionClient::reportLiveMapChanges(const LiveDataRef& ref, const std::vector< std::string updateTriggerEvent = ref.updateEvent.name; std::string removeTriggerEvent = ref.removeEvent.name; - auto liveMap = std::dynamic_pointer_cast(ref.objectPtr); + auto liveMap = std::static_pointer_cast(ref.objectPtr); auto mapPtr = std::make_shared(liveMap->getMap()); for (auto& change : changes) { auto key = change.key(); @@ -507,7 +507,7 @@ ExtensionClient::reportLiveArrayChanges(const LiveDataRef& ref, const std::vecto std::string updateTriggerEvent; std::string removeTriggerEvent; - auto arrayPtr = std::make_shared(std::dynamic_pointer_cast(ref.objectPtr)->getArray()); + auto arrayPtr = std::make_shared(std::static_pointer_cast(ref.objectPtr)->getArray()); for (auto& change : changes) { switch (change.command()) { case LiveArrayChange::Command::INSERT : @@ -616,7 +616,7 @@ ExtensionClient::updateLiveMap(ExtensionLiveDataUpdateType type, const LiveDataR auto typeDef = mTypes.at(dataRef.type); auto item = operation.get("item"); - auto liveMap = std::dynamic_pointer_cast(dataRef.objectPtr); + auto liveMap = std::static_pointer_cast(dataRef.objectPtr); auto result = true; switch (type) { @@ -658,7 +658,7 @@ ExtensionClient::updateLiveArray(ExtensionLiveDataUpdateType type, const LiveDat } auto index = indexObj.getInteger(); auto count = operation.get("count"); - auto liveArray = std::dynamic_pointer_cast(dataRef.objectPtr); + auto liveArray = std::static_pointer_cast(dataRef.objectPtr); auto result = true; switch (type) { @@ -1354,12 +1354,12 @@ ExtensionClient::flushPendingEvents(const RootContextPtr& rootContext) // Generate changelist based on notion of nothing been there initially if (ref.objectType == kExtensionLiveDataTypeArray) { std::vector changes; - auto& array = std::dynamic_pointer_cast(ref.objectPtr)->getArray(); + auto& array = std::static_pointer_cast(ref.objectPtr)->getArray(); changes.emplace_back(LiveArrayChange::insert(0, array.size())); reportLiveArrayChanges(ref, changes); } else { std::vector changes; - auto& map = std::dynamic_pointer_cast(ref.objectPtr)->getMap(); + auto& map = std::static_pointer_cast(ref.objectPtr)->getMap(); for (auto& item : map) { changes.emplace_back(LiveMapChange::set(item.first)); } diff --git a/aplcore/src/extension/extensioncomponent.cpp b/aplcore/src/extension/extensioncomponent.cpp index 51c0510..9bdf066 100644 --- a/aplcore/src/extension/extensioncomponent.cpp +++ b/aplcore/src/extension/extensioncomponent.cpp @@ -36,6 +36,12 @@ ExtensionComponent::create(const ExtensionComponentDefinition& definition, return component; } +std::shared_ptr +ExtensionComponent::cast(const std::shared_ptr& component) { + return component && component->getType() == ComponentType::kComponentTypeExtension + ? std::static_pointer_cast(component) : nullptr; +} + ExtensionComponent::ExtensionComponent(const ExtensionComponentDefinition& definition, const ContextPtr& context, Properties&& properties, const Path& path) @@ -77,7 +83,7 @@ const EventPropertyMap & ExtensionComponent::eventPropertyMap() const { static auto getType = [](const CoreComponent *c) { - auto extensionComp = dynamic_cast(c); + const auto* extensionComp = (const ExtensionComponent*)c; if (extensionComp != nullptr) { return extensionComp->mDefinition.getName(); } @@ -85,11 +91,11 @@ ExtensionComponent::eventPropertyMap() const }; static auto getResourceId = [](const CoreComponent *c) { - auto extensionComp = dynamic_cast(c); - if (extensionComp != nullptr) { - return extensionComp->getResourceID(); - } - return std::string(); + const auto* extensionComp = (const ExtensionComponent*)c; + if (extensionComp != nullptr) { + return extensionComp->getResourceID(); + } + return std::string(); }; static EventPropertyMap sExtensionComponentEventProperties = EventPropertyMap({ @@ -100,10 +106,10 @@ ExtensionComponent::eventPropertyMap() const } void -ExtensionComponent::release() +ExtensionComponent::releaseSelf() { - CoreComponent::release(); getContext()->extensionManager().removeExtensionComponent(getResourceID()); + CoreComponent::releaseSelf(); } void @@ -184,7 +190,7 @@ void ExtensionComponent::notifyExtension(bool acquireResource) { // Notify the extension the component has changed auto extManager = getContext()->extensionManager(); - extManager.notifyComponentUpdate(std::static_pointer_cast(shared_from_this()), acquireResource); + extManager.notifyComponentUpdate(ExtensionComponent::cast(shared_from_this()), acquireResource); } diff --git a/aplcore/src/extension/extensionmanager.cpp b/aplcore/src/extension/extensionmanager.cpp index 02e7e58..8dff2e9 100644 --- a/aplcore/src/extension/extensionmanager.cpp +++ b/aplcore/src/extension/extensionmanager.cpp @@ -167,7 +167,7 @@ ExtensionManager::findAndCreateExtensionComponent(const std::string& componentTy if (componentDef != nullptr) { MakeComponentFunc func = [this, componentDef](const ContextPtr& context, Properties&& properties, const Path& path){ auto component = ExtensionComponent::create(*componentDef, context, std::move(properties), path); - auto extComponent = std::dynamic_pointer_cast(component); + auto extComponent = ExtensionComponent::cast(component); this->addExtensionComponent(extComponent->getResourceID(), extComponent); return component; }; diff --git a/aplcore/src/focus/focusfinder.cpp b/aplcore/src/focus/focusfinder.cpp index 026a31d..e36d1a1 100644 --- a/aplcore/src/focus/focusfinder.cpp +++ b/aplcore/src/focus/focusfinder.cpp @@ -189,7 +189,7 @@ FocusFinder::getImplicitFocusRoot(const CoreComponentPtr& focused, FocusDirectio CoreComponentPtr prevParent = parent; do { prevParent = parent; - parent = std::dynamic_pointer_cast(prevParent->getParent()); + parent = CoreComponent::cast(prevParent->getParent()); // If parent can't take focus in direction we want - expand the search. if(parent && parent->isFocusable() && parent->canConsumeFocusDirectionEvent(direction, true)) { return parent; @@ -208,7 +208,7 @@ childIsVisible(const CoreComponentPtr& root, const CoreComponentPtr& child) if (current->getCalculated(kPropertyDisplay).asInt() != kDisplayNormal || current->getCalculated(kPropertyOpacity).getDouble() <= 0) return false; - current = std::dynamic_pointer_cast(current->getParent()); + current = CoreComponent::cast(current->getParent()); } return true; } @@ -234,7 +234,7 @@ FocusFinder::isValidCandidate( } // Check if child is actually in parent's viewport. - auto rootSize = root->getCalculated(kPropertyBounds).getRect().getSize(); + auto rootSize = root->getCalculated(kPropertyBounds).get().getSize(); auto scrollPosition = root->scrollPosition(); auto rootBounds = Rect(scrollPosition.getX(), scrollPosition.getY(), rootSize.getWidth(), rootSize.getHeight()); if (candidateRect.intersect(rootBounds).empty()) { diff --git a/aplcore/src/focus/focusmanager.cpp b/aplcore/src/focus/focusmanager.cpp index f152a03..fe7cc01 100644 --- a/aplcore/src/focus/focusmanager.cpp +++ b/aplcore/src/focus/focusmanager.cpp @@ -143,7 +143,7 @@ FocusManager::clearFocus(bool notifyViewhost, FocusDirection direction, bool for clearFocusedComponent(); focused->getContext()->pushEvent(Event(kEventTypeFocus, nullptr)); // Indicate null focus } else { - auto timers = std::dynamic_pointer_cast(mCore.rootConfig().getTimeManager()); + auto timers = std::static_pointer_cast(mCore.rootConfig().getTimeManager()); Rect bounds; focused->getBoundsInParent(mCore.top(), bounds); auto boundsObject = Object(std::move(bounds)); @@ -214,7 +214,7 @@ FocusManager::focus(FocusDirection direction) return true; } else { - auto origin = generateOrigin(direction, mCore.top()->getCalculated(kPropertyBounds).getRect()); + auto origin = generateOrigin(direction, mCore.top()->getCalculated(kPropertyBounds).get()); return focus(direction, origin); } } @@ -268,7 +268,7 @@ FocusManager::find(FocusDirection direction) CoreComponentPtr FocusManager::find(FocusDirection direction, const Rect& origin) { - return mFinder->findNext(mFocused.lock(), origin, direction, std::dynamic_pointer_cast(mCore.top())); + return mFinder->findNext(mFocused.lock(), origin, direction, CoreComponent::cast(mCore.top())); } CoreComponentPtr @@ -281,7 +281,7 @@ std::map FocusManager::getFocusableAreas() { std::map result; - auto root = std::dynamic_pointer_cast(mCore.top()); + auto root = CoreComponent::cast(mCore.top()); auto focusables = mFinder->getFocusables(root, false); if(root->isFocusable()) { focusables.push_back(root); diff --git a/aplcore/src/focus/focusvisitor.cpp b/aplcore/src/focus/focusvisitor.cpp index 4c3938f..43547da 100644 --- a/aplcore/src/focus/focusvisitor.cpp +++ b/aplcore/src/focus/focusvisitor.cpp @@ -24,7 +24,7 @@ FocusVisitor::visit(const CoreComponent& component) { bool inTheViewport = true; - auto parent = std::static_pointer_cast(component.getParent()); + auto parent = CoreComponent::cast(component.getParent()); // Defer visibility to scrollable itself if (parent && !parent->scrollable()) { inTheViewport = !parent || parent->isDisplayedChild(component); diff --git a/aplcore/src/graphic/graphic.cpp b/aplcore/src/graphic/graphic.cpp index 590ab0a..dfa2b4c 100644 --- a/aplcore/src/graphic/graphic.cpp +++ b/aplcore/src/graphic/graphic.cpp @@ -24,6 +24,7 @@ #include "apl/component/vectorgraphiccomponent.h" #ifdef SCENEGRAPH #include "apl/scenegraph/scenegraphupdates.h" +#include "apl/scenegraph/node.h" #endif // SCENEGRAPH namespace apl { @@ -87,7 +88,7 @@ Graphic::create(const ContextPtr& context, } Graphic::Graphic(const ContextPtr& context, const rapidjson::Value& json, GraphicVersions version) - : UIDObject(Context::createClean(context)), + : UIDObject(Context::createClean(context), UIDObject::UIDObjectType::GRAPHIC), mParameterArray(json), mVersion(version) { @@ -407,7 +408,12 @@ Graphic::updateSceneGraph(sg::SceneGraphUpdates& sceneGraph) element->updateSceneGraph(list); clearDirty(); - return mRootElement->needsRedraw(); + + for (auto node = mRootElement->getSceneGraphNode() ; node ; node = node->next()) + if (node->needsRedraw()) + return true; + + return false; } #endif // SCENEGRAPH diff --git a/aplcore/src/graphic/graphicelement.cpp b/aplcore/src/graphic/graphicelement.cpp index 718ad17..fc8b447 100644 --- a/aplcore/src/graphic/graphicelement.cpp +++ b/aplcore/src/graphic/graphicelement.cpp @@ -31,12 +31,12 @@ namespace apl { Object GraphicElement::asAvgFill(const Context& context, const Object& object) { - if (object.isGraphicPattern() || object.isGradient() || object.isColor()) + if (object.is() || object.is() || object.is()) return object; if (object.isMap()) { auto gradient = asAvgGradient(context, object); - if (gradient.isGradient()) return gradient; + if (gradient.is()) return gradient; } return asColor(context, object); @@ -45,7 +45,7 @@ GraphicElement::asAvgFill(const Context& context, const Object& object) { /**************************************************************************/ GraphicElement::GraphicElement(const GraphicPtr& graphic, const ContextPtr& context) - : UIDObject(context), + : UIDObject(context, UIDObject::UIDObjectType::GRAPHIC_ELEMENT), mGraphic(graphic) { } @@ -261,7 +261,7 @@ GraphicElement::updateTransform(const Context& context, GraphicPropertyKey inKey ? Transform2D() : Transform2D::parse(context.session(), inTransformStr); - auto currentTransform = mValues.get(outKey).getTransform2D(); + const auto& currentTransform = mValues.get(outKey).get(); if (inTransform != currentTransform) { mValues.set(outKey, Object(std::move(inTransform))); @@ -304,7 +304,7 @@ GraphicElement::getSceneGraph(sg::SceneGraphUpdates& sceneGraph) if (!filters.empty()) { // Build up the filter list in reverse order for (int i = 0; i < filters.size(); i++) { - const auto& filter = filters.at(i).getGraphicFilter(); + const auto& filter = filters.at(i).get(); switch (filter.getType()) { case kGraphicFilterTypeDropShadow: mSceneGraphNode = sg::shadowNode( @@ -334,11 +334,6 @@ GraphicElement::updateSceneGraph(sg::ModifiedNodeList& modList) updateSceneGraphInternal(modList, mInnerSceneGraphNode); } -bool -GraphicElement::needsRedraw() const -{ - return mSceneGraphNode ? mSceneGraphNode->needsRedraw() : false; -} #endif // SCENEGRAPH } // namespace apl diff --git a/aplcore/src/graphic/graphicelementcontainer.cpp b/aplcore/src/graphic/graphicelementcontainer.cpp index 1459c61..f8a8612 100644 --- a/aplcore/src/graphic/graphicelementcontainer.cpp +++ b/aplcore/src/graphic/graphicelementcontainer.cpp @@ -112,12 +112,11 @@ GraphicElementContainer::initialize(const GraphicPtr& graphic, const Object& jso sg::NodePtr GraphicElementContainer::buildSceneGraph(sg::SceneGraphUpdates& sceneGraph) { - auto node = sg::generic(); - - for (const auto& m : mChildren) { - auto child = m->getSceneGraph(sceneGraph); + sg::NodePtr node = nullptr; + for (auto it = mChildren.rbegin() ; it != mChildren.rend() ; it++) { + auto child = (*it)->getSceneGraph(sceneGraph); if (child) - node->appendChild(child); + node = child->setNext(node); } return node; diff --git a/aplcore/src/graphic/graphicelementgroup.cpp b/aplcore/src/graphic/graphicelementgroup.cpp index 5dfa651..b6a4f90 100644 --- a/aplcore/src/graphic/graphicelementgroup.cpp +++ b/aplcore/src/graphic/graphicelementgroup.cpp @@ -46,7 +46,7 @@ GraphicElementGroup::propDefSet() const {kGraphicPropertyClipPath, "", asString, kPropInOut | kPropDynamic}, {kGraphicPropertyFilters, Object::EMPTY_ARRAY(), asGraphicFilterArray, kPropInOut }, {kGraphicPropertyOpacity, 1.0, asOpacity, kPropInOut | kPropDynamic}, - {kGraphicPropertyTransform, Object::IDENTITY_2D(), nullptr, kPropOut}, + {kGraphicPropertyTransform, Transform2D(), nullptr, kPropOut}, {kGraphicPropertyTransformAssigned, "", asString, kPropIn | kPropDynamic, fixTransform}, {kGraphicPropertyRotation, 0, asNumber, kPropIn | kPropDynamic, fixTransform}, {kGraphicPropertyPivotX, 0, asNumber, kPropIn | kPropDynamic, fixTransform}, @@ -104,7 +104,7 @@ GraphicElementGroup::updateTransform(const Context& context, bool useDirtyFlag) outTransform *= Transform2D::translate(-pivotX, -pivotY); } - if (outTransform != mValues.get(kGraphicPropertyTransform).getTransform2D()) { + if (outTransform != mValues.get(kGraphicPropertyTransform).get()) { // TODO: We do not explicitly update "single property" values as going to deprecate them to kPropIn-only // after viewhosts act on that. mValues.set(kGraphicPropertyTransform, Object(std::move(outTransform))); @@ -119,14 +119,15 @@ GraphicElementGroup::updateTransform(const Context& context, bool useDirtyFlag) sg::NodePtr GraphicElementGroup::buildSceneGraph(sg::SceneGraphUpdates& sceneGraph) { - auto clip = sg::clip(sg::path(getValue(kGraphicPropertyClipPath).asString()), nullptr); - - for (const auto& m : mChildren) { - auto child = m->getSceneGraph(sceneGraph); + sg::NodePtr node = nullptr; + for (auto it = mChildren.rbegin() ; it != mChildren.rend() ; it++) { + auto child = (*it)->getSceneGraph(sceneGraph); if (child) - clip->appendChild(child); + node = child->setNext(node); } + auto clip = sg::clip(sg::path(getValue(kGraphicPropertyClipPath).asString()), node); + return sg::opacity( getValue(kGraphicPropertyOpacity), sg::transform( @@ -150,7 +151,7 @@ GraphicElementGroup::updateSceneGraphInternal(sg::ModifiedNodeList& modList, con auto* transform = sg::TransformNode::cast(opacity->child()); if (transformChanged && - transform->setTransform(getValue(kGraphicPropertyTransform).getTransform2D())) + transform->setTransform(getValue(kGraphicPropertyTransform).get())) modList.contentChanged(transform); auto* clip = sg::ClipNode::cast(transform->child()); diff --git a/aplcore/src/graphic/graphicelementpath.cpp b/aplcore/src/graphic/graphicelementpath.cpp index 67b80f7..f26563e 100644 --- a/aplcore/src/graphic/graphicelementpath.cpp +++ b/aplcore/src/graphic/graphicelementpath.cpp @@ -45,7 +45,7 @@ GraphicElementPath::propDefSet() const .add({ {kGraphicPropertyFill, Color(), asAvgFill, kPropInOut | kPropDynamic | kPropEvaluated}, {kGraphicPropertyFillOpacity, 1.0, asOpacity, kPropInOut | kPropDynamic}, - {kGraphicPropertyFillTransform, Object::IDENTITY_2D(), nullptr, kPropOut}, + {kGraphicPropertyFillTransform, Transform2D(), nullptr, kPropOut}, {kGraphicPropertyFillTransformAssigned, "", asString, kPropIn | kPropDynamic, fixFillTransform}, {kGraphicPropertyFilters, Object::EMPTY_ARRAY(), asGraphicFilterArray, kPropInOut}, {kGraphicPropertyPathData, "", asString, kPropInOut | kPropRequired | kPropDynamic}, @@ -57,7 +57,7 @@ GraphicElementPath::propDefSet() const {kGraphicPropertyStrokeLineJoin, kGraphicLineJoinMiter, sGraphicLineJoinBimap, kPropInOut | kPropDynamic}, {kGraphicPropertyStrokeMiterLimit, 4, asNumber, kPropInOut | kPropDynamic}, {kGraphicPropertyStrokeOpacity, 1.0, asOpacity, kPropInOut | kPropDynamic}, - {kGraphicPropertyStrokeTransform, Object::IDENTITY_2D(), nullptr, kPropOut}, + {kGraphicPropertyStrokeTransform, Transform2D(), nullptr, kPropOut}, {kGraphicPropertyStrokeTransformAssigned, "", asString, kPropIn | kPropDynamic, fixStrokeTransform}, {kGraphicPropertyStrokeWidth, 1.0, asNonNegativeNumber, kPropInOut | kPropDynamic} }); @@ -85,11 +85,11 @@ GraphicElementPath::buildSceneGraph(sg::SceneGraphUpdates& sceneGraph) auto path = sg::path(getValue(kGraphicPropertyPathData).asString()); auto fill = sg::fill(sg::paint(getValue(kGraphicPropertyFill), getValue(kGraphicPropertyFillOpacity).asFloat(), - getValue(kGraphicPropertyFillTransform).getTransform2D())); + getValue(kGraphicPropertyFillTransform).get())); auto stroke = sg::stroke(sg::paint(getValue(kGraphicPropertyStroke), getValue(kGraphicPropertyStrokeOpacity).asFloat(), - getValue(kGraphicPropertyStrokeTransform).getTransform2D())) + getValue(kGraphicPropertyStrokeTransform).get())) .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()) .miterLimit(getValue(kGraphicPropertyStrokeMiterLimit).asFloat()) .pathLength(getValue(kGraphicPropertyPathLength).asFloat()) @@ -100,8 +100,31 @@ GraphicElementPath::buildSceneGraph(sg::SceneGraphUpdates& sceneGraph) .dashes(getValue(kGraphicPropertyStrokeDashArray)) .get(); - fill->nextSibling = stroke; - return sg::draw(path, fill); + // Return nothing if there is no path and the path never mutates + const auto pathMutates = hasUpstream(kGraphicPropertyPathData); + if (path->empty() && !pathMutates) + return nullptr; + + // Include the fill operation if it is visible or if it can change to be visible + const auto includeFill = fill->visible() || + hasUpstream(kGraphicPropertyFill) || + hasUpstream(kGraphicPropertyFillOpacity); + + // Include the stroke operation if it is visible or if it can change to be visible + const auto includeStroke = stroke->visible() || + hasUpstream(kGraphicPropertyStroke) || + hasUpstream(kGraphicPropertyStrokeOpacity) || + hasUpstream(kGraphicPropertyStrokeWidth); + + // Assemble the operations list in reverse order so that fill occurs before stroke. + auto op = includeStroke ? stroke : nullptr; + if (includeFill) { + fill->nextSibling = op; + op = fill; + } + + // If there are no drawing operations, return a null node. + return op ? sg::draw(path, op) : nullptr; } void @@ -127,54 +150,59 @@ GraphicElementPath::updateSceneGraphInternal(sg::ModifiedNodeList& modList, cons if (pathChanged && draw->setPath(sg::path(getValue(kGraphicPropertyPathData).asString()))) modList.contentChanged(draw); - auto* fill = draw->getOp().get(); - if (fillChanged) { + auto op = draw->getOp(); + if (sg::FillPathOp::is_type(op) && fillChanged) { if (isDirty(kGraphicPropertyFill)) { // If the fill changes, we create a new paint object. // TODO: Shouldn't allocate memory here if the paint hasn't changed type - fill->paint = sg::paint(getValue(kGraphicPropertyFill), - getValue(kGraphicPropertyFillOpacity).asFloat(), - getValue(kGraphicPropertyFillTransform).getTransform2D()); + op->paint = sg::paint(getValue(kGraphicPropertyFill), + getValue(kGraphicPropertyFillOpacity).asFloat(), + getValue(kGraphicPropertyFillTransform).get()); } else { - fill->paint->setOpacity(getValue(kGraphicPropertyFillOpacity).asFloat()); - fill->paint->setTransform(getValue(kGraphicPropertyFillTransform).getTransform2D()); + op->paint->setOpacity(getValue(kGraphicPropertyFillOpacity).asFloat()); + op->paint->setTransform(getValue(kGraphicPropertyFillTransform).get()); } // TODO: Do a fine-grained check to see if the fill actually changed modList.contentChanged(draw); } - auto* stroke = fill->nextSibling.get(); - if (strokePaintChanged) { - if (isDirty(kGraphicPropertyStroke)) { - // If the stroke changes, we create a new paint object. - // TODO: Shouldn't allocate memory here if the paint hasn't changed type - stroke->paint = - sg::paint(getValue(kGraphicPropertyStroke), - getValue(kGraphicPropertyStrokeOpacity).asFloat(), - getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + // Advance to the stroke operation if the fill was defined. + if (op->nextSibling) + op = op->nextSibling; + + if (sg::StrokePathOp::is_type(op)) { + if (strokePaintChanged) { + if (isDirty(kGraphicPropertyStroke)) { + // If the stroke changes, we create a new paint object. + // TODO: Shouldn't allocate memory here if the paint hasn't changed type + op->paint = + sg::paint(getValue(kGraphicPropertyStroke), + getValue(kGraphicPropertyStrokeOpacity).asFloat(), + getValue(kGraphicPropertyStrokeTransform).get()); + } + else { + op->paint->setOpacity(getValue(kGraphicPropertyStrokeOpacity).asFloat()); + op->paint->setTransform( + getValue(kGraphicPropertyStrokeTransform).get()); + } + modList.contentChanged(draw); } - else { - stroke->paint->setOpacity(getValue(kGraphicPropertyStrokeOpacity).asFloat()); - stroke->paint->setTransform( - getValue(kGraphicPropertyStrokeTransform).getTransform2D()); - } - modList.contentChanged(draw); - } - if (strokeChanged) { - sg::stroke(sg::StrokePathOp::castptr(fill->nextSibling)) - .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()) - .miterLimit(getValue(kGraphicPropertyStrokeMiterLimit).asFloat()) - .pathLength(getValue(kGraphicPropertyPathLength).asFloat()) - .dashOffset(getValue(kGraphicPropertyStrokeDashOffset).asFloat()) - .lineCap( - static_cast(getValue(kGraphicPropertyStrokeLineCap).asInt())) - .lineJoin( - static_cast(getValue(kGraphicPropertyStrokeLineJoin).asInt())) - .dashes(getValue(kGraphicPropertyStrokeDashArray)); - modList.contentChanged(draw); + if (strokeChanged) { + sg::stroke(sg::StrokePathOp::castptr(op)) + .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()) + .miterLimit(getValue(kGraphicPropertyStrokeMiterLimit).asFloat()) + .pathLength(getValue(kGraphicPropertyPathLength).asFloat()) + .dashOffset(getValue(kGraphicPropertyStrokeDashOffset).asFloat()) + .lineCap(static_cast( + getValue(kGraphicPropertyStrokeLineCap).asInt())) + .lineJoin(static_cast( + getValue(kGraphicPropertyStrokeLineJoin).asInt())) + .dashes(getValue(kGraphicPropertyStrokeDashArray)); + modList.contentChanged(draw); + } } } } diff --git a/aplcore/src/graphic/graphicelementtext.cpp b/aplcore/src/graphic/graphicelementtext.cpp index 176a4bd..8a7131e 100644 --- a/aplcore/src/graphic/graphicelementtext.cpp +++ b/aplcore/src/graphic/graphicelementtext.cpp @@ -56,7 +56,7 @@ GraphicElementText::propDefSet() const { static auto fixTextTrigger = [](GraphicElement& element) -> void { #ifdef SCENEGRAPH - auto& text = dynamic_cast(element); + auto& text = (GraphicElementText&)element; text.mTextProperties = nullptr; text.mLayout = nullptr; #endif // SCENEGRAPH @@ -64,7 +64,7 @@ GraphicElementText::propDefSet() const static auto fixTextChunkTrigger = [](GraphicElement& element) -> void { #ifdef SCENEGRAPH - auto& text = dynamic_cast(element); + auto& text = (GraphicElementText&)element; text.mTextChunk = nullptr; text.mLayout = nullptr; #endif // SCENEGRAPH @@ -74,7 +74,7 @@ GraphicElementText::propDefSet() const .add({ {kGraphicPropertyFill, Color(Color::BLACK), asAvgFill, kPropInOut | kPropDynamic | kPropEvaluated}, {kGraphicPropertyFillOpacity, 1, asOpacity, kPropInOut | kPropDynamic}, - {kGraphicPropertyFillTransform, Object::IDENTITY_2D(), nullptr, kPropOut}, + {kGraphicPropertyFillTransform, Transform2D(), nullptr, kPropOut}, {kGraphicPropertyFillTransformAssigned, "", asString, kPropIn | kPropDynamic, fixFillTransform}, {kGraphicPropertyFilters, Object::EMPTY_ARRAY(), asGraphicFilterArray, kPropInOut }, {kGraphicPropertyFontFamily, "", asString, kPropInOut | kPropDynamic, fixTextTrigger, defaultFontFamily}, @@ -84,7 +84,7 @@ GraphicElementText::propDefSet() const {kGraphicPropertyLetterSpacing, 0, asNumber, kPropInOut | kPropDynamic, fixTextTrigger}, {kGraphicPropertyStroke, Color(), asAvgFill, kPropInOut | kPropDynamic | kPropEvaluated}, {kGraphicPropertyStrokeOpacity, 1, asOpacity, kPropInOut | kPropDynamic}, - {kGraphicPropertyStrokeTransform, Object::IDENTITY_2D(), nullptr, kPropOut}, + {kGraphicPropertyStrokeTransform, Transform2D(), nullptr, kPropOut}, {kGraphicPropertyStrokeTransformAssigned, "", asString, kPropIn | kPropDynamic, fixStrokeTransform}, {kGraphicPropertyStrokeWidth, 0, asNonNegativeNumber, kPropInOut | kPropDynamic}, {kGraphicPropertyText, "", asFilteredText, kPropInOut | kPropRequired | kPropDynamic, fixTextChunkTrigger}, @@ -118,16 +118,35 @@ GraphicElementText::buildSceneGraph(sg::SceneGraphUpdates& sceneGraph) auto fill = sg::fill(sg::paint(getValue(kGraphicPropertyFill), getValue(kGraphicPropertyFillOpacity).asFloat(), - getValue(kGraphicPropertyFillTransform).getTransform2D())); + getValue(kGraphicPropertyFillTransform).get())); auto stroke = sg::stroke(sg::paint(getValue(kGraphicPropertyStroke), getValue(kGraphicPropertyStrokeOpacity).asFloat(), - getValue(kGraphicPropertyStrokeTransform).getTransform2D())) + getValue(kGraphicPropertyStrokeTransform).get())) .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()) .get(); - fill->nextSibling = stroke; - return sg::transform(getPosition(), sg::text(mLayout, fill)); + + // Include the fill operation if it is visible or if it can change to be visible + const auto includeFill = fill->visible() || + hasUpstream(kGraphicPropertyFill) || + hasUpstream(kGraphicPropertyFillOpacity); + + // Include the stroke operation if it is visible or if it can change to be visible + const auto includeStroke = stroke->visible() || + hasUpstream(kGraphicPropertyStroke) || + hasUpstream(kGraphicPropertyStrokeOpacity) || + hasUpstream(kGraphicPropertyStrokeWidth); + + // Assemble the operations list in reverse order so that fill occurs before stroke. + auto op = includeStroke ? stroke : nullptr; + if (includeFill) { + fill->nextSibling = op; + op = fill; + } + + // If there are no drawing operations, return a null node + return op ? sg::transform(getPosition(), sg::text(mLayout, op)) : nullptr; } /* @@ -190,41 +209,46 @@ GraphicElementText::updateSceneGraphInternal(sg::ModifiedNodeList& modList, cons if (textChanged) text->setTextLayout(mLayout); - auto* fill = text->getOp().get(); - if (fillChanged) { + auto op = text->getOp(); + if (sg::FillPathOp::is_type(op) && fillChanged) { if (isDirty(kGraphicPropertyFill)) { // If the fill changes, we create a new paint object. // TODO: Shouldn't allocate memory here if the paint hasn't changed type - fill->paint = sg::paint(getValue(kGraphicPropertyFill), - getValue(kGraphicPropertyFillOpacity).asFloat(), - getValue(kGraphicPropertyFillTransform).getTransform2D()); + op->paint = sg::paint(getValue(kGraphicPropertyFill), + getValue(kGraphicPropertyFillOpacity).asFloat(), + getValue(kGraphicPropertyFillTransform).get()); } else { - fill->paint->setOpacity(getValue(kGraphicPropertyFillOpacity).asFloat()); - fill->paint->setTransform(getValue(kGraphicPropertyFillTransform).getTransform2D()); + op->paint->setOpacity(getValue(kGraphicPropertyFillOpacity).asFloat()); + op->paint->setTransform(getValue(kGraphicPropertyFillTransform).get()); } } - auto* stroke = fill->nextSibling.get(); - if (strokePaintChanged) { - if (isDirty(kGraphicPropertyStroke)) { - // If the stroke changes, we create a new paint object. - // TODO: Shouldn't allocate memory here if the paint hasn't changed type - stroke->paint = - sg::paint(getValue(kGraphicPropertyStroke), - getValue(kGraphicPropertyStrokeOpacity).asFloat(), - getValue(kGraphicPropertyStrokeTransform).getTransform2D()); - } - else { - stroke->paint->setOpacity(getValue(kGraphicPropertyStrokeOpacity).asFloat()); - stroke->paint->setTransform( - getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + // Advance to the stroke operation if the fill was defined + if (op->nextSibling) + op = op->nextSibling; + + if (sg::StrokePathOp::is_type(op)) { + if (strokePaintChanged) { + if (isDirty(kGraphicPropertyStroke)) { + // If the stroke changes, we create a new paint object. + // TODO: Shouldn't allocate memory here if the paint hasn't changed type + op->paint = + sg::paint(getValue(kGraphicPropertyStroke), + getValue(kGraphicPropertyStrokeOpacity).asFloat(), + getValue(kGraphicPropertyStrokeTransform).get()); + } + else { + op->paint->setOpacity(getValue(kGraphicPropertyStrokeOpacity).asFloat()); + op->paint->setTransform( + getValue(kGraphicPropertyStrokeTransform).get()); + } } - } - if (strokeWidthChanged) - sg::stroke(sg::StrokePathOp::castptr(fill->nextSibling)) - .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()); + if (strokeWidthChanged) + sg::stroke(sg::StrokePathOp::castptr(op)) + .strokeWidth(getValue(kGraphicPropertyStrokeWidth).asFloat()); + } modList.contentChanged(text); } @@ -241,8 +265,8 @@ GraphicElementText::ensureSGTextLayout() if (!context) return; - auto measure = std::dynamic_pointer_cast(mContext->measure()); - assert(measure); + assert(mContext->measure()->sceneGraphCompatible()); + auto measure = std::static_pointer_cast(mContext->measure()); ensureTextProperties(); mLayout = measure->layout(mTextChunk, mTextProperties, INFINITY, MeasureMode::Undefined, diff --git a/aplcore/src/graphic/graphicfilter.cpp b/aplcore/src/graphic/graphicfilter.cpp index 5832930..a903f9e 100644 --- a/aplcore/src/graphic/graphicfilter.cpp +++ b/aplcore/src/graphic/graphicfilter.cpp @@ -77,7 +77,7 @@ calculateNormal(const GraphicFilterPropDef& def, Object GraphicFilter::create(const Context& context, const Object& object) { - if (object.isGraphicFilter()) + if (object.is()) return object; if (!object.isMap()) diff --git a/aplcore/src/graphic/graphicpattern.cpp b/aplcore/src/graphic/graphicpattern.cpp index 00bf642..02ac4a2 100644 --- a/aplcore/src/graphic/graphicpattern.cpp +++ b/aplcore/src/graphic/graphicpattern.cpp @@ -17,9 +17,7 @@ #include "apl/engine/arrayify.h" #include "apl/graphic/graphicpattern.h" #include "apl/graphic/graphicbuilder.h" -#include "apl/primitives/object.h" #include "apl/utils/session.h" -#include "apl/utils/stringfunctions.h" namespace apl { @@ -27,7 +25,7 @@ GraphicPattern::GraphicPattern(const ContextPtr& context, const std::string& description, double height, double width, std::vector&& items) - : UIDObject(context), + : UIDObject(context, UIDObject::UIDObjectType::GRAPHIC_PATTERN), mDescription(description), mHeight(height), mWidth(width), @@ -37,7 +35,7 @@ GraphicPattern::GraphicPattern(const ContextPtr& context, Object GraphicPattern::create(const Context& context, const Object& object) { - if (object.isGraphicPattern()) + if (object.is()) return object; if (!object.isMap()) @@ -71,7 +69,7 @@ GraphicPattern::create(const Context& context, const Object& object) std::string GraphicPattern::toDebugString() const { - std::string result = "GraphicPattern< id=" + getUniqueId() + + std::string result = "GraphicPatterngetType(); + LOG(LogLevel::kError).session(context) << "Unexpected data type for live object key='" << key << "': " << static_cast(data->getType()); return nullptr; } - context->putSystemWriteable(key, Object(element)); - context->dataManager().add(element); - return element; + context->putSystemWriteable(key, element); + context->dataManager().add(element.getLiveDataObject()); + return element.getLiveDataObject(); } void diff --git a/aplcore/src/livedata/livemapobject.cpp b/aplcore/src/livedata/livemapobject.cpp index 7567cf0..51d53be 100644 --- a/aplcore/src/livedata/livemapobject.cpp +++ b/aplcore/src/livedata/livemapobject.cpp @@ -120,4 +120,10 @@ LiveMapObject::handleMapMessage(const LiveMapChange& change) markDirty(); } +std::shared_ptr +LiveMapObject::ObjectType::getLiveDataObject(const Object::DataHolder& dataHolder) const +{ + return std::static_pointer_cast(dataHolder.data); +} + } // namespace apl \ No newline at end of file diff --git a/aplcore/src/media/coremediamanager.cpp b/aplcore/src/media/coremediamanager.cpp index 056a1f5..20df98d 100644 --- a/aplcore/src/media/coremediamanager.cpp +++ b/aplcore/src/media/coremediamanager.cpp @@ -169,7 +169,7 @@ CoreMediaManager::mediaLoadComplete( return; } - auto mediaObject = std::dynamic_pointer_cast(ptr); + auto mediaObject = std::static_pointer_cast(ptr); if (!mediaObject) { LOG(LogLevel::kError) << "Unrecognized media object type"; return; diff --git a/aplcore/src/media/mediautils.cpp b/aplcore/src/media/mediautils.cpp index bd02e86..8d6b0ae 100644 --- a/aplcore/src/media/mediautils.cpp +++ b/aplcore/src/media/mediautils.cpp @@ -23,7 +23,7 @@ mediaSourcesToTracks(const Object& mediaSources) { std::vector result; for (auto i = 0 ; i < mediaSources.size() ; i++) { - const auto& ms = mediaSources.at(i).getMediaSource(); + const auto& ms = mediaSources.at(i).get(); result.emplace_back(MediaTrack{ ms.getUrl(), // URL ms.getHeaders(), // HTTP HeaderArray diff --git a/aplcore/src/primitives/CMakeLists.txt b/aplcore/src/primitives/CMakeLists.txt index aef6e40..0b1849b 100644 --- a/aplcore/src/primitives/CMakeLists.txt +++ b/aplcore/src/primitives/CMakeLists.txt @@ -33,7 +33,7 @@ target_sources_local(apl transform.cpp transform2d.cpp object.cpp - objectdata.cpp + objecttype.cpp unicode.cpp urlrequest.cpp ) diff --git a/aplcore/src/primitives/accessibilityaction.cpp b/aplcore/src/primitives/accessibilityaction.cpp index dda6f2c..b8d5835 100644 --- a/aplcore/src/primitives/accessibilityaction.cpp +++ b/aplcore/src/primitives/accessibilityaction.cpp @@ -75,8 +75,8 @@ AccessibilityAction::create(const CoreComponentPtr& component, const Object& obj { auto context = component->getContext(); - if (object.isAccessibilityAction()) - return object.getAccessibilityAction(); + if (object.is()) + return object.get(); if (!object.isMap()) { CONSOLE(context) << "Invalid accessibility action"; diff --git a/aplcore/src/primitives/color.cpp b/aplcore/src/primitives/color.cpp index 53204b0..45db163 100644 --- a/aplcore/src/primitives/color.cpp +++ b/aplcore/src/primitives/color.cpp @@ -27,15 +27,14 @@ namespace pegtl = tao::TAO_PEGTL_NAMESPACE; * @return The ARGB value. */ uint32_t Color::parse(const SessionPtr& session, const char *color) { - try { - colorgrammar::color_state state; - pegtl::string_input<> in(color, ""); - pegtl::parse(in, state); + colorgrammar::color_state state; + pegtl::string_input<> in(color, ""); + + if (!pegtl::parse(in, state) || state.failed) { + CONSOLE(session) << "Error parsing color '" << color << "', " << state.what(); + } else { return state.getColor(); } - catch (pegtl::parse_error e) { - CONSOLE(session) << "Error parsing color '" << color << "', " << e.what(); - } return TRANSPARENT; } diff --git a/aplcore/src/primitives/dimension.cpp b/aplcore/src/primitives/dimension.cpp index f1d395c..56f0e88 100644 --- a/aplcore/src/primitives/dimension.cpp +++ b/aplcore/src/primitives/dimension.cpp @@ -13,136 +13,133 @@ * permissions and limitations under the License. */ -#include #include #include "apl/primitives/dimension.h" #include "apl/engine/context.h" -#include "apl/utils/stringfunctions.h" namespace apl { - namespace pegtl = tao::TAO_PEGTL_NAMESPACE; - using namespace pegtl; - - // TODO: Currently if the grammar doesn't match, we set the result to 0. - - /** - * \cond ShowDimensionGrammar - */ - struct d_ws : star< space > {}; - struct d_num : sor< seq< opt>, plus< digit >, opt< one<'.'>, star< digit > > >, - seq< opt>, one<'.'>, plus< digit > > > {}; - struct d_vh : string<'v', 'h'> {}; - struct d_vw : string<'v', 'w'> {}; - struct d_px : string<'p', 'x'> {}; - struct d_dp : string<'d', 'p'> {}; - struct d_auto : string<'a', 'u', 't', 'o'> {}; - struct d_percent : one<'%'> {}; - struct d_op : sor< d_px, d_dp, d_vh, d_vw, d_percent > {}; - struct d_value : seq< d_num, opt< d_ws, d_op > > {}; - struct d_expr : sor< d_auto, d_value >{}; - struct d_grammar : seq< d_ws, d_expr, d_ws, eof > {}; - - template - struct d_action : nothing< Rule > - { - }; - - template<> struct d_action< d_num > - { - template< typename Input > - static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { - value = sutil::stod(in.string()); - } - }; +namespace pegtl = tao::TAO_PEGTL_NAMESPACE; +using namespace pegtl; - template<> struct d_action< d_op > - { - template< typename Input > - static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { - unit = in.string(); - } - }; +// TODO: Currently if the grammar doesn't match, we set the result to 0. - template<> struct d_action< d_auto > - { - template< typename Input > - static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { - isAuto = true; - } - }; - /** - * \endcond - */ - - /** - * Construct a dimension from a string. - * @param context The defining context. Used to retrieve screen metrics. - * @param value The dimension definition. - * @param preferRelative If true, pure numeric values should be interpreted as relative values - */ - Dimension::Dimension(const Context& context, const std::string& value, bool preferRelative) - : mType(DimensionType::Absolute), - mValue(0) - { - std::string unit; - bool isAuto = false; - pegtl::string_input<> in(value, ""); - - // If you fail to parse it, return 0 - if (!pegtl::parse(in, unit, isAuto, mValue)) { - mValue = 0; - return; - } +/** + * \cond ShowDimensionGrammar + */ +struct d_ws : star< space > {}; +struct d_num : sor< seq< opt>, plus< digit >, opt< one<'.'>, star< digit > > >, + seq< opt>, one<'.'>, plus< digit > > > {}; +struct d_vh : string<'v', 'h'> {}; +struct d_vw : string<'v', 'w'> {}; +struct d_px : string<'p', 'x'> {}; +struct d_dp : string<'d', 'p'> {}; +struct d_auto : string<'a', 'u', 't', 'o'> {}; +struct d_percent : one<'%'> {}; +struct d_op : sor< d_px, d_dp, d_vh, d_vw, d_percent > {}; +struct d_value : seq< d_num, opt< d_ws, d_op > > {}; +struct d_expr : sor< d_auto, d_value >{}; +struct d_grammar : seq< d_ws, d_expr, d_ws, eof > {}; + +template +struct d_action : nothing< Rule > +{ +}; + +template<> struct d_action< d_num > +{ + template< typename Input > + static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { + value = sutil::stod(in.string()); + } +}; - // We were set to auto - if (isAuto) { - mType = DimensionType::Auto; - } - else if (unit.compare("vh") == 0) { - mValue = context.vhToDp(mValue); - } - else if (unit.compare("vw") == 0) { - mValue = context.vwToDp(mValue); - } - else if (unit.compare("px") == 0) { - mValue = context.pxToDp(mValue); - } - else if (unit.compare("%") == 0) { - mType = DimensionType::Relative; - } - else if (unit.compare("") == 0) { - if (preferRelative) { - mValue *= 100; - mType = DimensionType::Relative; - } - } +template<> struct d_action< d_op > +{ + template< typename Input > + static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { + unit = in.string(); + } +}; - // Anything else is absolute - }; - - /** - * Write a dimension to the output stream. - * @param os The output stream. - * @param dimension The dimension to show. - * @return The output stream, for chaining. - */ - streamer& operator<<(streamer& os, const Dimension& dimension) - { - switch(dimension.mType) { - case DimensionType::Auto: - os << "auto"; - break; - case DimensionType::Relative: - os << dimension.mValue << "%"; - break; - case DimensionType::Absolute: - os << dimension.mValue << "dp"; - break; +template<> struct d_action< d_auto > +{ + template< typename Input > + static void apply(const Input& in, std::string& unit, bool& isAuto, double& value) { + isAuto = true; + } +}; +/** + * \endcond + */ + +/** + * Construct a dimension from a string. + * @param context The defining context. Used to retrieve screen metrics. + * @param value The dimension definition. + * @param preferRelative If true, pure numeric values should be interpreted as relative values + */ +Dimension::Dimension(const Context& context, const std::string& value, bool preferRelative) + : mType(DimensionType::Absolute), + mValue(0) +{ + std::string unit; + bool isAuto = false; + pegtl::string_input<> in(value, ""); + + // If you fail to parse it, return 0 + if (!pegtl::parse(in, unit, isAuto, mValue)) { + mValue = 0; + return; + } + + // We were set to auto + if (isAuto) { + mType = DimensionType::Auto; + } + else if (unit.compare("vh") == 0) { + mValue = context.vhToDp(mValue); + } + else if (unit.compare("vw") == 0) { + mValue = context.vwToDp(mValue); + } + else if (unit.compare("px") == 0) { + mValue = context.pxToDp(mValue); + } + else if (unit.compare("%") == 0) { + mType = DimensionType::Relative; + } + else if (unit.compare("") == 0) { + if (preferRelative) { + mValue *= 100; + mType = DimensionType::Relative; } - return os; } + // Anything else is absolute +}; + +/** + * Write a dimension to the output stream. + * @param os The output stream. + * @param dimension The dimension to show. + * @return The output stream, for chaining. + */ +streamer& operator<<(streamer& os, const Dimension& dimension) +{ + switch(dimension.mType) { + case DimensionType::Auto: + os << "auto"; + break; + case DimensionType::Relative: + os << dimension.mValue << "%"; + break; + case DimensionType::Absolute: + os << dimension.mValue << "dp"; + break; + } + return os; +} } // namespace apl \ No newline at end of file diff --git a/aplcore/src/primitives/filter.cpp b/aplcore/src/primitives/filter.cpp index cbd1183..a2d9507 100644 --- a/aplcore/src/primitives/filter.cpp +++ b/aplcore/src/primitives/filter.cpp @@ -171,7 +171,7 @@ calculateExtended(const std::string& name, Object Filter::create(const Context& context, const Object& object) { - if (object.isFilter()) + if (object.is()) return object; if (!object.isMap()) diff --git a/aplcore/src/primitives/functions.cpp b/aplcore/src/primitives/functions.cpp index 49e4964..05f437c 100644 --- a/aplcore/src/primitives/functions.cpp +++ b/aplcore/src/primitives/functions.cpp @@ -24,7 +24,6 @@ #include "apl/animation/coreeasing.h" #include "apl/engine/context.h" #include "apl/primitives/functions.h" -#include "apl/primitives/object.h" #include "apl/primitives/rangegenerator.h" #include "apl/primitives/slicegenerator.h" #include "apl/primitives/timefunctions.h" diff --git a/aplcore/src/primitives/gradient.cpp b/aplcore/src/primitives/gradient.cpp index c320de1..c407af6 100644 --- a/aplcore/src/primitives/gradient.cpp +++ b/aplcore/src/primitives/gradient.cpp @@ -17,7 +17,7 @@ #include "apl/engine/arrayify.h" #include "apl/utils/log.h" #include "apl/utils/session.h" -#include "apl/primitives/object.h" +#include "apl/primitives/objectdata.h" #include "apl/primitives/gradient.h" namespace apl { @@ -91,7 +91,7 @@ inline void convertAngleToCoordinates(double angle, double& x1, double& x2, doub Object Gradient::create(const Context& context, const Object& object, bool avg) { - if (object.isGradient()) + if (object.is()) return object; if (!object.isMap()) diff --git a/aplcore/src/primitives/mediasource.cpp b/aplcore/src/primitives/mediasource.cpp index e78b0d7..87fa01e 100644 --- a/aplcore/src/primitives/mediasource.cpp +++ b/aplcore/src/primitives/mediasource.cpp @@ -17,9 +17,9 @@ #include "apl/engine/arrayify.h" #include "apl/content/rootconfig.h" #include "apl/primitives/mediasource.h" +#include "apl/primitives/objectdata.h" #include "apl/utils/log.h" #include "apl/utils/session.h" -#include "apl/utils/stringfunctions.h" namespace apl { @@ -39,7 +39,7 @@ MediaSource::MediaSource(URLRequest urlRequest, Object MediaSource::create(const Context& context, const Object& object) { - if (object.isMediaSource()) + if (object.is()) return object; if (object.isString()) { @@ -48,7 +48,7 @@ MediaSource::create(const Context& context, const Object& object) CONSOLE(context) << "Empty string for media source"; return Object::NULL_OBJECT(); } - return Object(MediaSource(URLRequest::create(context, object).getURLRequest(), + return Object(MediaSource(URLRequest::create(context, object).get(), "", 0, 0, @@ -71,7 +71,7 @@ MediaSource::create(const Context& context, const Object& object) int offset = propertyAsInt(context, object, "offset", 0); auto entities = Object(arrayifyProperty(context, object, "entities", "entity")); - return Object(MediaSource(URLRequest::create(context, object).getURLRequest(), + return Object(MediaSource(URLRequest::create(context, object).get(), description, duration, repeatCount, diff --git a/aplcore/src/primitives/object.cpp b/aplcore/src/primitives/object.cpp index 4e63eba..6e4d530 100644 --- a/aplcore/src/primitives/object.cpp +++ b/aplcore/src/primitives/object.cpp @@ -13,46 +13,25 @@ * permissions and limitations under the License. */ -#include "rapidjson/stringbuffer.h" -#include "rapidjson/writer.h" +#include "apl/primitives/object.h" + +#include -#include "apl/animation/easing.h" -#include "apl/component/componenteventwrapper.h" #include "apl/datagrammar/boundsymbol.h" #include "apl/datagrammar/bytecode.h" -#include "apl/engine/contextwrapper.h" -#include "apl/graphic/graphic.h" -#include "apl/graphic/graphicfilter.h" -#include "apl/graphic/graphicpattern.h" -#include "apl/livedata/livedataobject.h" -#include "apl/primitives/accessibilityaction.h" -#include "apl/primitives/filter.h" +#include "apl/engine/context.h" +#include "apl/primitives/color.h" +#include "apl/primitives/dimension.h" #include "apl/primitives/functions.h" -#include "apl/primitives/gradient.h" -#include "apl/primitives/mediasource.h" -#include "apl/primitives/object.h" -#include "apl/primitives/objectdata.h" -#include "apl/primitives/range.h" +#include "apl/primitives/objecttype.h" #include "apl/primitives/rangegenerator.h" #include "apl/primitives/slicegenerator.h" -#include "apl/primitives/transform.h" -#include "apl/primitives/urlrequest.h" #include "apl/utils/log.h" -#include "apl/utils/stringfunctions.h" namespace apl { const bool OBJECT_DEBUG = false; -/****************************************************************************/ - -template -std::shared_ptr create(T&& data) { - return std::make_shared(std::forward(data)); -} - -/****************************************************************************/ - const Object& Object::TRUE_OBJECT() { static Object *answer = new Object(true); return *answer; @@ -72,10 +51,6 @@ Object Object::NAN_OBJECT() { return Object(std::numeric_limits::quiet_NaN()); } -Object Object::AUTO_OBJECT() { - return Object(Dimension()); -} - Object Object::EMPTY_ARRAY() { return Object(std::vector{}); } @@ -92,43 +67,18 @@ Object Object::EMPTY_MUTABLE_MAP() { return Object(std::make_shared(), true); } -Object Object::ZERO_ABS_DIMEN() { - return Object(Dimension(DimensionType::Absolute, 0)); -} - -Object Object::EMPTY_RECT() { - return Object(Rect(0,0,0,0)); -} - -Object Object::EMPTY_RADII() { - return Object(Radii()); -} - -Object Object::IDENTITY_2D() { - return Object(Transform2D()); -} - -Object Object::LINEAR_EASING() { - return Object(Easing::linear()); -} - /****************************************************************************/ Object::Object(const Object& object) noexcept : mType(object.mType) { - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + case StorageType::kStorageTypeValue: mU.value = object.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: new(&mU.string) std::string(object.mU.string); break; default: @@ -140,18 +90,13 @@ Object::Object(const Object& object) noexcept Object::Object(Object&& object) noexcept : mType(object.mType) { - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + case StorageType::kStorageTypeValue: mU.value = object.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: new(&mU.string) std::string(std::move(object.mU.string)); break; default: @@ -166,18 +111,13 @@ Object& Object::operator=(const Object& rhs) noexcept return *this; if (mType == rhs.mType) { - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + case StorageType::kStorageTypeValue: mU.value = rhs.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: mU.string = rhs.mU.string; break; default: @@ -187,16 +127,11 @@ Object& Object::operator=(const Object& rhs) noexcept } else { // Delete the old item - switch (mType) { - case kNullType: - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: // FALL_THROUGH + case StorageType::kStorageTypeValue: break; - case kStringType: + case StorageType::kStorageTypeString: mU.string.~basic_string(); break; default: @@ -206,18 +141,13 @@ Object& Object::operator=(const Object& rhs) noexcept // Construct the new mType = rhs.mType; - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + case StorageType::kStorageTypeValue: mU.value = rhs.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: new(&mU.string) std::string(rhs.mU.string); break; default: @@ -232,18 +162,13 @@ Object& Object::operator=(const Object& rhs) noexcept Object& Object::operator=(Object&& rhs) noexcept { if (mType == rhs.mType) { - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: - mU.value = std::move(rhs.mU.value); + case StorageType::kStorageTypeValue: + mU.value = rhs.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: mU.string = std::move(rhs.mU.string); break; default: @@ -253,16 +178,11 @@ Object& Object::operator=(Object&& rhs) noexcept } else { // Delete the old item - switch (mType) { - case kNullType: - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: // FALL_THROUGH + case StorageType::kStorageTypeValue: break; - case kStringType: + case StorageType::kStorageTypeString: mU.string.~basic_string(); break; default: @@ -272,18 +192,13 @@ Object& Object::operator=(Object&& rhs) noexcept // Construct the new mType = rhs.mType; - switch (mType) { - case kNullType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: break; - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: - mU.value = std::move(rhs.mU.value); + case StorageType::kStorageTypeValue: + mU.value = rhs.mU.value; break; - case kStringType: + case StorageType::kStorageTypeString: new(&mU.string) std::string(std::move(rhs.mU.string)); break; default: @@ -300,16 +215,11 @@ Object& Object::operator=(Object&& rhs) noexcept Object::~Object() { LOG_IF(OBJECT_DEBUG) << " --- Destroying " << *this; - switch (mType) { - case kNullType: - case kBoolType: - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kAutoDimensionType: - case kColorType: + switch(mType->storageType()) { + case StorageType::kStorageTypeEmpty: // FALL_THROUGH + case StorageType::kStorageTypeValue: break; - case kStringType: + case StorageType::kStorageTypeString: mU.string.~basic_string(); break; default: @@ -319,199 +229,152 @@ Object::~Object() } Object::Object() - : mType(kNullType) + : mType(Null::ObjectType::instance()) { LOG_IF(OBJECT_DEBUG) << "Object null constructor" << this; } -Object::Object(ObjectType type) - : mType(type) -{ - LOG_IF(OBJECT_DEBUG) << "Object type constructor" << this; -} - Object::Object(bool b) - : mType(kBoolType), + : mType(Boolean::ObjectType::instance()), mU(b ? 1.0 : 0.0) { LOG_IF(OBJECT_DEBUG) << "Object bool constructor: " << b << " this=" << this; } Object::Object(int i) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(i) {} Object::Object(uint32_t u) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(u) {} Object::Object(unsigned long l) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(static_cast(l)) {} Object::Object(long l) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(static_cast(l)) {} Object::Object(unsigned long long l) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(static_cast(l)) {} Object::Object(long long l) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(static_cast(l)) {} Object::Object(double d) - : mType(kNumberType), + : mType(Number::ObjectType::instance()), mU(d) {} Object::Object(const char *s) - : mType(kStringType), + : mType(String::ObjectType::instance()), mU(s) {} Object::Object(const std::string& s) - : mType(kStringType), + : mType(String::ObjectType::instance()), mU(s) {} Object::Object(const ObjectMapPtr& m, bool isMutable) - : mType(kMapType), + : mType(Map::ObjectType::instance()), mU(std::static_pointer_cast(std::make_shared(m, isMutable))) {} Object::Object(const ObjectArrayPtr& v, bool isMutable) - : mType(kArrayType), + : mType(Array::ObjectType::instance()), mU(std::static_pointer_cast(std::make_shared(v, isMutable))) {} Object::Object(ObjectArray&& v, bool isMutable) - : mType(kArrayType), - mU(std::static_pointer_cast(std::make_shared(std::move(v),isMutable))) + : mType(Array::ObjectType::instance()), + mU(std::static_pointer_cast(std::make_shared(std::move(v), isMutable))) {} -Object::Object(const std::shared_ptr& n) - : mType(kByteCodeType), - mU(std::static_pointer_cast(n)) -{ - LOG_IF(OBJECT_DEBUG) << "Object constructor compiled byte code: " << this; -} - -Object::Object(const std::shared_ptr& bs) - : mType(kBoundSymbolType), - mU(std::static_pointer_cast(bs)) -{ - LOG_IF(OBJECT_DEBUG) << "Object constructor bound symbol: " << this; -} - -Object::Object(const std::shared_ptr& d) - : mType(d->getType()), - mU(d) -{ - LOG_IF(OBJECT_DEBUG) << "Object live data: " << this; -} - -Object::Object(const std::shared_ptr& d) - : mType(kComponentType), - mU(d) -{ - LOG_IF(OBJECT_DEBUG) << "Object component event data: " << this; -} - -Object::Object(const std::shared_ptr& c) - : mType(kContextType), - mU(c) -{ - LOG_IF(OBJECT_DEBUG) << "Object context: " << this; -} - -Object::Object(const rapidjson::Value& value) +Object::Object(const rapidjson::Value& value) : mType(Null::ObjectType::instance()) { LOG_IF(OBJECT_DEBUG) << "Object constructor value: " << this; switch(value.GetType()) { case rapidjson::kNullType: - mType = kNullType; + mType = Null::ObjectType::instance(); break; case rapidjson::kFalseType: - mType = kBoolType; + mType = Boolean::ObjectType::instance(); mU.value = 0; break; case rapidjson::kTrueType: - mType = kBoolType; + mType = Boolean::ObjectType::instance(); mU.value = 1; break; case rapidjson::kNumberType: - mType = kNumberType; + mType = Number::ObjectType::instance(); mU.value = value.GetDouble(); break; case rapidjson::kStringType: - mType = kStringType; + mType = String::ObjectType::instance(); new(&mU.string) std::string(value.GetString()); // TODO: Should we keep the string in place? break; case rapidjson::kObjectType: - mType = kMapType; + mType = Map::ObjectType::instance(); new(&mU.data) std::shared_ptr(std::make_shared(&value)); break; case rapidjson::kArrayType: - mType = kArrayType; + mType = Array::ObjectType::instance(); new(&mU.data) std::shared_ptr(std::make_shared(&value)); break; } } -Object::Object(rapidjson::Document&& value) +Object::Object(rapidjson::Document&& value) : mType(Null::ObjectType::instance()) { if (OBJECT_DEBUG) LOG(LogLevel::kDebug) << "Object constructor value: " << this; switch(value.GetType()) { case rapidjson::kNullType: - mType = kNullType; + mType = Null::ObjectType::instance(); break; case rapidjson::kFalseType: - mType = kBoolType; + mType = Boolean::ObjectType::instance(); mU.value = 0; break; case rapidjson::kTrueType: - mType = kBoolType; + mType = Boolean::ObjectType::instance(); mU.value = 1; break; case rapidjson::kNumberType: - mType = kNumberType; + mType = Number::ObjectType::instance(); mU.value = value.GetDouble(); break; case rapidjson::kStringType: - mType = kStringType; + mType = String::ObjectType::instance(); mU.string = value.GetString(); // TODO: Should we keep the string in place? break; case rapidjson::kObjectType: - mType = kMapType; + mType = Map::ObjectType::instance(); new(&mU.data) std::shared_ptr(std::make_shared(std::move(value))); break; case rapidjson::kArrayType: - mType = kArrayType; + mType = Array::ObjectType::instance(); new(&mU.data) std::shared_ptr(std::make_shared(std::move(value))); break; } } -Object::Object(const std::shared_ptr& f) - : mType(kFunctionType), - mU(std::static_pointer_cast(f)) -{ - LOG_IF(OBJECT_DEBUG) << "User Function constructor"; -} - Object::Object(const Dimension& d) - : mType(d.isAuto() ? kAutoDimensionType : - (d.isRelative() ? kRelativeDimensionType : kAbsoluteDimensionType)), + : mType(d.isAuto() ? Dimension::AutoDimensionObjectType::instance() : + (d.isRelative() ? Dimension::RelativeDimensionObjectType::instance() + : Dimension::AbsoluteDimensionObjectType::instance())), mU(d.getValue()) { if (OBJECT_DEBUG) @@ -519,782 +382,117 @@ Object::Object(const Dimension& d) } Object::Object(const Color& color) - : mType(kColorType), + : mType(Color::ObjectType::instance()), mU(color.get()) { LOG_IF(OBJECT_DEBUG) << "Object color constructor " << this; } -Object::Object(Filter&& filter) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(filter))) -{ - LOG_IF(OBJECT_DEBUG) << "Object filter constructor " << this; -} - -Object::Object(GraphicFilter&& graphicFilter) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(graphicFilter))) -{ - LOG_IF(OBJECT_DEBUG) << "Object graphic filter constructor " << this; -} - -Object::Object(Gradient&& gradient) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(gradient))) -{ - LOG_IF(OBJECT_DEBUG) << "Object gradient constructor " << this; -} - - -Object::Object(MediaSource&& mediaSource) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(mediaSource))) -{ - LOG_IF(OBJECT_DEBUG) << "Object MediaSource constructor " << this; -} - -Object::Object(Rect&& rect) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(rect))) -{ - LOG_IF(OBJECT_DEBUG) << "Object Rect constructor " << this; -} - -Object::Object(Radii&& radii) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(radii))) -{ - LOG_IF(OBJECT_DEBUG) << "Object Radii constructor " << this; -} - -Object::Object(URLRequest&& urlRequest) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(urlRequest))) -{ - LOG_IF(OBJECT_DEBUG) << "Object Source constructor " << this; -} - -Object::Object(StyledText&& styledText) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(styledText))) -{ - LOG_IF(OBJECT_DEBUG) << "Object StyledText constructor " << this; -} - -Object::Object(Range range) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(range))) -{ - LOG_IF(OBJECT_DEBUG) << "Object Range constructor " << this; -} - -Object::Object(const GraphicPtr& graphic) - : mType(kGraphicType), - mU(std::make_shared(graphic)) -{ - LOG_IF(OBJECT_DEBUG) << "Object Graphic constructor " << this; -} - -Object::Object(const GraphicPatternPtr& graphicPattern) - : mType(kGraphicPatternType), - mU(graphicPattern) -{ - LOG_IF(OBJECT_DEBUG) << "Object GraphicPattern constructor " << this; -} - -Object::Object(const std::shared_ptr& transform) - : mType(kTransformType), - mU(std::make_shared(transform)) -{ - LOG_IF(OBJECT_DEBUG) << "Object transform constructor " << this; -} - -Object::Object(Transform2D&& transform) - : mType(DirectObjectData::sType), - mU(DirectObjectData::create(std::move(transform))) -{ - LOG_IF(OBJECT_DEBUG) << "Object transform 2D constructor " << this; -} - -Object::Object(const EasingPtr& easing) - : mType(kEasingType), - mU(std::static_pointer_cast(easing)) -{ - LOG_IF(OBJECT_DEBUG) << "Object easing constructor " << this; -} - -Object::Object(const std::shared_ptr& accessibilityAction) - : mType(kAccessibilityActionType), - mU(std::static_pointer_cast(accessibilityAction)) -{ - LOG_IF(OBJECT_DEBUG) << "Object accessibility action constructor " << this; -} - Object::Object(const std::shared_ptr& range) - : mType(kArrayType), + : mType(Array::ObjectType::instance()), mU(std::static_pointer_cast(range)) { LOG_IF(OBJECT_DEBUG) << "Object range generator " << this; } Object::Object(const std::shared_ptr& range) - : mType(kArrayType), + : mType(Array::ObjectType::instance()), mU(std::static_pointer_cast(range)) { LOG_IF(OBJECT_DEBUG) << "Object slice generator " << this; } bool -Object::operator==(const Object& rhs) const -{ - LOG_IF(OBJECT_DEBUG) << "comparing " << *this << " to " << rhs; - - if (mType != rhs.mType) - return false; - - switch (mType) { - case kNullType: // FALL_THROUGH - case kAutoDimensionType: - return true; - case kBoolType: // FALL_THROUGH - case kNumberType: - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kColorType: - return mU.value == rhs.mU.value; - case kStringType: - return mU.string == rhs.mU.string; - case kByteCodeType: // FALL_THROUGH - case kFunctionType: - case kGraphicType: - case kGraphicPatternType: - case kTransformType: - return mU.data == rhs.mU.data; - case kGradientType: // FALL_THROUGH - case kFilterType: - case kGraphicFilterType: - case kMediaSourceType: - case kRectType: - case kRadiiType: - case kURLRequestType: - case kTransform2DType: - case kStyledTextType: - case kRangeType: - case kArrayType: - case kMapType: - return *(mU.data.get()) == *(rhs.mU.data.get()); - case kEasingType: - return *std::static_pointer_cast(mU.data) == - *std::static_pointer_cast(rhs.mU.data); - case kAccessibilityActionType: - return *std::static_pointer_cast(mU.data) == - *std::static_pointer_cast(rhs.mU.data); - case kBoundSymbolType: - return *std::static_pointer_cast(mU.data) == - *std::static_pointer_cast(rhs.mU.data); - case kComponentType: - return *std::static_pointer_cast(mU.data) == - *std::static_pointer_cast(rhs.mU.data); - case kContextType: - return *std::static_pointer_cast(mU.data) == - *std::static_pointer_cast(rhs.mU.data); - } - - return false; // Shouldn't ever get here -} - -bool -Object::operator!=(const Object& rhs) const -{ - return !operator==(rhs); -} - -bool -Object::isJson() const -{ - switch(mType) { - case kMapType: - case kArrayType: - return mU.data->getJson() != nullptr; - default: - return false; - } -} - -/** - * Return a formatted double for display. The formatted double follows the APL syntax for - * floating-point numbers. Additionally, we drop trailing zeros for decimal numbers. If the number - * is an integer or rounds to an integer, we drop the decimal point as well. - * Scientific notation numbers are not handled attractively. - * - * @param value The value to format - * @return A suitable string - */ -static inline std::string -doubleToString(double value) -{ - if (value < static_cast(std::numeric_limits::max()) - && value > static_cast(std::numeric_limits::min())) { - auto iValue = static_cast(value); - if (value == iValue) - return std::to_string(iValue); - } - - auto s = sutil::to_string(value); - auto it = s.find_last_not_of('0'); - if (it != s.find(sutil::DECIMAL_POINT)) // Remove a trailing decimal point - it++; - s.erase(it, std::string::npos); - return s; -} - -/** - * This method is used when coercing an object to a string. This can be used - * by an APL author to display information in a Text component, so we deliberately - * do not return values for many of the internal object types. Please use - * Object::toDebugString() to return strings suitable for writing to the system log. - * - * @return The user-friendly value of the object as a string. - */ -std::string -Object::asString() const -{ - switch (mType) { - case kNullType: return ""; - case kBoolType: return mU.value ? "true": "false"; - case kStringType: return mU.string; - case kNumberType: return doubleToString(mU.value); - case kAutoDimensionType: return "auto"; - case kAbsoluteDimensionType: return doubleToString(mU.value)+"dp"; - case kRelativeDimensionType: return doubleToString(mU.value)+"%"; - case kColorType: return Color(mU.value).asString(); - case kMapType: return ""; - case kArrayType: return ""; - case kByteCodeType: return ""; - case kFunctionType: return ""; - case kFilterType: return ""; - case kGraphicFilterType: return ""; - case kGradientType: return ""; - case kMediaSourceType: return ""; - case kRectType: return ""; - case kRadiiType: return ""; - case kURLRequestType: return ""; - case kStyledTextType: return as().asString(); - case kRangeType: return ""; - case kGraphicType: return ""; - case kGraphicPatternType: return ""; - case kTransformType: return ""; - case kTransform2DType: return ""; - case kEasingType: return ""; - case kBoundSymbolType: return ""; - case kComponentType: return ""; - case kContextType: return ""; - case kAccessibilityActionType: return ""; - } - - return "ERROR"; // TODO: Fix up the string type -} - -inline double -stringToDouble(const std::string& string) -{ - try { - auto len = string.size(); - auto idx = len; - double result = sutil::stod(string, &idx); - // Handle percentages. We skip over whitespace and stop on any other character - while (idx < len) { - auto c = string[idx]; - if (c == '%') { - result *= 0.01; - break; - } - if (!sutil::isspace(c)) - break; - idx++; - } - return result; - } catch (...) {} - return std::numeric_limits::quiet_NaN(); -} - -double -Object::asNumber() const -{ - switch (mType) { - case kBoolType: - case kNumberType: - return mU.value; - case kStringType: - return stringToDouble(mU.string); - case kStyledTextType: - return stringToDouble(as().asString()); - case kAbsoluteDimensionType: - return mU.value; - default: - return std::numeric_limits::quiet_NaN(); - } -} - -float -Object::asFloat() const -{ - return static_cast(asNumber()); -} - -int -Object::asInt(int base) const -{ - switch (mType) { - case kBoolType: - return static_cast(mU.value); - case kNumberType: - return std::lround(mU.value); - case kStringType: - try { return std::stoi(mU.string, nullptr, base); } catch(...) {} - return 0; - case kStyledTextType: - try { return std::stoi(as().asString(), nullptr, base); } catch(...) {} - return 0; - case kAbsoluteDimensionType: - return std::lround(mU.value); - default: - return 0; - } -} +Object::comparableWith(const Object& rhs) const { + // Same type is comparable + if (mType == rhs.mType) return true; -std::int64_t -Object::asInt64(int base) const -{ - switch (mType) { - case kBoolType: - return static_cast(mU.value); - case kNumberType: - return std::llround(mU.value); - case kStringType: - try { return std::stoll(mU.string, nullptr, base); } catch(...) {} - return 0; - case kStyledTextType: - try { return std::stoll(as().asString(), nullptr, base); } catch(...) {} - return 0; - case kAbsoluteDimensionType: - return std::llround(mU.value); - default: - return 0; - } -} - -/// @deprecated -Color -Object::asColor() const -{ - return asColor(nullptr); -} - -Color -Object::asColor(const SessionPtr& session) const -{ - switch (mType) { - case kNumberType: - case kColorType: - return Color(mU.value); - case kStringType: - return Color(session, mU.string); - case kStyledTextType: - return Color(session, as().asString()); - default: - return Color(); // Transparent - } -} - -Color -Object::asColor(const Context& context) const -{ - return asColor(context.session()); -} - -Dimension -Object::asDimension(const Context& context) const -{ - switch (mType) { - case kNumberType: - return Dimension(DimensionType::Absolute, mU.value); - case kStringType: - return Dimension(context, mU.string); - case kAbsoluteDimensionType: - return Dimension(DimensionType::Absolute, mU.value); - case kRelativeDimensionType: - return Dimension(DimensionType::Relative, mU.value); - case kAutoDimensionType: - return Dimension(DimensionType::Auto, 0); - case kStyledTextType: - return Dimension(context, as().asString()); - default: - return Dimension(DimensionType::Absolute, 0); - } -} - -Dimension -Object::asAbsoluteDimension(const Context& context) const -{ - switch (mType) { - case kNumberType: - return Dimension(DimensionType::Absolute, mU.value); - case kStringType: { - auto d = Dimension(context, mU.string); - return (d.getType() == DimensionType::Absolute ? d : Dimension(DimensionType::Absolute, 0)); - } - case kStyledTextType: { - auto d = Dimension(context, as().asString()); - return (d.getType() == DimensionType::Absolute ? d : Dimension(DimensionType::Absolute, 0)); - } - case kAbsoluteDimensionType: - return Dimension(DimensionType::Absolute, mU.value); - default: - return Dimension(DimensionType::Absolute, 0); - } -} - -Dimension -Object::asNonAutoDimension(const Context& context) const -{ - switch (mType) { - case kNumberType: - return Dimension(DimensionType::Absolute, mU.value); - case kStringType: { - auto d = Dimension(context, mU.string); - return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Absolute, 0) : d); - } - case kStyledTextType: { - auto d = Dimension(context, as().asString()); - return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Absolute, 0) : d); - } - case kAbsoluteDimensionType: - return Dimension(DimensionType::Absolute, mU.value); - case kRelativeDimensionType: - return Dimension(DimensionType::Relative, mU.value); - default: - return Dimension(DimensionType::Absolute, 0); - } -} - -Dimension -Object::asNonAutoRelativeDimension(const Context& context) const -{ - switch (mType) { - case kNumberType: - return Dimension(DimensionType::Relative, mU.value * 100); - case kStringType: { - auto d = Dimension(context, mU.string, true); - return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Relative, 0) : d); - } - case kStyledTextType: { - auto d = Dimension(context, as().asString(), true); - return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Relative, 0) : d); - } - case kAbsoluteDimensionType: - return Dimension(DimensionType::Absolute, mU.value); - case kRelativeDimensionType: - return Dimension(DimensionType::Relative, mU.value); - default: - return Dimension(DimensionType::Relative, 0); - } -} - -URLRequest -Object::asURLRequest() const -{ - switch (mType) { - case kStringType: - return { mU.string, HeaderArray() }; - case kURLRequestType: - return as(); - default: - return { "", HeaderArray() }; - } -} - -std::shared_ptr -Object::getFunction() const -{ - assert(mType == kFunctionType); - return std::static_pointer_cast(mU.data); -} - -std::shared_ptr -Object::getBoundSymbol() const -{ - assert(mType == kBoundSymbolType); - return std::static_pointer_cast(mU.data); -} - -std::shared_ptr -Object::getLiveDataObject() const -{ - assert(mType == kArrayType || mType == kMapType); - return std::dynamic_pointer_cast(mU.data); -} - -std::shared_ptr -Object::getByteCode() const -{ - assert(mType == kByteCodeType); - return std::static_pointer_cast(mU.data); -} - -std::shared_ptr -Object::getAccessibilityAction() const { - assert(mType == kAccessibilityActionType); - return std::static_pointer_cast(mU.data); -} - -const ObjectMap& -Object::getMap() const { - assert(isMap()); return mU.data->getMap(); -} - -ObjectMap& -Object::getMutableMap() { - assert(mType == kMapType); return mU.data->getMutableMap(); -} - -const ObjectArray& -Object::getArray() const { - assert(mType == kArrayType); return mU.data->getArray(); -} - -ObjectArray& -Object::getMutableArray() { - assert(mType == kArrayType); return mU.data->getMutableArray(); -} - - -const Filter& -Object::getFilter() const { - return as(); -} - -const GraphicFilter& -Object::getGraphicFilter() const { - return as(); -} + // Any true maps considered comparable + if (mType->isTrueMap() && rhs.mType->isTrueMap()) return true; -const Gradient& -Object::getGradient() const { - return as(); -} - -const MediaSource& -Object::getMediaSource() const { - return as(); -} - -GraphicPtr -Object::getGraphic() const { - assert(mType == kGraphicType); return mU.data->getGraphic(); -} - -GraphicPatternPtr -Object::getGraphicPattern() const { - assert(mType == kGraphicPatternType); return std::static_pointer_cast(mU.data); -} - -Rect -Object::getRect() const { - return as(); -} - -Radii -Object::getRadii() const { - return as(); -} - -const URLRequest& -Object::getURLRequest() const { - return as(); -} - -const StyledText& -Object::getStyledText() const { - return as(); -} - -const Range& -Object::getRange() const { - return as(); -} - -std::shared_ptr -Object::getTransformation() const { - assert(mType == kTransformType); return mU.data->getTransform(); -} + // Any arrays considered comparable. + if (mType->isArray() && rhs.mType->isArray()) return true; -Transform2D -Object::getTransform2D() const { - return as(); + return false; } -EasingPtr -Object::getEasing() const { - assert(mType == kEasingType); - return std::static_pointer_cast(mU.data); -} - -const rapidjson::Value& -Object::getJson() const { - assert(isJson()); - return *(mU.data->getJson()); -} - - bool -Object::truthy() const +Object::operator==(const Object& rhs) const { - switch (mType) { - case kNullType: - return false; - case kBoolType: - case kNumberType: - return mU.value != 0; - case kStringType: - return mU.string.size() != 0; - case kArrayType: - case kMapType: - case kByteCodeType: - case kFunctionType: - return true; - case kAbsoluteDimensionType: - case kRelativeDimensionType: - return mU.value != 0; - case kAutoDimensionType: - case kColorType: - return true; - - case kFilterType: - case kGraphicFilterType: - case kGradientType: - case kMediaSourceType: - case kEasingType: - case kRectType: - case kRadiiType: - case kTransform2DType: - case kURLRequestType: - case kStyledTextType: - case kRangeType: - case kGraphicPatternType: - return mU.data->truthy(); - - case kGraphicType: - return true; - case kTransformType: - return true; - case kBoundSymbolType: - return true; - case kComponentType: - return std::static_pointer_cast(mU.data)->getComponent() != nullptr; - case kContextType: - case kAccessibilityActionType: - return mU.data->truthy(); - } + LOG_IF(OBJECT_DEBUG) << "comparing " << *this << " to " << rhs; - // Should never be reached. - return true; -} + if (!comparableWith(rhs)) return false; -// Methods for MAP objects -Object -Object::get(const std::string& key) const -{ - assert(mType == kMapType || mType == kComponentType || mType == kContextType); - return mU.data->get(key); + return mType->equals(mU, rhs.mU); } bool -Object::has(const std::string& key) const +Object::operator!=(const Object& rhs) const { - assert(mType == kMapType || mType == kComponentType || mType == kContextType); - return mU.data->has(key); + return !operator==(rhs); } -Object -Object::opt(const std::string& key, const Object& def) const -{ - assert(mType == kMapType || mType == kComponentType || mType == kContextType); - return mU.data->opt(key, def); -} +bool Object::isNull() const { return mType->is(); } +bool Object::isBoolean() const { return mType->is();} +bool Object::isString() const { return mType->is(); } +bool Object::isNumber() const { return mType->is(); } +bool Object::isNaN() const { return isNumber() && std::isnan(getDouble()); } +bool Object::isArray() const { return mType->isArray(); } +bool Object::isMap() const { return mType->isMap(); } +bool Object::isTrueMap() const { return mType->isTrueMap(); } +bool Object::isCallable() const { return mType->isCallable(); } +bool Object::isEvaluable() const { return mType->isEvaluable(); } +bool Object::isAbsoluteDimension() const { return mType->isAbsoluteDimension(); } +bool Object::isRelativeDimension() const { return mType->isRelativeDimension(); } +bool Object::isAutoDimension() const { return mType->isAutoDimension(); } +bool Object::isNonAutoDimension() const { return mType->isNonAutoDimension(); } +bool Object::isDimension() const { return mType->isDimension(); } + +std::string Object::asString() const { return mType->asString(mU); } +double Object::asNumber() const { return mType->asNumber(mU); } +float Object::asFloat() const { return static_cast(asNumber()); } +int Object::asInt(int base) const { return mType->asInt(mU, base); } +std::int64_t Object::asInt64(int base) const { return mType->asInt64(mU, base); } +Color Object::asColor() const { return asColor(nullptr); } +Color Object::asColor(const SessionPtr& session) const { return mType->asColor(mU, session); } +Color Object::asColor(const Context& context) const { return asColor(context.session()); } +Dimension Object::asDimension(const Context& context) const { return mType->asDimension(mU, context); } +Dimension Object::asAbsoluteDimension(const Context& context) const { return mType->asAbsoluteDimension(mU, context); } +Dimension Object::asNonAutoDimension(const Context& context) const { return mType->asNonAutoDimension(mU, context); } +Dimension Object::asNonAutoRelativeDimension(const Context& context) const { return mType->asNonAutoRelativeDimension(mU, context); } + +const std::string& Object::getString() const { return mType->getString(mU); } +bool Object::getBoolean() const { return mType->getBoolean(mU); } +double Object::getDouble() const { return mType->getDouble(mU); } +int Object::getInteger() const { return static_cast(mType->getDouble(mU)); } +double Object::getAbsoluteDimension() const { return mType->getAbsoluteDimension(mU); } +double Object::getRelativeDimension() const { return mType->getRelativeDimension(mU); } +uint32_t Object::getColor() const { return mType->getColor(mU); } +const ObjectMap& Object::getMap() const { return mType->getMap(mU); } +ObjectMap& Object::getMutableMap() { return mType->getMutableMap(mU); } +const ObjectArray& Object::getArray() const { return mType->getArray(mU); } +ObjectArray& Object::getMutableArray() { return mType->getMutableArray(mU); } +std::shared_ptr Object::getLiveDataObject() const { return mType->getLiveDataObject(mU); } + +bool Object::truthy() const { return mType->truthy(mU); } + +// Methods for MAP-like objects +Object Object::get(const std::string& key) const { return mType->get(mU, key); } +bool Object::has(const std::string& key) const { return mType->has(mU, key); } +Object Object::opt(const std::string& key, const Object& def) const { return mType->opt(mU, key, def); } // Methods for ARRAY objects -Object -Object::at(std::uint64_t index) const -{ - assert(mType == kArrayType); - return mU.data->at(index); -} +Object Object::at(std::uint64_t index) const { return mType->at(mU, index); } -Object::ObjectType -Object::getType() const -{ - return mType; -} +const apl::ObjectType& Object::type() const { return *mType; } +std::uint64_t Object::size() const { return mType->size(mU); } -std::uint64_t -Object::size() const -{ - switch (mType) { - case kArrayType: - case kMapType: - case kComponentType: - case kContextType: - case kGraphicPatternType: - return mU.data->size(); - case kStringType: - return mU.string.size(); - case kStyledTextType: - return as().asString().size(); // Size of the raw text - default: - return 0; - } -} - -bool -Object::empty() const -{ - switch (mType) { - case kNullType: - return true; - case kArrayType: - case kMapType: - case kRectType: - case kGraphicPatternType: - return mU.data->empty(); - case kStringType: - return mU.string.empty(); - case kStyledTextType: - return mU.data->empty(); - default: - return false; - } -} +bool Object::empty() const { return mType->empty(mU); } -bool -Object::isMutable() const -{ - switch (mType) { - case kArrayType: - case kMapType: - return mU.data->isMutable(); - default: - return false; - } -} +bool Object::isMutable() const { return mType->isMutable(mU); } -Object -Object::eval() const -{ - return (mType == kByteCodeType || mType == kBoundSymbolType) ? mU.data->eval() : *this; -} +Object Object::eval() const { return mType->isEvaluable() ? mType->eval(mU) : *this; } /** * Internal visitor class used to check if an equation is "pure" - that is, if the result @@ -1303,7 +501,7 @@ Object::eval() const class PureVisitor : public Visitor { public: void visit(const Object& object) override { - if (object.isFunction() && !object.getFunction()->isPure()) + if (object.is() && !object.get()->isPure()) mIsPure = false; } @@ -1344,12 +542,12 @@ class SymbolVisitor : public Visitor { mCurrentSuffix.clear(); // In the majority of cases there will be no suffix - if (object.isBoundSymbol()) { // A bound symbol should be added to the map with existing suffixes - auto symbol = object.getBoundSymbol()->getSymbol(); + if (object.is()) { // A bound symbol should be added to the map with existing suffixes + auto symbol = object.get()->getSymbol(); if (symbol.second) // An invalid bound symbol will not have a context mMap.emplace(symbol.first + (mIndex == 0 ? mParentSuffix : ""), symbol.second); } - else if (object.isByteCode()) { + else if (object.is()) { object.symbols(mMap); } @@ -1387,206 +585,42 @@ class SymbolVisitor : public Visitor { void Object::symbols(SymbolReferenceMap& symbols) const { - if (mType == kByteCodeType) - std::dynamic_pointer_cast(getByteCode())->symbols(symbols); + if (mType == datagrammar::ByteCode::ObjectType::instance()) + get()->symbols(symbols); else { SymbolVisitor visitor(symbols); accept(visitor); } } -Object -Object::call(const ObjectArray& args) const -{ - assert(mType == kFunctionType || mType == kEasingType); - LOG_IF(OBJECT_DEBUG) << "Calling user function"; - return mU.data->call(args); -} - -size_t -Object::hash() const -{ - switch (mType) { - case kNullType: - return 0; - case kBoolType: - return std::hash{}(mU.value > 0); - case kStringType: - return std::hash{}(mU.string); - case kNumberType: // FALL_THORUGH - case kAbsoluteDimensionType: - case kRelativeDimensionType: - case kColorType: - return std::hash{}(mU.value); - case kAutoDimensionType: - return std::hash{}("auto"); - case kStyledTextType: - return std::hash{}(getStyledText().getRawText()); - case kArrayType: // FALL_THORUGH UNSUPPORTED - case kMapType: - case kByteCodeType: - case kFunctionType: - case kFilterType: - case kGraphicFilterType: - case kGradientType: - case kMediaSourceType: - case kRectType: - case kRadiiType: - case kURLRequestType: - case kEasingType: - case kTransform2DType: - case kGraphicPatternType: - case kGraphicType: - case kTransformType: - case kBoundSymbolType: - case kComponentType: - case kContextType: - case kAccessibilityActionType: - default: - return 0; - } +Object Object::call(const ObjectArray& args) const { return mType->call(mU, args); } - return 0; -} +size_t Object::hash() const { return mType->hash(mU); } // Visitor pattern void Object::accept(Visitor& visitor) const { visitor.visit(*this); - if (!visitor.isAborted() && (mType == kArrayType || mType == kMapType)) - mU.data->accept(visitor); + if (!visitor.isAborted()) + mType->accept(mU, visitor); } -rapidjson::Value -Object::serialize(rapidjson::Document::AllocatorType& allocator) const -{ - switch (mType) { - case kNullType: - return rapidjson::Value(); - case kBoolType: - return rapidjson::Value(static_cast(mU.value)); - case kNumberType: - return std::isfinite(mU.value) ? rapidjson::Value(mU.value) : rapidjson::Value(); - case kStringType: - return rapidjson::Value(mU.string.c_str(), allocator); - case kArrayType: { - rapidjson::Value v(rapidjson::kArrayType); - for (int i = 0 ; i < size() ; i++) - v.PushBack(at(i).serialize(allocator), allocator); - return v; - } - case kMapType: { - rapidjson::Value m(rapidjson::kObjectType); - for (auto &kv : mU.data->getMap()) - m.AddMember(rapidjson::Value(kv.first.c_str(), allocator), kv.second.serialize(allocator).Move(), allocator); - return m; - } - case kByteCodeType: - return rapidjson::Value("COMPILED BYTE CODE", allocator); - case kFunctionType: - return rapidjson::Value("FUNCTION", allocator); - case kAbsoluteDimensionType: - return rapidjson::Value(std::isfinite(mU.value) ? mU.value : 0); - case kRelativeDimensionType: - return rapidjson::Value((doubleToString(mU.value)+"%").c_str(), allocator); - case kAutoDimensionType: - return rapidjson::Value("auto", allocator); - case kColorType: - return rapidjson::Value(asString().c_str(), allocator); - case kFilterType: - case kGraphicFilterType: - case kGradientType: - case kMediaSourceType: - case kRectType: - case kRadiiType: - case kURLRequestType: - case kEasingType: - case kTransform2DType: - case kStyledTextType: - case kRangeType: - case kGraphicPatternType: - return mU.data->serialize(allocator); - case kGraphicType: - return getGraphic()->serialize(allocator); - case kTransformType: - return rapidjson::Value("TRANSFORM", allocator); - case kBoundSymbolType: - return rapidjson::Value("BOUND SYMBOL", allocator); - case kComponentType: - return std::static_pointer_cast(mU.data)->serialize(allocator); - case kContextType: - case kAccessibilityActionType: - return mU.data->serialize(allocator); - } - - return rapidjson::Value(); // Never should be reached -} +rapidjson::Value Object::serialize(rapidjson::Document::AllocatorType& allocator) const { return mType->serialize(mU, allocator); } rapidjson::Value Object::serializeDirty(rapidjson::Document::AllocatorType& allocator) const { - switch (mType) { - case kGraphicType: - // TODO: Fix this - should return just the dirty bits - return serialize(allocator); - break; - default: - return serialize(allocator); - } + // TODO: Fix this - should return just the dirty bits + return serialize(allocator); } -template const T& Object::as() const { - assert(mType == DirectObjectData::sType); - return *static_cast(mU.data->inner()); -} +std::string Object::toDebugString() const { return mType->toDebugString(mU); } -std::string -Object::toDebugString() const +const void* +Object::extractDataInner(const Object::DataHolder& dataHolder) { - switch (mType) { - case Object::kNullType: - return "null"; - case Object::kBoolType: - return (mU.value ? "true" : "false"); - case Object::kNumberType: - return sutil::to_string(mU.value); - case Object::kStringType: - return "'" + mU.string + "'"; - case Object::kMapType: - case Object::kArrayType: - case Object::kByteCodeType: - case Object::kFunctionType: - return mU.data->toDebugString(); - case Object::kAbsoluteDimensionType: - return "AbsDim<" + sutil::to_string(mU.value) + ">"; - case Object::kRelativeDimensionType: - return "RelDim<" + sutil::to_string(mU.value) + ">"; - case Object::kAutoDimensionType: - return "AutoDim"; - case Object::kColorType: - return asString(); - case Object::kFilterType: - case Object::kGraphicFilterType: - case Object::kGradientType: - case Object::kMediaSourceType: - case Object::kRectType: - case Object::kRadiiType: - case Object::kURLRequestType: - case Object::kStyledTextType: - case Object::kRangeType: - case Object::kGraphicType: - case Object::kGraphicPatternType: - case Object::kTransformType: - case Object::kTransform2DType: - case Object::kEasingType: - case Object::kBoundSymbolType: - case Object::kComponentType: - case Object::kContextType: - case Object::kAccessibilityActionType: - return mU.data->toDebugString(); - } - return ""; + return dataHolder.data->inner(); } streamer& diff --git a/aplcore/src/primitives/objectdata.cpp b/aplcore/src/primitives/objectdata.cpp deleted file mode 100644 index e63f99f..0000000 --- a/aplcore/src/primitives/objectdata.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include "apl/animation/easing.h" -#include "apl/graphic/graphicfilter.h" -#include "apl/primitives/filter.h" -#include "apl/primitives/gradient.h" -#include "apl/primitives/mediasource.h" -#include "apl/primitives/radii.h" -#include "apl/primitives/range.h" -#include "apl/primitives/rect.h" -#include "apl/primitives/styledtext.h" -#include "apl/primitives/transform2d.h" - -#include "apl/primitives/objectdata.h" - -namespace apl { - -/****************************************************************************/ - -template<> const Object::ObjectType DirectObjectData::sType = Object::kFilterType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kGraphicFilterType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kGradientType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kMediaSourceType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kRectType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kRadiiType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kURLRequestType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kTransform2DType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kStyledTextType; -template<> const Object::ObjectType DirectObjectData::sType = Object::kRangeType; - -} // namespace apl diff --git a/aplcore/src/primitives/objecttype.cpp b/aplcore/src/primitives/objecttype.cpp new file mode 100644 index 0000000..981ed52 --- /dev/null +++ b/aplcore/src/primitives/objecttype.cpp @@ -0,0 +1,122 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "apl/primitives/objecttype.h" + +#include "apl/engine/context.h" +#include "apl/livedata/livedataobject.h" +#include "apl/primitives/color.h" +#include "apl/primitives/dimension.h" + +namespace apl { + +/// Base/grouping + +Color +ObjectType::asColor(const Object::DataHolder&, const SessionPtr&) const +{ + return {}; +} + +Dimension +ObjectType::asDimension(const Object::DataHolder&, const Context&) const +{ + return {DimensionType::Absolute, 0}; +} + +Dimension +ObjectType::asAbsoluteDimension(const Object::DataHolder&, const Context&) const +{ + return {DimensionType::Absolute, 0}; +} + +Dimension +ObjectType::asNonAutoDimension(const Object::DataHolder&, const Context&) const +{ + return {DimensionType::Absolute, 0}; +} + +Dimension +ObjectType::asNonAutoRelativeDimension(const Object::DataHolder&, const Context&) const +{ + return {DimensionType::Relative, 0}; +} + +/// Primitive types + +Color +Number::ObjectType::asColor(const Object::DataHolder& dataHolder, const SessionPtr&) const +{ + return {(uint32_t)dataHolder.value}; +} + +Dimension +Number::ObjectType::asDimension(const Object::DataHolder& dataHolder, const Context&) const +{ + return {DimensionType::Absolute, dataHolder.value}; +} + +Dimension +Number::ObjectType::asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context&) const +{ + return {DimensionType::Absolute, dataHolder.value}; +} + +Dimension +Number::ObjectType::asNonAutoDimension(const Object::DataHolder& dataHolder, const Context&) const +{ + return {DimensionType::Absolute, dataHolder.value}; +} + +Dimension +Number::ObjectType::asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context&) const +{ + return {DimensionType::Relative, dataHolder.value * 100}; +} + +Color +String::ObjectType::asColor(const Object::DataHolder& dataHolder, const SessionPtr& session) const +{ + return {session, dataHolder.string}; +} + +Dimension +String::ObjectType::asDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + return {context, dataHolder.string}; +} + +Dimension +String::ObjectType::asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, dataHolder.string); + return (d.getType() == DimensionType::Absolute ? d : Dimension(DimensionType::Absolute, 0)); +} + +Dimension +String::ObjectType::asNonAutoDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, dataHolder.string); + return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Absolute, 0) : d); +} + +Dimension +String::ObjectType::asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, dataHolder.string, true); + return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Relative, 0) : d); +} + +} // namespace apl diff --git a/aplcore/src/primitives/rect.cpp b/aplcore/src/primitives/rect.cpp index 42e301c..5e83ab3 100644 --- a/aplcore/src/primitives/rect.cpp +++ b/aplcore/src/primitives/rect.cpp @@ -14,6 +14,7 @@ */ #include "apl/primitives/rect.h" +#include "apl/primitives/transform2d.h" #include #ifdef APL_CORE_UWP @@ -124,6 +125,25 @@ Rect::inset(float dx, float dy) const { return {x, y, w, h}; } +Rect +Rect::boundingBox(const apl::Transform2D& transform) const +{ + if (empty()) + return {}; + + auto p1 = transform * getTopLeft(); + auto p2 = transform * getTopRight(); + auto p3 = transform * getBottomLeft(); + auto p4 = transform * getBottomRight(); + + auto left = std::min({p1.getX(), p2.getX(), p3.getX(), p4.getX()}); + auto top = std::min({p1.getY(), p2.getY(), p3.getY(), p4.getY()}); + auto right = std::max({p1.getX(), p2.getX(), p3.getX(), p4.getX()}); + auto bottom = std::max({p1.getY(), p2.getY(), p3.getY(), p4.getY()}); + + return { left, top, right - left, bottom - top }; +} + rapidjson::Value Rect::serialize(rapidjson::Document::AllocatorType& allocator) const { rapidjson::Value v(rapidjson::kArrayType); diff --git a/aplcore/src/primitives/styledtext.cpp b/aplcore/src/primitives/styledtext.cpp index d4cfcf3..ba42979 100644 --- a/aplcore/src/primitives/styledtext.cpp +++ b/aplcore/src/primitives/styledtext.cpp @@ -19,7 +19,9 @@ #include -#include "apl/primitives/object.h" +#include "apl/primitives/objectdata.h" +#include "apl/primitives/color.h" +#include "apl/primitives/dimension.h" #include "apl/primitives/unicode.h" #include "apl/primitives/styledtext.h" #include "apl/primitives/styledtextstate.h" @@ -238,8 +240,8 @@ template<> struct action< hexentity > StyledText StyledText::create(const Context& context, const Object& object) { - if (object.isStyledText()) - return object.getStyledText(); + if (object.is()) + return object.get(); return {context, object.asString()}; } @@ -352,4 +354,72 @@ StyledText::Iterator::spanCount() { return mStyledText.mSpans.size(); } +std::string +StyledText::ObjectType::asString(const Object::DataHolder& dataHolder) const +{ + return get(dataHolder).asString(); +} + +double +StyledText::ObjectType::asNumber(const Object::DataHolder& dataHolder) const +{ + return aplFormattedStringToDouble(get(dataHolder).asString()); +} + +int +StyledText::ObjectType::asInt(const Object::DataHolder& dataHolder, int base) const +{ + return sutil::stoi(get(dataHolder).asString(), nullptr, base); +} + +int64_t +StyledText::ObjectType::asInt64(const Object::DataHolder& dataHolder, int base) const +{ + return sutil::stoll(get(dataHolder).asString(), nullptr, base); +} + +Color +StyledText::ObjectType::asColor(const Object::DataHolder& dataHolder, const apl::SessionPtr& session) const +{ + return Color(session, get(dataHolder).asString()); +} + +Dimension +StyledText::ObjectType::asDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + return Dimension(context, get(dataHolder).asString()); +} + +Dimension +StyledText::ObjectType::asAbsoluteDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, get(dataHolder).asString()); + return (d.getType() == DimensionType::Absolute ? d : Dimension(DimensionType::Absolute, 0)); +} + +Dimension +StyledText::ObjectType::asNonAutoDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, get(dataHolder).asString()); + return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Absolute, 0) : d); +} +Dimension +StyledText::ObjectType::asNonAutoRelativeDimension(const Object::DataHolder& dataHolder, const Context& context) const +{ + auto d = Dimension(context, get(dataHolder).asString(), true); + return (d.getType() == DimensionType::Auto ? Dimension(DimensionType::Relative, 0) : d); +} + +std::uint64_t +StyledText::ObjectType::size(const Object::DataHolder& dataHolder) const +{ + return get(dataHolder).asString().size(); +} + +size_t +StyledText::ObjectType::hash(const Object::DataHolder& dataHolder) const +{ + return std::hash{}(get(dataHolder).getRawText()); +} + } // namespace apl diff --git a/aplcore/src/primitives/styledtextstate.cpp b/aplcore/src/primitives/styledtextstate.cpp index 1f356bd..29d1482 100644 --- a/aplcore/src/primitives/styledtextstate.cpp +++ b/aplcore/src/primitives/styledtextstate.cpp @@ -14,6 +14,7 @@ */ #include "apl/engine/evaluate.h" +#include "apl/primitives/color.h" #include "apl/primitives/styledtextstate.h" #include "apl/utils/stringfunctions.h" diff --git a/aplcore/src/primitives/timegrammar.cpp b/aplcore/src/primitives/timegrammar.cpp index 84aba43..cb2c6f5 100644 --- a/aplcore/src/primitives/timegrammar.cpp +++ b/aplcore/src/primitives/timegrammar.cpp @@ -21,15 +21,13 @@ namespace timegrammar { std::string timeToString(const std::string& format, double time) { - try { - time_state state(time); - pegtl::string_input<> in(format, ""); - pegtl::parse(in, state); + time_state state(time); + pegtl::string_input<> in(format, ""); + if (!pegtl::parse(in, state) || state.failed) { + LOG(LogLevel::kError) << "Error in '" << format << "', " << state.what(); + } else { return state.mString; } - catch (parse_error e) { - LOG(LogLevel::kError) << "Error in '" << format << "', " << e.what(); - } return ""; } diff --git a/aplcore/src/primitives/transform.cpp b/aplcore/src/primitives/transform.cpp index 65cf3cb..19f94ab 100644 --- a/aplcore/src/primitives/transform.cpp +++ b/aplcore/src/primitives/transform.cpp @@ -23,6 +23,8 @@ namespace apl { +// TODO: Comparisons here should not use dynamic_casts. + // Evaluate this object as a dimension. If relative, use the side length to convert to a DP value. inline float evalDim(const Dimension& object, float side) @@ -40,15 +42,10 @@ class ScalarTransform : public Transform { return mFunction(mValue); } - bool canInterpolate(Transform& other) const override { - auto o = dynamic_cast(&other); - if (o == nullptr) - return false; - - return &mFunction == &o->mFunction; - } - Transform2D interpolate(Transform& other, float alpha, float, float) const override { + assert(other.getType() == Type::ROTATE || + other.getType() == Type::SKEW_X || + other.getType() == Type::SKEW_Y); float v2 = static_cast(other).mValue; float value = mValue * (1-alpha) + v2 * alpha; return mFunction(value); @@ -70,12 +67,9 @@ class ScaleTransform : public Transform { return Transform2D::scale(mX, mY); } - bool canInterpolate(Transform& other) const override { - return dynamic_cast(&other) != nullptr; - } - Transform2D interpolate(Transform& other, float alpha, float, float) const override { - auto o = dynamic_cast(&other); + assert(other.getType() == Type::SCALE); + auto o = static_cast(&other); float x = mX * (1-alpha) + o->mX * alpha; float y = mY * (1-alpha) + o->mY * alpha; return Transform2D::scale(x, y); @@ -96,12 +90,9 @@ class TranslateTransform : public Transform { return Transform2D::translate(evalDim(mX, width), evalDim(mY, height)); } - bool canInterpolate(Transform& other) const override { - return dynamic_cast(&other) != nullptr; - } - Transform2D interpolate(Transform& other, float alpha, float width, float height) const override { - auto o = dynamic_cast(&other); + assert(other.getType() == Type::TRANSLATE); + auto o = static_cast(&other); float x = evalDim(mX, width) * (1-alpha) + evalDim(o->mX, width) * alpha; float y = evalDim(mY, height) * (1-alpha) + evalDim(o->mY, height) * alpha; return Transform2D::translate(x, y); diff --git a/aplcore/src/primitives/transform2d.cpp b/aplcore/src/primitives/transform2d.cpp index 3fb3e09..046c2bc 100644 --- a/aplcore/src/primitives/transform2d.cpp +++ b/aplcore/src/primitives/transform2d.cpp @@ -15,6 +15,8 @@ #include +#include "apl/datagrammar/grammarpolyfill.h" +#include "apl/primitives/rect.h" #include "apl/primitives/transform2d.h" #include "apl/utils/session.h" #include "apl/utils/stringfunctions.h" @@ -67,12 +69,12 @@ struct grammar : must {}; // **************** Error reporting *************** template -struct t2_control : pegtl::normal< Rule > { +struct t2_control : apl_control< Rule > { static const std::string error_message; template - static void raise( const Input& in, States&&... ) { - throw pegtl::parse_error( error_message, in ); + static void raise( const Input& in, States&&... st) { + apl_control::fail(in, st...); } }; @@ -91,7 +93,7 @@ template struct action : pegtl::nothing< Rule > { }; -struct transform_state { +struct transform_state : fail_state { void push(double value) { args[arg_count++] = value; } @@ -247,21 +249,20 @@ namespace pegtl = tao::TAO_PEGTL_NAMESPACE; Transform2D Transform2D::parse(const SessionPtr& session, const std::string& transform) { - try { - t2grammar::transform_state state; - pegtl::string_input<> in(transform, ""); - pegtl::parse(in, state); + t2grammar::transform_state state; + pegtl::string_input<> in(transform, ""); + + if (!pegtl::parse(in, state) || state.failed) { + CONSOLE(session) << "Error parsing transform '" << transform << "'" << state.what(); + } else { return state.transform; } - catch (pegtl::parse_error error) { - CONSOLE(session) << "Error parsing transform '" << transform << "'" << error.what(); - } return Transform2D(); } Rect -Transform2D::calculateAxisAlignedBoundingBox(const Rect& rect) { +Transform2D::calculateAxisAlignedBoundingBox(const Rect& rect) const { if (isIdentity()) return rect; diff --git a/aplcore/src/primitives/urlrequest.cpp b/aplcore/src/primitives/urlrequest.cpp index f79d761..55a49df 100644 --- a/aplcore/src/primitives/urlrequest.cpp +++ b/aplcore/src/primitives/urlrequest.cpp @@ -59,18 +59,20 @@ processHeaders( const std::vector& sourceHeaders, const FilterRuleArray& return result; } -URLRequest::URLRequest(const std::string url, const HeaderArray headers) : - mUrl(std::move(url)), - mHeaders(std::move(headers)) { +URLRequest::URLRequest(const std::string& url, HeaderArray headers) : + mUrl(url), + mHeaders(std::move(headers)) +{ } Object -URLRequest::create(const Context& context, const Object& object) { - if (object.isURLRequest()) +URLRequest::create(const Context& context, const Object& object) +{ + if (object.is()) return object; if (object.isString()) - return object.asURLRequest(); + return URLRequest(object.getString(), HeaderArray()); if (!object.isMap()) return Object::NULL_OBJECT(); @@ -85,8 +87,27 @@ URLRequest::create(const Context& context, const Object& object) { context.getRootConfig().getHttpHeadersFilterRules() )}; } +Object +URLRequest::create(const std::string& url) +{ + return URLRequest(url, HeaderArray()); +} + +URLRequest +URLRequest::asURLRequest(const Object& object) +{ + if (object.isString()) { + return URLRequest::create(object.getString()).get(); + } else if (object.is()) { + return object.get(); + } + + return URLRequest::create("").get(); +} + bool -URLRequest::operator==(const URLRequest& other) const { +URLRequest::operator==(const URLRequest& other) const +{ return mUrl == other.mUrl && mHeaders == other.mHeaders; } @@ -94,7 +115,8 @@ URLRequest::operator==(const URLRequest& other) const { // LCOV_EXCL_START // GCOV_EXCL_START std::string -URLRequest::toDebugString() const { +URLRequest::toDebugString() const +{ std::string result = "Source(name, bounds, opacity, std::move(transform)); } -NodePtr -generic() -{ - return std::make_shared(); -} - NodePtr transform(Transform2D transform, const NodePtr& child) { auto node = std::make_shared(); node->setTransform(transform); - if (child) - node->appendChild(child); + node->setChild(child); return node; } @@ -58,7 +51,7 @@ transform(Point offset, const NodePtr& child) NodePtr transform(const Object& object, const NodePtr& child) { - return transform(object.getTransform2D(), child); + return transform(object.get(), child); } NodePtr @@ -72,8 +65,7 @@ clip(PathPtr path, const NodePtr& child) { auto node = std::make_shared(); node->setPath(std::move(path)); - if (child) - node->appendChild(child); + node->setChild(child); return node; } @@ -83,10 +75,7 @@ opacity(float opacity, const NodePtr& child) { auto node = std::make_shared(); node->setOpacity(opacity); - - if (child) - node->appendChild(child); - + node->setChild(child); return node; } @@ -121,10 +110,10 @@ video(MediaPlayerPtr player, Rect target, VideoScale scale) NodePtr shadowNode(ShadowPtr shadow, const NodePtr& child) { - assert(filter); + // No assert. nullptr shadow is a valid case. auto node = std::make_shared(); - node->setShadow(std::move(shadow)); - node->appendChild(child); + if (shadow) node->setShadow(std::move(shadow)); + node->setChild(child); return node; } @@ -294,25 +283,28 @@ paint(const GraphicPatternPtr& pattern, float opacity, Transform2D transform) SceneGraphUpdates updates; - auto node = std::make_shared(); - for (const auto& m : pattern->getItems()) - node->appendChild(m->getSceneGraph(updates)); - paint->setNode(node); + NodePtr node = nullptr; + for (auto it = pattern->getItems().rbegin() ; it != pattern->getItems().rend() ; it++) { + auto child = (*it)->getSceneGraph(updates); + if (child) + node = child->setNext(node); + } + paint->setNode(node); return paint; } PaintPtr paint(const Object& object, float opacity, Transform2D transform) { - if (object.isColor()) + if (object.is()) return paint(static_cast(object.getColor()), opacity); - if (object.isGradient()) - return paint(object.getGradient(), opacity, transform); + if (object.is()) + return paint(object.get(), opacity, transform); - if (object.isGraphicPattern()) - return paint(object.getGraphicPattern(), opacity, transform); + if (object.is()) + return paint(object.get(), opacity, transform); return std::make_shared(); } @@ -364,7 +356,7 @@ accessibility(CoreComponent& component) acc->setRole(role); for (const auto& m : actions) { - const auto& ptr = m.getAccessibilityAction(); + const auto& ptr = m.get(); acc->appendAction(ptr->getName(), ptr->getLabel(), ptr->enabled()); } diff --git a/aplcore/src/scenegraph/filter.cpp b/aplcore/src/scenegraph/filter.cpp index e5d604b..18aceb8 100644 --- a/aplcore/src/scenegraph/filter.cpp +++ b/aplcore/src/scenegraph/filter.cpp @@ -40,7 +40,7 @@ BlendFilter::serialize(rapidjson::Document::AllocatorType& allocator) const std::string BlurFilter::toDebugString() const { - return "Blur radius=" + std::to_string(radius); + return "Blur radius=" + sutil::to_string(radius); } rapidjson::Value @@ -58,7 +58,7 @@ BlurFilter::serialize(rapidjson::Document::AllocatorType& allocator) const std::string GrayscaleFilter::toDebugString() const { - return "Grayscale amount=" + std::to_string(amount); + return "Grayscale amount=" + sutil::to_string(amount); } rapidjson::Value @@ -98,7 +98,7 @@ NoiseFilter::toDebugString() const { return "Noise kind=" + sFilterNoiseKindBimap.at(kind) + " useColor=" + (useColor ? "yes" : "no") + - " sigma=" + std::to_string(sigma); + " sigma=" + sutil::to_string(sigma); } rapidjson::Value @@ -118,7 +118,7 @@ NoiseFilter::serialize(rapidjson::Document::AllocatorType& allocator) const std::string SaturateFilter::toDebugString() const { - return "Saturate amount=" + std::to_string(amount); + return "Saturate amount=" + sutil::to_string(amount); } rapidjson::Value diff --git a/aplcore/src/scenegraph/layer.cpp b/aplcore/src/scenegraph/layer.cpp index eed5535..44a132e 100644 --- a/aplcore/src/scenegraph/layer.cpp +++ b/aplcore/src/scenegraph/layer.cpp @@ -118,22 +118,12 @@ Layer::appendChild(const LayerPtr& child) } void -Layer::clearContent() +Layer::setContent(const NodePtr& node) { - if (mContent.empty()) - return; - - mContent.clear(); - setFlag(kFlagRedrawContent); -} - -void -Layer::appendContent(const NodePtr& node) -{ - assert(node); - - mContent.push_back(node); - setFlag(kFlagRedrawContent); + if (node != mContent) { + mContent = node; + setFlag(kFlagRedrawContent); + } } bool @@ -245,8 +235,8 @@ Layer::visible() const if (mShadow && mShadow->visible()) return true; - for (const auto& m : mContent) - if (m->visible()) + for (auto node = mContent; node; node = node->next()) + if (node->visible()) return true; for (const auto& m : mChildren) @@ -276,9 +266,9 @@ Layer::serialize(rapidjson::Document::AllocatorType& allocator) const if (mShadow) out.AddMember("shadow", mShadow->serialize(allocator), allocator); - if (!mContent.empty()) { + if (mContent) { auto contentArray = rapidjson::Value(rapidjson::kArrayType); - for (const auto& node : mContent) + for (auto node = mContent ; node != nullptr ; node = node->next()) contentArray.PushBack(node->serialize(allocator), allocator); out.AddMember("content", contentArray, allocator); } diff --git a/aplcore/src/scenegraph/modifiednodelist.cpp b/aplcore/src/scenegraph/modifiednodelist.cpp index 7a6119e..6c2160c 100644 --- a/aplcore/src/scenegraph/modifiednodelist.cpp +++ b/aplcore/src/scenegraph/modifiednodelist.cpp @@ -20,7 +20,7 @@ namespace apl { namespace sg { -static NodePtr sTerminal = std::make_shared(); +static NodePtr sTerminal = std::make_shared(); ModifiedNodeList::ModifiedNodeList() { @@ -58,17 +58,5 @@ ModifiedNodeList::contentChanged(Node* node) } } -void -ModifiedNodeList::childChanged(Node* node) -{ - assert(node); - node->setFlag(Node::kNodeFlagChildrenChanged); - - if (!node->mNextModified) { - node->mNextModified = mModified; - mModified = node->shared_from_this(); - } -} - } // namespace sg } // namespace apl diff --git a/aplcore/src/scenegraph/node.cpp b/aplcore/src/scenegraph/node.cpp index 73f989c..07e54b5 100644 --- a/aplcore/src/scenegraph/node.cpp +++ b/aplcore/src/scenegraph/node.cpp @@ -32,47 +32,24 @@ namespace sg { Node::~Node() { - assert(!mNextSibling); - - auto child = mFirstChild; - while (child) { - auto nextChild = child->mNextSibling; - child->mNextSibling.reset(); - child = nextChild; - } - - mFirstChild.reset(); } -// TODO: Performance here is linear, but this is rarely called void -Node::appendChild(const NodePtr& child) // NOLINT(performance-unnecessary-value-param) +Node::setChild(const NodePtr& child) { - assert(child); + mFirstChild = child; +} - if (mFirstChild) { - auto ptr = mFirstChild; - while (ptr->next()) - ptr = ptr->next(); - ptr->mNextSibling = child; - } - else - mFirstChild = child; +NodePtr +Node::setNext(const NodePtr& sibling) +{ + mNextSibling = sibling; + return shared_from_this(); } void Node::removeAllChildren() { - if (!mFirstChild) - return; - - auto child = mFirstChild; - while (child) { - auto nextChild = child->mNextSibling; - child->mNextSibling.reset(); - child = nextChild; - } - mFirstChild.reset(); } @@ -117,6 +94,24 @@ Node::visible() const return false; } +Rect +Node::boundingBox(const Transform2D& transform) const +{ + Rect result; + for (auto child = mFirstChild ; child ; child = child->mNextSibling) + result = result.extend(child->boundingBox(transform)); + return result; +} + +Rect +Node::calculateBoundingBox(const NodePtr& node, const Transform2D& transform) +{ + Rect result; + for (auto n = node ; n ; n = n->next()) + result = result.extend(n->boundingBox(transform)); + return result; +} + rapidjson::Value Node::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -132,23 +127,6 @@ Node::serialize(rapidjson::Document::AllocatorType& allocator) const /************************************************************************************************/ -std::string -GenericNode::toDebugString() const -{ - return "GenericNode"; -} - - -rapidjson::Value -GenericNode::serialize(rapidjson::Document::AllocatorType& allocator) const -{ - auto result = Node::serialize(allocator); - result.AddMember("type", rapidjson::StringRef("generic"), allocator); - return result; -} - -/************************************************************************************************/ - bool DrawNode::setPath(PathPtr path) { @@ -193,6 +171,26 @@ DrawNode::visible() const return false; } +Rect +DrawNode::boundingBox(const Transform2D& transform) const +{ + if (!mPath) + return {}; + + auto result = mPath->boundingBox(transform); + float width = 0.0f; + for (auto op = mOp ; op ; op = op->nextSibling) + width = std::max(width, op->maxWidth() * 0.5f); + + // Scale the pen width + if (width > 0) { + auto s = transform.scaleExpansion(); + result = result.inset(-s.getX() * width, -s.getY() * width); + } + + return result; +} + rapidjson::Value DrawNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -200,8 +198,12 @@ DrawNode::serialize(rapidjson::Document::AllocatorType& allocator) const result.AddMember("type", rapidjson::StringRef("draw"), allocator); if (mPath) result.AddMember("path", mPath->serialize(allocator), allocator); - if (mOp) - result.AddMember("op", mOp->serialize(allocator), allocator); + if (mOp) { + auto opArray = rapidjson::Value(rapidjson::kArrayType); + for (auto op = mOp ; op ; op = op->nextSibling) + opArray.PushBack(op->serialize(allocator), allocator); + result.AddMember("op", opArray, allocator); + } return result; } @@ -270,13 +272,37 @@ TextNode::visible() const return false; } +Rect +TextNode::boundingBox(const Transform2D& transform) const +{ + if (!mTextLayout) + return {}; + + auto result = mTextLayout->getBoundingBoxForLines(mRange).boundingBox(transform); + float width = 0.0f; + for (auto op = mOp ; op ; op = op->nextSibling) + width = std::max(width, op->maxWidth() * 0.5f); + + // Scale the pen width + if (width > 0) { + auto s = transform.scaleExpansion(); + result = result.inset(-s.getX() * width, -s.getY() * width); + } + + return result; +} + rapidjson::Value TextNode::serialize(rapidjson::Document::AllocatorType& allocator) const { auto result = Node::serialize(allocator); result.AddMember("type", rapidjson::StringRef("text"), allocator); - if (mOp) - result.AddMember("op", mOp->serialize(allocator), allocator); + if (mOp) { + auto opArray = rapidjson::Value(rapidjson::kArrayType); + for (auto op = mOp ; op ; op = op->nextSibling) + opArray.PushBack(op->serialize(allocator), allocator); + result.AddMember("op", opArray, allocator); + } if (!mRange.empty()) result.AddMember("range", mRange.serialize(allocator), allocator); if (mTextLayout && !mTextLayout->empty()) @@ -302,6 +328,12 @@ TransformNode::toDebugString() const return std::string("TransformNode transform="+ mTransform.toDebugString()); } +Rect +TransformNode::boundingBox(const Transform2D& transform) const +{ + return Node::boundingBox(transform * mTransform); +} + rapidjson::Value TransformNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -311,6 +343,7 @@ TransformNode::serialize(rapidjson::Document::AllocatorType& allocator) const return result; } + /************************************************************************************************/ bool @@ -329,6 +362,15 @@ ClipNode::toDebugString() const return "ClipNode"; } +Rect +ClipNode::boundingBox(const Transform2D& transform) const +{ + auto bb = Node::boundingBox(transform); + if (mPath) + bb = bb.intersect(mPath->boundingBox(transform)); + return bb; +} + rapidjson::Value ClipNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -450,6 +492,12 @@ ImageNode::visible() const return mImage != nullptr; } +Rect +ImageNode::boundingBox(const Transform2D& transform) const +{ + return mTarget.boundingBox(transform); +} + rapidjson::Value ImageNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -516,6 +564,12 @@ VideoNode::visible() const return mPlayer != nullptr; } +Rect +VideoNode::boundingBox(const Transform2D& transform) const +{ + return mTarget.boundingBox(transform); +} + rapidjson::Value VideoNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -546,6 +600,27 @@ ShadowNode::toDebugString() const return "ShadowNode"; } +Rect +ShadowNode::boundingBox(const Transform2D& transform) const +{ + if (!mShadow) + return Node::boundingBox(transform); + + // Calculate the content size in the local coordinate system + auto inner = Node::boundingBox(Transform2D()); + auto offset = mShadow->getOffset(); + auto delta = mShadow->getRadius(); + + // Apply the shadow offset to all four sides + auto x1 = inner.getLeft() + std::min(0.0f, offset.getX() - delta); + auto y1 = inner.getTop() + std::min(0.0f, offset.getY() - delta); + auto x2 = inner.getRight() + std::max(0.0f, offset.getX() + delta); + auto y2 = inner.getBottom() + std::max(0.0f, offset.getY() + delta); + + // Calculate the transform of the shadowed box and return that + return Rect{x1, y1, x2 - x1, y2 - y1}.boundingBox(transform); +} + rapidjson::Value ShadowNode::serialize(rapidjson::Document::AllocatorType& allocator) const { @@ -620,6 +695,14 @@ EditTextNode::visible() const return true; } +Rect +EditTextNode::boundingBox(const Transform2D& transform) const +{ + // Note: The EditTextNode has no intrinsic size. An EditTextNode is required to + // be directly under a layer and the layer sets the size of the edit text box. + return {}; +} + rapidjson::Value EditTextNode::serialize(rapidjson::Document::AllocatorType& allocator) const { diff --git a/aplcore/src/scenegraph/paint.cpp b/aplcore/src/scenegraph/paint.cpp index cbbf604..b1260ef 100644 --- a/aplcore/src/scenegraph/paint.cpp +++ b/aplcore/src/scenegraph/paint.cpp @@ -24,8 +24,10 @@ namespace sg { static std::string tail(const Paint& paint) { - return std::string(" opacity=") + std::to_string(paint.getOpacity()) + - " transform=" + paint.getTransform().toDebugString(); + auto result = std::string(" opacity=") + sutil::to_string(paint.getOpacity()); + if (!paint.getTransform().isIdentity()) + result += " transform=" + paint.getTransform().toDebugString(); + return result; } bool @@ -295,7 +297,13 @@ PatternPaint::serialize(rapidjson::Document::AllocatorType& allocator) const result.AddMember("type", "patternPaint", allocator); result.AddMember("size", mSize.serialize(allocator), allocator); - result.AddMember("node", mNode->serialize(allocator), allocator); // TODO: Do we need an array of node here? + + if (mNode) { + auto contentArray = rapidjson::Value(rapidjson::kArrayType); + for (auto node = mNode ; node != nullptr; node = node->next()) + contentArray.PushBack(node->serialize(allocator), allocator); + result.AddMember("content", contentArray, allocator); + } return result; } diff --git a/aplcore/src/scenegraph/path.cpp b/aplcore/src/scenegraph/path.cpp index cafbdad..c803614 100644 --- a/aplcore/src/scenegraph/path.cpp +++ b/aplcore/src/scenegraph/path.cpp @@ -14,6 +14,7 @@ */ #include "apl/scenegraph/path.h" +#include "apl/scenegraph/pathbounds.h" namespace apl { namespace sg { @@ -53,7 +54,8 @@ operator==(const PathPtr& lhs, const PathPtr& rhs) { bool RectPath::empty() const { - return mRect.empty(); + // A rectangular path always has segments that can be stroked. + return false; } std::string @@ -73,6 +75,12 @@ RectPath::setRect(Rect rect) return true; } +Rect +RectPath::boundingBox(const Transform2D& transform) const +{ + return mRect.boundingBox(transform); +} + rapidjson::Value RectPath::serialize(rapidjson::Document::AllocatorType& allocator) const { rapidjson::Value result(rapidjson::kObjectType); @@ -84,7 +92,8 @@ RectPath::serialize(rapidjson::Document::AllocatorType& allocator) const { bool RoundedRectPath::empty() const { - return mRoundedRect.empty(); + // A rounded rectangular path always has segments that can be stroked. + return false; } std::string @@ -104,6 +113,13 @@ RoundedRectPath::setRoundedRect(const RoundedRect& roundedRect) return true; } +Rect +RoundedRectPath::boundingBox(const Transform2D& transform) const +{ + // Note: this bounding box could be made tighter if there are large radii and 45 degree rotation + return mRoundedRect.rect().boundingBox(transform); +} + rapidjson::Value RoundedRectPath::serialize(rapidjson::Document::AllocatorType& allocator) const { rapidjson::Value result(rapidjson::kObjectType); @@ -116,14 +132,15 @@ RoundedRectPath::serialize(rapidjson::Document::AllocatorType& allocator) const bool FramePath::empty() const { - return mRoundedRect.empty() || mInset == 0; + // A frame path always has segments that can be stroked. + return false; } std::string FramePath::toDebugString() const { return std::string("FramePath ") + mRoundedRect.toDebugString() + - " inset=" + std::to_string(mInset); + " inset=" + sutil::to_string(mInset); } bool @@ -148,6 +165,13 @@ FramePath::setInset(float inset) return true; } +Rect +FramePath::boundingBox(const Transform2D& transform) const +{ + // Note: this bounding box could be made tighter if there are large radii and 45 degree rotation + return mRoundedRect.rect().boundingBox(transform); +} + rapidjson::Value FramePath::serialize(rapidjson::Document::AllocatorType& allocator) const { rapidjson::Value result(rapidjson::kObjectType); @@ -174,26 +198,21 @@ GeneralPath::toDebugString() const auto result = std::string("GeneralPath ") + mValue + " ["; auto len = mPoints.size(); if (len) - result += std::to_string(mPoints.at(0)); + result += sutil::to_string(mPoints.at(0)); for (int i = 1 ; i < len ; i++) - result += "," + std::to_string(mPoints.at(i)); + result += "," + sutil::to_string(mPoints.at(i)); return result + "]"; } -bool -GeneralPath::setPaths(const std::string value, const std::vector points) +Rect +GeneralPath::boundingBox(const Transform2D& transform) const { - if (mValue == value && mPoints == points) - return false; - - mValue = value; - mPoints = std::move(points); - mModified = true; - return true; + return calculatePathBounds(transform, mValue, mPoints); } rapidjson::Value -GeneralPath::serialize(rapidjson::Document::AllocatorType& allocator) const { +GeneralPath::serialize(rapidjson::Document::AllocatorType& allocator) const +{ rapidjson::Value result(rapidjson::kObjectType); result.AddMember("type", rapidjson::StringRef("generalPath"), allocator); result.AddMember("values", rapidjson::Value(mValue.c_str(), allocator), allocator); diff --git a/aplcore/src/scenegraph/pathbounds.cpp b/aplcore/src/scenegraph/pathbounds.cpp new file mode 100644 index 0000000..550f814 --- /dev/null +++ b/aplcore/src/scenegraph/pathbounds.cpp @@ -0,0 +1,284 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "apl/scenegraph/pathbounds.h" + +namespace apl { +namespace sg { + +/** + * A support class to hold the bounding rectangle. As points are added to the + * class, the rectangle expands to hold the new point. + */ +class ExpandingRect { +public: + void add(float x, float y) { + if (!mInitialized) { + mMinX = x; + mMinY = y; + mMaxX = x; + mMaxY = y; + mInitialized = true; + } + else { + mMinX = std::min(x, mMinX); + mMaxX = std::max(x, mMaxX); + mMinY = std::min(y, mMinY); + mMaxY = std::max(y, mMaxY); + } + } + + Rect rect() const { + if (mInitialized) + return {mMinX, mMinY, mMaxX - mMinX, mMaxY - mMinY}; + else + return {}; + } + +private: + float mMinX = 0.0f; + float mMaxX = 0.0f; + float mMinY = 0.0f; + float mMaxY = 0.0f; + bool mInitialized = false; +}; + +/** + * Given the cubic polynomial f(t) = a*(1-t)^3 + 3*b*t*(1-t)^2 + 3*c*t^2*(1-t) + d*t^3, + * calculate the values of t where f'(t) = 0 and 0 < t < 1. Return the number of roots + * found and store the values of those roots in the provided array. + * @param a The starting point, a = f(0) + * @param b The first control point + * @param c The second control point + * @param d The ending point, d = f(1) + * @param root An reference to an array with at least two elements. Return the value of t where + * f'(t) = 0 if it exists and is in the interval (0,1) + * @return The number of valid roots found. May be 0, 1, or 2. + */ +int findCubicZeros(float a, float b, float c, float d, float roots[]) +{ + // Reduce f'(t) down to e*t^2 + 2*f*t + g + auto e = -a + 3*b - 3*c + d; + auto f = a - 2*b + c; + auto g = b - a; + + if (std::abs(e) < 1e-12) { // If e is zero we have a simple linear equation + if (std::abs(f) < 1e-12) // If f is also zero we have straight line + return 0; + + auto t = -g/(2*f); + if (t < 0.0f || t >= 1.0f) + return 0; + roots[0] = t; + return 1; + } + + auto discriminant = f*f - e*g; + if (discriminant < 0.0f) + return 0; // Imaginary roots + auto h = sqrt(discriminant); + auto t1 = (-f + h) / e; + auto t2 = (-f - h) / e; + + int i = 0; + if (t1 > 0 && t1 < 1.0f) + roots[i++] = t1; + if (t2 > 0 && t2 < 1.0f) + roots[i++] = t2; + return i; +} + +/** + * Given the quadratic polynomial f(t) = a*(1-t)^2 + 2*b*t*(1-t) + c*t^2, calculate + * the values of t where f'(t) = 0 and 0 < t < 1. + * @param a The starting point, a = f(0) + * @param b The control point. + * @param c The ending point, c = f(1) + * @param root Return the value of t where f'(t) = 0 if it exists and is in the interval (0,1) + * @return True if a root was found. + */ +bool findQuadraticZero(float a, float b, float c, float& root) +{ + auto d = a - 2 * b + c; + if (std::abs(d) < 1e-12) + return 0; + + root = (a-b)/d; + return root > 0 && root < 1.0f; +} + +/** + * A support class for calculating the bounds of a general path. + */ +class BoundingBox { +public: + /** + * Update the bounding box with a move command. Moving to a new location doesn't + * actually change the bounding box unless it is followed by command that draws a segment. + * @param p An array of the pair x and y. + */ + void addMove(const float p[]) { + mLastAdded = false; + mLastX = p[0]; + mLastY = p[1]; + } + + /** + * Add a line segment to the bounding box. + * @param p An array of the pair x and y + */ + void addLine(const float p[]) { + if (!mLastAdded) { + mRect.add(mLastX, mLastY); + mLastAdded = true; + } + + mRect.add(p[0], p[1]); + mLastX = p[0]; + mLastY = p[1]; + } + + /** + * Add a quadratic Bézier curve to the bounding box. This method adds the starting point and + * ending point. It checks the curve for minimums and maximums of x(t) and y(t) and adds those + * points as well. + * @param p An array of four values: cx1, cy2, x, and y. + */ + void addQuadratic(const float p[]) { + if (!mLastAdded) { + mRect.add(mLastX, mLastY); + mLastAdded = true; + } + + float root; + // Find the minimums and maximums of x(t) + if (findQuadraticZero(mLastX, p[0], p[2], root)) + addQuadraticPoint(p, root); + + // Minimums and maximums of y(t) + if (findQuadraticZero(mLastY, p[1], p[3], root)) + addQuadraticPoint(p, root); + + mRect.add(p[2], p[3]); + mLastX = p[2]; + mLastY = p[3]; + } + + /** + * Add a cubic Bézier curve to the bounding box. This method adds the starting point and + * ending point. It checks the curve for minimums and maximums of x(t) and y(t) and adds those + * points as well. + * @param p An array of six values: cx1, cy1, cx2, cy2, x, and y. + */ + void addCubic(const float p[]) { + if (!mLastAdded) { + mRect.add(mLastX, mLastY); + mLastAdded = true; + } + + float roots[2]; + // Find valid minimums and maximums of x(t) + auto count = findCubicZeros(mLastX, p[0], p[2], p[4], roots); + for (int i = 0 ; i < count ; i++) + addCubicPoint(p, roots[i]); + + // Minimums and maximums of y(t) + count = findCubicZeros(mLastY, p[1], p[3], p[5], roots); + for (int i = 0 ; i < count ; i++) + addCubicPoint(p, roots[i]); + + mRect.add(p[4], p[5]); + mLastX = p[4]; + mLastY = p[5]; + } + + /** + * @return The bounding rectangle. + */ + Rect getRect() const { + return mRect.rect(); + } + +private: + void addQuadraticPoint(const float p[], float t) { + const auto mt = 1 - t; + const auto t1 = mt * mt; + const auto t2 = 2 * t * mt; + const auto t3 = t * t; + mRect.add(mLastX * t1 + p[0] * t2 + p[2] * t3, + mLastY * t1 + p[1] * t2 + p[3] * t3); + } + + void addCubicPoint(const float p[], float t) { + const auto mt = 1 - t; + const auto t1 = mt * mt * mt; + const auto t2 = 3 * t * mt * mt; + const auto t3 = 3 * t * t * mt; + const auto t4 = t * t * t; + mRect.add(mLastX * t1 + p[0] * t2 + p[2] * t3 + p[4] * t4, + mLastY * t1 + p[1] * t2 + p[3] * t3 + p[5] * t4); + } + +private: + ExpandingRect mRect; + float mLastX = 0.0f; + float mLastY = 0.0f; + bool mLastAdded = false; +}; + +Rect +calculatePathBounds(const std::string& commands, const std::vector& points) +{ + auto *ptr = points.data(); + BoundingBox bb; + + for (const auto& m : commands) { + switch (m) { + case 'M': + bb.addMove(ptr); + ptr += 2; + break; + case 'L': + bb.addLine(ptr); + ptr += 2; + break; + case 'Q': + bb.addQuadratic(ptr); + ptr += 4; + break; + case 'C': + bb.addCubic(ptr); + ptr += 6; + break; + default: + // No action required + break; + } + } + + return bb.getRect(); +} + +Rect +calculatePathBounds(const Transform2D& transform, + const std::string& commands, + const std::vector& points) +{ + return calculatePathBounds(commands, transform * points); +} + +} // namespace sg +} // namespace apl diff --git a/aplcore/src/scenegraph/pathop.cpp b/aplcore/src/scenegraph/pathop.cpp index cd0d429..11011bd 100644 --- a/aplcore/src/scenegraph/pathop.cpp +++ b/aplcore/src/scenegraph/pathop.cpp @@ -23,8 +23,6 @@ PathOp::serialize(rapidjson::Document::AllocatorType& allocator) const { auto result = rapidjson::Value(rapidjson::kObjectType); result.AddMember("paint", paint->serialize(allocator), allocator); - if (nextSibling) - result.AddMember("op", nextSibling->serialize(allocator), allocator); return result; } @@ -35,14 +33,14 @@ StrokePathOp::toDebugString() const auto d = std::string(); auto len = dashes.size(); if (len) - d += std::to_string(dashes[0]); + d += sutil::to_string(dashes[0]); // Note: use sutil to ensure locale-independence for (int i = 1 ; i < len ; i++) - d += "," + std::to_string(dashes[i]); + d += "," + sutil::to_string(dashes[i]); - return "Stroke width=" + std::to_string(strokeWidth) + - " miterLimit=" + std::to_string(miterLimit) + - " pathLen=" + std::to_string(pathLength) + - " dashOffset=" + std::to_string(dashOffset) + + return "Stroke width=" + sutil::to_string(strokeWidth) + + " miterLimit=" + sutil::to_string(miterLimit) + + " pathLen=" + sutil::to_string(pathLength) + + " dashOffset=" + sutil::to_string(dashOffset) + " lineCap=" + sGraphicLineCapBimap.at(lineCap) + " lineJoin=" + sGraphicLineJoinBimap.at(lineJoin) + " dashes=[" + d + "]"; diff --git a/aplcore/src/scenegraph/pathparser.cpp b/aplcore/src/scenegraph/pathparser.cpp index d6e798c..6247c2b 100644 --- a/aplcore/src/scenegraph/pathparser.cpp +++ b/aplcore/src/scenegraph/pathparser.cpp @@ -13,11 +13,10 @@ * permissions and limitations under the License. */ -#include #include #include -#include "apl/primitives/point.h" +#include "apl/primitives/transform2d.h" #include "apl/scenegraph/pathparser.h" namespace apl { @@ -64,7 +63,7 @@ class Tokenizer if (peek() != NUMBER) return false; - // TODO: This isn't exactly right because it accepts a hexidecimal values and other invalid SVG path data + // TODO: This isn't exactly right because it accepts hexadecimal values and other invalid SVG path data char *p; result = ::strtof(mPtr, &p); // Note: can I pass &mPtr as the second argument? mPtr = p; @@ -106,10 +105,6 @@ class PathParser explicit PathParser(const char *pathData) : mTokenizer(pathData) {} - bool failed() { - return mError || mTokenizer.peek() != Tokenizer::END; - } - bool isCharacter() { return !mError && mTokenizer.peek() == Tokenizer::CHARACTER; } @@ -138,264 +133,428 @@ class PathParser bool mError = false; }; -std::shared_ptr -parsePathString(const std::string& path) -{ - std::vector points; - std::string commands; +/** + * A MutablePath is a path to which we add pathData strings + * A valid MutablePath is one that has at least one drawn segment. + */ +class MutablePath { +public: + MutablePath() + : mGeneralPath(std::make_shared()), + mPoints(mGeneralPath->mPoints), + mCommands(mGeneralPath->mValue) + {} - PathParser parser(path.c_str()); + void add(const std::string& path); + std::shared_ptr getGeneralPath(); + +private: + void addArc(float radiusX, float radiusY, float degrees, bool largeArcFlag, + bool sweepFlag, float endX, float endY); + +private: + std::shared_ptr mGeneralPath; + + std::vector& mPoints; + std::string& mCommands; // Last point - float last_x = 0; - float last_y = 0; + float mLastX = 0; + float mLastY = 0; // Last control point - float control_x = 0; - float control_y = 0; + float mControlX = 0; + float mControlY = 0; + + char mLastCharacter = 0; + bool mIsDrawn = false; // Set to true once we find something that draws +}; + +std::shared_ptr +MutablePath::getGeneralPath() { + // Return an empty path if nothing was drawn + if (!mIsDrawn) { + mPoints.clear(); + mCommands.erase(); + } + + // Drop any move commands from the end of the command list + while (mCommands.back() == 'M') { + mCommands.resize(mCommands.size() - 1); + mPoints.resize(mPoints.size() - 2); + } + + return mGeneralPath; +} + +void +MutablePath::addArc(float radiusX, float radiusY, float degrees, bool largeArcFlag, + bool sweepFlag, float endX, float endY) +{ + if (mLastX == endX && mLastY == endY) + return; + + auto start = Point(mLastX, mLastY); + auto end = Point(endX, endY); + + // Straight line? + auto rx = std::abs(radiusX); + auto ry = std::abs(radiusY); + if (rx < 1e-12 || ry < 1e-12) { + mPoints.emplace_back(endX); + mPoints.emplace_back(endY); + mCommands.push_back('L'); + mIsDrawn = true; + return; + } + + auto pointTransform = Transform2D::rotate(-degrees); + + // TODO: Is this negative? + auto midPointDistance = Point(0.5f*(mLastX - endX), 0.5f*(mLastY - endY)); + auto transformedMidPoint = pointTransform * midPointDistance; + + float squareRx = rx * rx; + float squareRy = ry * ry; + float squareX = transformedMidPoint.getX() * transformedMidPoint.getX(); + float squareY = transformedMidPoint.getY() * transformedMidPoint.getY(); + + // Check if the radii are big enough to draw the arc, scale radii if not. + // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii + float radiiScale = squareX / squareRx + squareY / squareRy; + if (radiiScale > 1) { + radiiScale = sqrt(radiiScale); + rx *= radiiScale; + ry *= radiiScale; + } + + // Set up a transformation for a unit circle (scale, then rotate) + pointTransform = Transform2D::scale(1/rx, 1/ry) * Transform2D::rotate(-degrees); - char lastCh = 0; - bool isDrawn = false; // Set to true once we find something that draws + auto point1 = pointTransform * start; + auto point2 = pointTransform * end; - auto result = std::make_shared(); + float dx = point2.getX() - point1.getX(); + float dy = point2.getY() - point1.getY(); + + float d = dx * dx + dy * dy; + float scaleFactorSquared = std::max(1 / d - 0.25f, 0.f); + float scaleFactor = sqrt(scaleFactorSquared); + if (largeArcFlag == sweepFlag) + scaleFactor = -scaleFactor; + + dx *= scaleFactor; + dy *= scaleFactor; + + auto centerPoint = Point(0.5f * (point1.getX() + point2.getX()) - dy, + 0.5f * (point1.getY() + point2.getY()) + dx); + + // Calculate the starting and ending angle + float theta1 = atan2f(point1.getY() - centerPoint.getY(), point1.getX() - centerPoint.getX()); + float theta2 = atan2f(point2.getY() - centerPoint.getY(), point2.getX() - centerPoint.getX()); + + float thetaArc = theta2 - theta1; + if (thetaArc < 0 && sweepFlag) { // arcSweep flipped from the original implementation + thetaArc += M_PI * 2; + } else if (thetaArc > 0 && !sweepFlag) { // arcSweep flipped from the original implementation + thetaArc -= M_PI * 2; + } + // Very tiny angles cause our subsequent math to go wonky (skbug.com/9272) + // so we do a quick check here. The precise tolerance amount is just made up. + // PI/million happens to fix the bug in 9272, but a larger value is probably + // ok too. + if (std::abs(thetaArc) < (M_PI / (1000 * 1000))) { + mPoints.emplace_back(endX); + mPoints.emplace_back(endY); + mCommands.push_back('L'); + mIsDrawn = true; + return; + } + + // Configure the transform to go from the unit circle to the rotated ellipse + pointTransform = Transform2D::rotate(degrees) * Transform2D::scale(rx, ry); + + // the arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd + int segments = ceil(std::abs(thetaArc / (2 * M_PI / 3))); // Also has been done as (M_PI_2 + 0.001) + float thetaWidth = thetaArc / segments; + float startThetaSin = sinf(theta1); + float startThetaCos = cosf(theta1); + float t = (4.f/3.f) * tanf(0.25f * thetaWidth); // Used for the Cubic's control points + if (!std::isfinite(t)) + return; + + for (int i = 0 ; i < segments ; i++) { + float endTheta = theta1 + (i + 1) * thetaWidth; + + float endThetaSin = sinf(endTheta); + float endThetaCos = cosf(endTheta); + + auto cp1 = pointTransform * Point( startThetaCos - t * startThetaSin + centerPoint.getX(), + startThetaSin + t * startThetaCos + centerPoint.getY()); + auto cp2 = pointTransform * Point( endThetaCos + t * endThetaSin + centerPoint.getX(), + endThetaSin - t * endThetaCos + centerPoint.getY()); + auto xy = pointTransform * Point( endThetaCos + centerPoint.getX(), + endThetaSin + centerPoint.getY()); + + mPoints.insert(mPoints.end(), { cp1.getX(), cp1.getY(), cp2.getX(), cp2.getY(), xy.getX(), xy.getY()}); + mCommands.push_back('C'); + mControlX = 2 * xy.getX() - cp2.getX(); // Set this because we could be followed by an 'S' or 's' + mControlY = 2 * xy.getY() - cp2.getY(); + mIsDrawn = true; + + startThetaSin = endThetaSin; + startThetaCos = endThetaCos; + } +} + +void +MutablePath::add(const std::string& path) +{ + PathParser parser(path.c_str()); while (parser.isCharacter()) { auto ch = parser.matchCharacter(); switch (ch) { - case 'M': // Absolute MoveTo + case 'M': // Absolute MoveTo do { - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('M'); + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + if (!mCommands.empty() && mCommands.back() == 'M') // Last was move + mPoints.resize(mPoints.size() - 2); // Drop the points from the last move + else + mCommands.push_back('M'); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); } while (parser.isNumber()); break; - case 'm': // Relative MoveTo -> convert to absolute move to + case 'm': // Relative MoveTo -> convert to absolute move to do { - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('M'); + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + if (!mCommands.empty() && mCommands.back() == 'M') // Last was move + mPoints.resize(mPoints.size() - 2); // Drop the points from the last move + else + mCommands.push_back('M'); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); } while (parser.isNumber()); break; - case 'L': // Absolute LineTo + case 'L': // Absolute LineTo do { - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'l': // Relative LineTo -> convert to absolute line to + case 'l': // Relative LineTo -> convert to absolute line to do { - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'H': // Absolute Horizontal Line + case 'H': // Absolute Horizontal Line do { - last_x = parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastX = parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'h': // Relative Horizontal Line + case 'h': // Relative Horizontal Line do { - last_x += parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastX += parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'V': // Absolute Vertical Line + case 'V': // Absolute Vertical Line do { - last_y = parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastY = parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'v': // Relative Vertical Line + case 'v': // Relative Vertical Line do { - last_y += parser.matchNumber(); - points.emplace_back(last_x); - points.emplace_back(last_y); - commands.push_back('L'); - isDrawn = true; + mLastY += parser.matchNumber(); + mPoints.emplace_back(mLastX); + mPoints.emplace_back(mLastY); + mCommands.push_back('L'); + mIsDrawn = true; } while (parser.isNumber()); break; - case 'C': // Absolute Bezier cubic curve + case 'C': // Absolute Bezier cubic curve do { float x1 = parser.matchNumber(); float y1 = parser.matchNumber(); float x2 = parser.matchNumber(); float y2 = parser.matchNumber(); - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.insert(points.end(), {x1, y1, x2, y2, last_x, last_y}); - commands.push_back('C'); - control_x = 2 * last_x - x2; - control_y = 2 * last_y - y2; - isDrawn = true; + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + mPoints.insert(mPoints.end(), {x1, y1, x2, y2, mLastX, mLastY}); + mCommands.push_back('C'); + mControlX = 2 * mLastX - x2; + mControlY = 2 * mLastY - y2; + mIsDrawn = true; } while (parser.isNumber()); break; - case 'c': // Relative Bezier curve + case 'c': // Relative Bezier curve do { - float x1 = parser.matchNumber() + last_x; - float y1 = parser.matchNumber() + last_y; - float x2 = parser.matchNumber() + last_x; - float y2 = parser.matchNumber() + last_y; - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.insert(points.end(), {x1, y1, x2, y2, last_x, last_y}); - commands.push_back('C'); - control_x = 2 * last_x - x2; - control_y = 2 * last_y - y2; - isDrawn = true; + float x1 = parser.matchNumber() + mLastX; + float y1 = parser.matchNumber() + mLastY; + float x2 = parser.matchNumber() + mLastX; + float y2 = parser.matchNumber() + mLastY; + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + mPoints.insert(mPoints.end(), {x1, y1, x2, y2, mLastX, mLastY}); + mCommands.push_back('C'); + mControlX = 2 * mLastX - x2; + mControlY = 2 * mLastY - y2; + mIsDrawn = true; } while (parser.isNumber()); break; - case 'S': // Smooth Bezier curve - if (!std::strchr("CcSs", lastCh)) { - control_x = last_x; - control_y = last_y; + case 'S': // Smooth Bezier curve + if (!std::strchr("CcSs", mLastCharacter)) { + mControlX = mLastX; + mControlY = mLastY; } do { float x2 = parser.matchNumber(); float y2 = parser.matchNumber(); - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.insert(points.end(), {control_x, control_y, x2, y2, last_x, last_y}); - commands.push_back('C'); - control_x = 2 * last_x - x2; - control_y = 2 * last_y - y2; - isDrawn = true; + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + mPoints.insert(mPoints.end(), {mControlX, mControlY, x2, y2, mLastX, mLastY}); + mCommands.push_back('C'); + mControlX = 2 * mLastX - x2; + mControlY = 2 * mLastY - y2; + mIsDrawn = true; } while (parser.isNumber()); break; case 's': - if (!std::strchr("CcSs", lastCh)) { - control_x = last_x; - control_y = last_y; + if (!std::strchr("CcSs", mLastCharacter)) { + mControlX = mLastX; + mControlY = mLastY; } do { - float x2 = parser.matchNumber() + last_x; - float y2 = parser.matchNumber() + last_y; - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.insert(points.end(), {control_x, control_y, x2, y2, last_x, last_y}); - commands.push_back('C'); - control_x = 2 * last_x - x2; - control_y = 2 * last_y - y2; - isDrawn = true; + float x2 = parser.matchNumber() + mLastX; + float y2 = parser.matchNumber() + mLastY; + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + mPoints.insert(mPoints.end(), {mControlX, mControlY, x2, y2, mLastX, mLastY}); + mCommands.push_back('C'); + mControlX = 2 * mLastX - x2; + mControlY = 2 * mLastY - y2; + mIsDrawn = true; } while (parser.isNumber()); break; - case 'Q': // Quadratic Bezier curve + case 'Q': // Quadratic Bezier curve do { float x1 = parser.matchNumber(); float y1 = parser.matchNumber(); - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.insert(points.end(), {x1, y1, last_x, last_y}); - commands.push_back('Q'); - control_x = 2 * last_x - x1; - control_y = 2 * last_y - y1; - isDrawn = true; + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + mPoints.insert(mPoints.end(), {x1, y1, mLastX, mLastY}); + mCommands.push_back('Q'); + mControlX = 2 * mLastX - x1; + mControlY = 2 * mLastY - y1; + mIsDrawn = true; } while (parser.isNumber()); break; case 'q': do { - float x1 = parser.matchNumber() + last_x; - float y1 = parser.matchNumber() + last_y; - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.insert(points.end(), {x1, y1, last_x, last_y}); - commands.push_back('Q'); - control_x = 2 * last_x - x1; - control_y = 2 * last_y - y1; - isDrawn = true; + float x1 = parser.matchNumber() + mLastX; + float y1 = parser.matchNumber() + mLastY; + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + mPoints.insert(mPoints.end(), {x1, y1, mLastX, mLastY}); + mCommands.push_back('Q'); + mControlX = 2 * mLastX - x1; + mControlY = 2 * mLastY - y1; + mIsDrawn = true; } while (parser.isNumber()); break; - case 'T': // Smooth Quadratic Bezier curve - if (!std::strchr("QqTt", lastCh)) { - control_x = last_x; - control_y = last_y; + case 'T': // Smooth Quadratic Bezier curve + if (!std::strchr("QqTt", mLastCharacter)) { + mControlX = mLastX; + mControlY = mLastY; } do { - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.insert(points.end(), {control_x, control_y, last_x, last_y}); - commands.push_back('Q'); - control_x = 2 * last_x - control_x; - control_y = 2 * last_y - control_y; - isDrawn = true; + mLastX = parser.matchNumber(); + mLastY = parser.matchNumber(); + mPoints.insert(mPoints.end(), {mControlX, mControlY, mLastX, mLastY}); + mCommands.push_back('Q'); + mControlX = 2 * mLastX - mControlX; + mControlY = 2 * mLastY - mControlY; + mIsDrawn = true; } while (parser.isNumber()); break; case 't': - if (!std::strchr("QqTt", lastCh)) { - control_x = last_x; - control_y = last_y; + if (!std::strchr("QqTt", mLastCharacter)) { + mControlX = mLastX; + mControlY = mLastY; } do { - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.insert(points.end(), {control_x, control_y, last_x, last_y}); - commands.push_back('Q'); - control_x = 2 * last_x - control_x; - control_y = 2 * last_y - control_y; - isDrawn = true; + mLastX += parser.matchNumber(); + mLastY += parser.matchNumber(); + mPoints.insert(mPoints.end(), {mControlX, mControlY, mLastX, mLastY}); + mCommands.push_back('Q'); + mControlX = 2 * mLastX - mControlX; + mControlY = 2 * mLastY - mControlY; + mIsDrawn = true; } while (parser.isNumber()); break; - case 'A': // Elliptical arc + case 'A': // Elliptical arc do { float rx = parser.matchNumber(); float ry = parser.matchNumber(); float x_axis_rotation = parser.matchNumber(); - float large_arc_flag = parser.matchNumber(); - float sweep_flag = parser.matchNumber(); - last_x = parser.matchNumber(); - last_y = parser.matchNumber(); - points.insert(points.end(), {rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, last_x, last_y}); - commands.push_back('A'); - isDrawn = true; + bool large_arc_flag = parser.matchNumber() != 0.0f; + bool sweep_flag = parser.matchNumber() != 0.0f; + float endX = parser.matchNumber(); + float endY = parser.matchNumber(); + addArc(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, endX, endY); + mLastX = endX; + mLastY = endY; } while (parser.isNumber()); break; @@ -405,34 +564,40 @@ parsePathString(const std::string& path) float rx = parser.matchNumber(); float ry = parser.matchNumber(); float x_axis_rotation = parser.matchNumber(); - float large_arc_flag = parser.matchNumber(); - float sweep_flag = parser.matchNumber(); - last_x += parser.matchNumber(); - last_y += parser.matchNumber(); - points.insert(points.end(), {rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, last_x, last_y}); - commands.push_back('A'); - isDrawn = true; + bool large_arc_flag = parser.matchNumber() != 0.0f; + bool sweep_flag = parser.matchNumber() != 0.0f; + float endX = mLastX + parser.matchNumber(); + float endY = mLastY + parser.matchNumber(); + addArc(rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, endX, endY); + mLastX = endX; + mLastY = endY; } while (parser.isNumber()); break; - case 'Z': // Close the current sub-path + case 'Z': // Close the current sub-path case 'z': - commands.push_back('Z'); + if (!mCommands.empty() && mCommands.back() != 'Z') + mCommands.push_back('Z'); break; default: - printf("Error - unrecognized character %d\n", ch); - return result; + // Illegal character; drop all drawing changes + mCommands.erase(); + mPoints.clear(); + return; } - lastCh = ch; + mLastCharacter = ch; } +} - if (!parser.failed() && isDrawn) - result->setPaths(std::move(commands), std::move(points)); - - return result; +std::shared_ptr +parsePathString(const std::string& path) +{ + MutablePath mp; + mp.add(path); + return mp.getGeneralPath(); } } // namespace sg -} // namespace apl \ No newline at end of file +} // namespace apl diff --git a/aplcore/src/scenegraph/utilities.cpp b/aplcore/src/scenegraph/utilities.cpp index e0e0df2..0adcc21 100644 --- a/aplcore/src/scenegraph/utilities.cpp +++ b/aplcore/src/scenegraph/utilities.cpp @@ -20,8 +20,7 @@ #include "apl/scenegraph/textproperties.h" #include "apl/utils/session.h" -#include -#include +#include "apl/datagrammar/grammarpolyfill.h" namespace apl { namespace sg { @@ -56,7 +55,8 @@ struct action : pegtl::nothing< Rule > { }; -struct split_state { +struct split_state : fail_state +{ std::vector strings; std::string working; }; @@ -102,13 +102,9 @@ std::vector splitFontString(const RootConfig& rootConfig, const std::string& text) { grammar::split_state state; - - try { - pegtl::string_input<> in(text, ""); - pegtl::parse(in, state); - } - catch (const pegtl::parse_error& e) { - CONSOLE(rootConfig.getSession()) << "Parse error in '" << text << "' - " << e.what(); + pegtl::string_input<> in(text, ""); + if (!pegtl::parse(in, state) || state.failed) { + CONSOLE(rootConfig.getSession()) << "Parse error in '" << text << "' - " << state.what(); state.strings.clear(); // Throw away any partial data that was parsed } diff --git a/aplcore/src/touch/gestures/pagerflinggesture.cpp b/aplcore/src/touch/gestures/pagerflinggesture.cpp index 88e1083..e39a262 100644 --- a/aplcore/src/touch/gestures/pagerflinggesture.cpp +++ b/aplcore/src/touch/gestures/pagerflinggesture.cpp @@ -46,7 +46,7 @@ static inline float getAnimationDistance(const ActionablePtr& actionable, PageDirection direction, float amount, LayoutDirection layoutDirection) { - auto parentBounds = actionable->getCalculated(kPropertyBounds).getRect(); + const auto& parentBounds = actionable->getCalculated(kPropertyBounds).get(); auto wholeDistance = actionable->isHorizontal() ? parentBounds.getWidth() : parentBounds.getHeight(); auto sign = direction == kPageDirectionForward ? 1.0f : -1.0f; if (actionable->isHorizontal() && layoutDirection == kLayoutDirectionRTL) { @@ -59,7 +59,7 @@ getAnimationDistance(const ActionablePtr& actionable, PageDirection direction, f static inline float getTranslationAmount(const ActionablePtr& actionable, float distance) { - auto parentBounds = actionable->getCalculated(kPropertyBounds).getRect(); + const auto& parentBounds = actionable->getCalculated(kPropertyBounds).get(); return std::abs(distance) / (actionable->isHorizontal() ? parentBounds.getWidth() : parentBounds.getHeight()); } @@ -150,7 +150,7 @@ PagerFlingGesture::onMove(const PointerEvent& event, apl_time_t timestamp) if (!FlingGesture::onMove(event, timestamp)) return false; - auto pager = std::dynamic_pointer_cast(mActionable); + auto pager = PagerComponent::cast(mActionable); auto localPoint = mActionable->toLocalPoint(event.pointerEventPosition); auto distance = getDistance(mActionable, mStartPosition, localPoint); // Flip direction for RTL layout @@ -238,7 +238,7 @@ PagerFlingGesture::animateRemainder(bool fulfill) auto amount = self->mAmount + alpha * remainder; self->mLastAnimationAmount = amount; - std::dynamic_pointer_cast(self->mActionable)->executePageMove(amount); + PagerComponent::cast(self->mActionable)->executePageMove(amount); } }); @@ -261,7 +261,7 @@ PagerFlingGesture::awaitForFinish(const std::weak_ptr& weak_p [weak_ptr, fulfill](ActionRef ref) { auto self = weak_ptr.lock(); if (self) { - std::dynamic_pointer_cast(self->mActionable)->endPageMove(fulfill, ref, false); + PagerComponent::cast(self->mActionable)->endPageMove(fulfill, ref, false); } }); @@ -305,7 +305,7 @@ PagerFlingGesture::finishUp() CONSOLE(mActionable->getContext()) << "Singular transform encountered during page switch. Animation impossible, resetting."; // Reset the state of the component - auto pager = std::dynamic_pointer_cast(mActionable); + auto pager = PagerComponent::cast(mActionable); pager->executePageMove(0.0f); pager->endPageMove(false); // And gesture. diff --git a/aplcore/src/touch/gestures/scrollgesture.cpp b/aplcore/src/touch/gestures/scrollgesture.cpp index fe02905..bc339cc 100644 --- a/aplcore/src/touch/gestures/scrollgesture.cpp +++ b/aplcore/src/touch/gestures/scrollgesture.cpp @@ -140,8 +140,8 @@ ScrollGesture::getVelocityLimit(const Point& travel) { auto maxTravel = scrollable->isVertical() ? mActionable->getContext()->height() : mActionable->getContext()->width(); - auto velocityEasing = scrollable->isVertical() ? rootConfig.getProperty(RootProperty::kScrollFlingVelocityLimitEasingVertical).getEasing() - : rootConfig.getProperty(RootProperty::kScrollFlingVelocityLimitEasingHorizontal).getEasing(); + auto velocityEasing = scrollable->isVertical() ? rootConfig.getProperty(RootProperty::kScrollFlingVelocityLimitEasingVertical).get() + : rootConfig.getProperty(RootProperty::kScrollFlingVelocityLimitEasingHorizontal).get(); auto directionalTravel = std::abs(scrollable->isVertical() ? travel.getY() : travel.getX()); diff --git a/aplcore/src/touch/gestures/swipeawaygesture.cpp b/aplcore/src/touch/gestures/swipeawaygesture.cpp index 74fd2d2..c300a98 100644 --- a/aplcore/src/touch/gestures/swipeawaygesture.cpp +++ b/aplcore/src/touch/gestures/swipeawaygesture.cpp @@ -176,7 +176,7 @@ SwipeAwayGesture::onDown(const PointerEvent& event, apl_time_t timestamp) std::shared_ptr SwipeAwayGesture::getTransformation(bool above) { - auto bounds = mActionable->getCalculated(kPropertyInnerBounds).getRect(); + auto bounds = mActionable->getCalculated(kPropertyInnerBounds).get(); auto from = std::make_shared(); auto to = std::make_shared(); @@ -291,7 +291,7 @@ SwipeAwayGesture::onMove(const PointerEvent& event, apl_time_t timestamp) mSwipeComponent = Builder().inflate(mActionable->getContext(), mItems); - std::dynamic_pointer_cast(mActionable)->injectReplaceComponent(mSwipeComponent, + TouchWrapperComponent::cast(mActionable)->injectReplaceComponent(mSwipeComponent, mAction != SwipeAwayActionType::kSwipeAwayActionReveal); if (mAction == SwipeAwayActionType::kSwipeAwayActionReveal || @@ -337,7 +337,7 @@ SwipeAwayGesture::animateRemainder(bool fulfilled, float velocity) animationDuration = 0; } - std::weak_ptr weak_ptr(std::dynamic_pointer_cast(shared_from_this())); + std::weak_ptr weak_ptr(shared_from_this()); mAnimateAction = Action::makeAnimation(timeManager, animationDuration, [this, weak_ptr, animationDuration, travelPercentage, remainingPercentage](apl_duration_t offset) { auto self = weak_ptr.lock(); @@ -366,7 +366,7 @@ SwipeAwayGesture::animateRemainder(bool fulfilled, float velocity) self->mReplacedComponent->remove(); self->mReplacedComponent->release(); // Need that as absolute positioning does not play well when original child removed - std::dynamic_pointer_cast(self->mActionable)->resetChildPositionType(); + TouchWrapperComponent::cast(self->mActionable)->resetChildPositionType(); auto params = std::make_shared(); params->emplace("direction", sSwipeDirectionMap.at(self->mDirection)); self->mActionable->executeEventHandler("SwipeDone", self->mOnSwipeDone, false, params); diff --git a/aplcore/src/touch/pointermanager.cpp b/aplcore/src/touch/pointermanager.cpp index 1b6e24c..29d17c6 100644 --- a/aplcore/src/touch/pointermanager.cpp +++ b/aplcore/src/touch/pointermanager.cpp @@ -92,7 +92,7 @@ class HitListIterator { private: void advance() { while (mCurrent) { - mCurrent = std::static_pointer_cast(mCurrent->getParent()); + mCurrent = CoreComponent::cast(mCurrent->getParent()); if (mCurrent && mCurrent->isActionable()) { if (mCurrent->isTouchable()) { // Skip this component because we already found a touchable component @@ -163,7 +163,7 @@ PointerManager::handlePointerEvent(const PointerEvent& pointerEvent, apl_time_t // If component claims the event - pointer should be captured by it. if (pointerStatus != kPointerStatusNotCaptured) { if (!pointer->isCaptured()) - pointer->setCapture(std::dynamic_pointer_cast(hitTarget)); + pointer->setCapture(ActionableComponent::cast(hitTarget)); if (pointerStatus == kPointerStatusCaptured) break; } @@ -216,13 +216,13 @@ PointerManager::handlePointerStart(const std::shared_ptr& pointer, if (mActivePointer != nullptr) return nullptr; - auto top = std::dynamic_pointer_cast(mCore.top()); + auto top = CoreComponent::cast(mCore.top()); if (!top) return nullptr; auto visitor = TouchableAtPosition(pointerEvent.pointerEventPosition); top->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + auto target = ActionableComponent::cast(visitor.getResult()); pointer->setTarget(target); pointer->setPosition(pointerEvent.pointerEventPosition); diff --git a/aplcore/src/touch/utils/pagemovehandler.cpp b/aplcore/src/touch/utils/pagemovehandler.cpp index d3a2207..69797ae 100644 --- a/aplcore/src/touch/utils/pagemovehandler.cpp +++ b/aplcore/src/touch/utils/pagemovehandler.cpp @@ -72,7 +72,7 @@ PageMoveHandler::create( } // Go for default handling. - auto pager = std::dynamic_pointer_cast(component); + auto pager = PagerComponent::cast(component); auto layoutDireciton = static_cast(component->getCalculated(kPropertyLayoutDirection).asInt()); bool fromLeft = pageDirection == PageDirection::kPageDirectionForward; // Flip direction for RTL layout @@ -145,7 +145,7 @@ PageMoveHandler::execute(const CoreComponentPtr& component, float amount) } if (mCommands.isNull()) { - auto animationEasing = component->getRootConfig().getProperty(RootProperty::kDefaultPagerAnimationEasing).getEasing(); + auto animationEasing = component->getRootConfig().getProperty(RootProperty::kDefaultPagerAnimationEasing).get(); executeDefaultPagingAnimation(animationEasing->calc(amount), currentPage, targetPage); } else { component->getContext()->sequencer().executeCommands(mCommands, @@ -194,18 +194,20 @@ PageMoveHandler::executeDefaultPagingAnimation( if (mCurrentPageTransform) { mCurrentPageTransform->interpolate(amount); currentChild->setProperty(kPropertyTransformAssigned, Object(mCurrentPageTransform)); + currentChild->markProperty(kPropertyTransformAssigned); } if (mTargetPageTransform) { mTargetPageTransform->interpolate(amount); nextChild->setProperty(kPropertyTransformAssigned, Object(mTargetPageTransform)); + nextChild->markProperty(kPropertyTransformAssigned); } } std::shared_ptr PageMoveHandler::getPageTransformation(const PagerPtr& pager, bool comeIn, bool fromLeft) { - auto bounds = pager->getCalculated(kPropertyInnerBounds).getRect(); + const auto& bounds = pager->getCalculated(kPropertyInnerBounds).get(); auto from = std::make_shared(); auto to = std::make_shared(); diff --git a/aplcore/src/touch/utils/unidirectionaleasingscroller.cpp b/aplcore/src/touch/utils/unidirectionaleasingscroller.cpp index 09b9fdb..3cb80dc 100644 --- a/aplcore/src/touch/utils/unidirectionaleasingscroller.cpp +++ b/aplcore/src/touch/utils/unidirectionaleasingscroller.cpp @@ -49,7 +49,7 @@ UnidirectionalEasingScroller::make( // S = v^2 / 2*a auto distance = (directionalVelocity * directionalVelocity) / (2 * deceleration) * time::MS_PER_SECOND; - auto easing = rootConfig.getProperty(RootProperty::kUEScrollerVelocityEasing).getEasing(); + auto easing = rootConfig.getProperty(RootProperty::kUEScrollerVelocityEasing).get(); auto maxDuration = rootConfig.getProperty(RootProperty::kUEScrollerMaxDuration).getDouble(); auto duration = std::min(static_cast(std::abs(distance/directionalVelocity)), maxDuration); return std::make_shared( @@ -73,7 +73,7 @@ UnidirectionalEasingScroller::make( return nullptr; } - auto easing = scrollable->getRootConfig().getProperty(RootProperty::kUEScrollerDurationEasing).getEasing(); + auto easing = scrollable->getRootConfig().getProperty(RootProperty::kUEScrollerDurationEasing).get(); return std::make_shared(scrollable, easing, std::move(finish), target, duration); } diff --git a/aplcore/src/utils/CMakeLists.txt b/aplcore/src/utils/CMakeLists.txt index a1f6774..eeccc23 100644 --- a/aplcore/src/utils/CMakeLists.txt +++ b/aplcore/src/utils/CMakeLists.txt @@ -21,6 +21,7 @@ target_sources_local(apl stickychildrentree.cpp stickyfunctions.cpp stringfunctions.cpp + throw.cpp tracing.cpp url.cpp ) \ No newline at end of file diff --git a/aplcore/src/utils/log.cpp b/aplcore/src/utils/log.cpp index fa780e3..cf3e348 100644 --- a/aplcore/src/utils/log.cpp +++ b/aplcore/src/utils/log.cpp @@ -109,7 +109,7 @@ Logger::session(const ComponentPtr& component) Logger& Logger::session(const ConstCommandPtr& command) { - return command ? session(std::dynamic_pointer_cast(command)->context()->session()) : *this; + return command ? session(std::static_pointer_cast(command)->context()->session()) : *this; } void diff --git a/aplcore/src/utils/stickychildrentree.cpp b/aplcore/src/utils/stickychildrentree.cpp index 603555b..6185bf4 100644 --- a/aplcore/src/utils/stickychildrentree.cpp +++ b/aplcore/src/utils/stickychildrentree.cpp @@ -101,7 +101,7 @@ StickyChildrenTree::handleChildInsert(const CoreComponentPtr& component) { // the every child of our ancestor scrollables each time we insert a child. Instead of rebuilding // the entire tree we just add the extra nodes and only traverse the children of the inserted child - auto parent = std::dynamic_pointer_cast(component->getParent()); + auto parent = CoreComponent::cast(component->getParent()); // Find the StickyNode we need to change by finding all the ancestors with position: sticky // and their corresponding sticky nodes @@ -117,7 +117,7 @@ StickyChildrenTree::handleChildInsert(const CoreComponentPtr& component) { if (ancestor->getCalculated(kPropertyPosition) == kPositionSticky) parentNodeComponents.push(ancestor); - ancestor = std::dynamic_pointer_cast(ancestor->getParent()); + ancestor = CoreComponent::cast(ancestor->getParent()); } assert(ancestor && ancestor->scrollType() == mScrollable.scrollType()); diff --git a/aplcore/src/utils/stickyfunctions.cpp b/aplcore/src/utils/stickyfunctions.cpp index 02ddc82..841aeac 100644 --- a/aplcore/src/utils/stickyfunctions.cpp +++ b/aplcore/src/utils/stickyfunctions.cpp @@ -22,8 +22,8 @@ namespace apl { namespace stickyfunctions { std::pair -getAncestorHorizontalAndVerticalScrollable(const CoreComponentPtr &component) { - auto ancestor = std::dynamic_pointer_cast(component->getParent()); +getHorizontalAndVerticalScrollable(const CoreComponentPtr &component) { + auto ancestor = component; CoreComponentPtr horizontal, vertical; while (ancestor != nullptr) { if (horizontal != nullptr && vertical != nullptr) @@ -35,18 +35,24 @@ getAncestorHorizontalAndVerticalScrollable(const CoreComponentPtr &component) { if (vertical == nullptr && ancestor->scrollable() && ancestor->scrollType() == kScrollTypeVertical) vertical = ancestor; - ancestor = std::dynamic_pointer_cast(ancestor->getParent()); + ancestor = CoreComponent::cast(ancestor->getParent()); } return {horizontal, vertical}; } +std::pair +getAncestorHorizontalAndVerticalScrollable(const CoreComponentPtr &component) { + auto ancestor = CoreComponent::cast(component->getParent()); + return getHorizontalAndVerticalScrollable(ancestor); +} + void updateStickyOffset(const CoreComponentPtr &component) { auto offset = calculateStickyOffset(component); // The offset for nested sticky components will be calculated in order from ancestor to descendant. // We update the bounds early so any sticky descendants have up to date bounds to work with auto currentStickyOffset = component->getStickyOffset(); - auto b = component->getCalculated(kPropertyBounds).getRect(); + auto b = component->getCalculated(kPropertyBounds).get(); b.offset(-currentStickyOffset); b.offset(offset); component->setCalculated(kPropertyBounds, std::move(b)); @@ -58,7 +64,7 @@ static Point calculateVerticalOffset(const CoreComponentPtr& component, Point& currentStickyOffset, const CoreComponentPtr& verticalScrollable) { Point calculatedOffset; - float scrollableHeight = verticalScrollable->getCalculated(kPropertyBounds).getRect().getHeight(); + float scrollableHeight = verticalScrollable->getCalculated(kPropertyBounds).get().getHeight(); float scrollY = verticalScrollable->scrollPosition().getY(); auto topStyleOffset = component->getCalculated(kPropertyTop); auto bottomStyleOffset = component->getCalculated(kPropertyBottom); @@ -117,7 +123,7 @@ Point calculateHorizontalOffset(const CoreComponentPtr& component, const Point& currentStickyOffset, const CoreComponentPtr& horizontalScrollable) { Point calculatedOffset; - float scrollableWidth = horizontalScrollable->getCalculated(kPropertyBounds).getRect().getWidth(); + float scrollableWidth = horizontalScrollable->getCalculated(kPropertyBounds).get().getWidth(); float scrollX = horizontalScrollable->scrollPosition().getX(); auto leftStyleOffset = component->getCalculated(kPropertyLeft); auto rightStyleOffset = component->getCalculated(kPropertyRight); diff --git a/aplcore/src/utils/stringfunctions.cpp b/aplcore/src/utils/stringfunctions.cpp index 83ee8f4..2be52a2 100644 --- a/aplcore/src/utils/stringfunctions.cpp +++ b/aplcore/src/utils/stringfunctions.cpp @@ -81,6 +81,44 @@ tolower(const std::string& str) return output; } +std::string +doubleToAplFormattedString(double value) +{ + if (value < static_cast(std::numeric_limits::max()) + && value > static_cast(std::numeric_limits::min())) { + auto iValue = static_cast(value); + if (value == iValue) + return std::to_string(iValue); + } + + auto s = sutil::to_string(value); + auto it = s.find_last_not_of('0'); + if (it != s.find(sutil::DECIMAL_POINT)) // Remove a trailing decimal point + it++; + s.erase(it, std::string::npos); + return s; +} + +double +aplFormattedStringToDouble(const std::string& string) +{ + auto len = string.size(); + auto idx = len; + double result = sutil::stod(string, &idx); + // Handle percentages. We skip over whitespace and stop on any other character + while (idx < len) { + auto c = string[idx]; + if (c == '%') { + result *= 0.01; + break; + } + if (!sutil::isspace(c)) + break; + idx++; + } + return result; +} + namespace sutil { // ---- Internal utilities for parsing/formatting floating-point values @@ -315,6 +353,70 @@ stold(const std::string& str, std::size_t* pos) return parseFloatingPointLiteral(str, pos); } +/// Naive implementation of Numeric Conversions [string.conversions] spec. + +struct ErrnoPreserve { + ErrnoPreserve() : mErrno(errno) { errno = 0; } + ~ErrnoPreserve() { if (errno == 0) errno = mErrno; } + int mErrno; +}; + +int +stoi(const std::string& str, std::size_t* pos, int base) +{ + ErrnoPreserve _errnoPreserve; + + int result; + char* tEnd; + const char* cStr = str.c_str(); + // On success, the function returns the converted integral number as a long int value. + // If no valid conversion could be performed, a zero value is returned (0L). + // If the value read is out of the range of representable values by a long int, the function + // returns LONG_MAX or LONG_MIN (defined in ), and errno is set to ERANGE. + // Additionally we account for integer range. + const auto temp = std::strtol(str.c_str(), &tEnd, base); + if (tEnd == cStr || + errno == ERANGE || + (temp < (long)std::numeric_limits::min() || temp > (long)std::numeric_limits::max())) { + return 0; + } + + result = (int)temp; + + if (pos) { + *pos = tEnd - cStr; + } + + return result; +} + +long long +stoll(const std::string& str, std::size_t* pos, int base) +{ + ErrnoPreserve _errnoPreserve; + + char* tEnd; + const char* cStr = str.c_str(); + + // On success, the function returns the converted integral number as a long long int value. + // If no valid conversion could be performed, a zero value is returned (0LL). + // If the value read is out of the range of representable values by a long long int, the + // function returns LLONG_MAX or LLONG_MIN (defined in ), and errno is set to ERANGE. + const auto result = std::strtoll(cStr, &tEnd, base); + if (tEnd == cStr || + errno == ERANGE) { + return 0; + } + + if (pos) { + *pos = tEnd - cStr; + } + + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////////// + std::string to_string(float value) { diff --git a/aplcore/src/utils/throw.cpp b/aplcore/src/utils/throw.cpp new file mode 100644 index 0000000..5dc62e3 --- /dev/null +++ b/aplcore/src/utils/throw.cpp @@ -0,0 +1,29 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include +#include + +#include "apl/utils/throw.h" + +namespace apl { + +void aplThrow(const char* message) noexcept { + perror(message); + std::terminate(); +} + +} + diff --git a/bin/apl-header-inclusion-validation.sh b/bin/apl-header-inclusion-validation.sh index e4475f9..65882f7 100644 --- a/bin/apl-header-inclusion-validation.sh +++ b/bin/apl-header-inclusion-validation.sh @@ -101,6 +101,7 @@ public_apl_headers=( "apl/primitives/object.h" "apl/primitives/objectbag.h" "apl/primitives/objectdata.h" + "apl/primitives/objecttype.h" "apl/primitives/point.h" "apl/primitives/radii.h" "apl/primitives/range.h" @@ -143,6 +144,7 @@ public_apl_headers=( "apl/utils/session.h" "apl/utils/streamer.h" "apl/utils/stringfunctions.h" + "apl/utils/throw.h" "apl/utils/userdata.h" "apl/utils/visitor.h" ) diff --git a/components.cmake b/components.cmake index 43c0ce9..a81783f 100644 --- a/components.cmake +++ b/components.cmake @@ -38,6 +38,13 @@ if (DEBUG_MEMORY_USE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG_MEMORY_USE=1") endif (DEBUG_MEMORY_USE) +#Disable RTTI if requested. Will not work for every case. +if (DISABLE_RTTI) + message("RTTI disabled. Static inclusion may not work for clients using dynamic casting.") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + if(COVERAGE) # We can't really generate core coverage without tests. Option will be applied in clang.cmake as feature is clang # specific. diff --git a/doc/scene_graph.puml b/doc/scene_graph.puml index 309fbaa..b419a6a 100644 --- a/doc/scene_graph.puml +++ b/doc/scene_graph.puml @@ -117,10 +117,6 @@ Node <|-- ShadowNode ShadowNode : shadow : Shadow ShadowNode *-- Shadow -' ====== GenericNode ======= -object GenericNode #A0C4FF -Node <|-- GenericNode - ' ===== DrawNode ======= object DrawNode #A0C4FF Node <|-- DrawNode diff --git a/doc/scenegraph.md b/doc/scenegraph.md index b703bd8..83ab7e8 100644 --- a/doc/scenegraph.md +++ b/doc/scenegraph.md @@ -52,19 +52,22 @@ the layer bridge is deleted when the layer goes out of scope. Layers are intended to be used for caching the results of drawing operations on GPU and accelerating animation effects. Each layer contains the following: +* A **name** used purely for debugging. * The **position** and **size** of the layer. The position is with respect - to its parent. The size is a width and height. + to its parent. The size is a width and height. This information is stored in + rectangular **bounds** of the layer. * A **transformation** to be applied to the layer before drawing. This is used to slide, rotate, scale, and otherwise shift the layer out of its normal position. * The **opacity** of the layer [0-1]. This affects everything within the layer, - including the *content* and *child layers* + including the *content* and *child layers*. * The **content** of the layer. This is a `Node` tree of drawing elements. The - content is drawn before child layers + content is drawn before child layers. * Shadow: * The **outline** of the layer. This may be a rectangle or it may be a rounded - rectangle. The outline is used for drawing the layer shadow. - * A `ShadowPathOp` defining the **shadow** properties of the layer. This may + rectangle. The outline is used for drawing the layer shadow. If not specified, + the **bounds** of the layer are used. + * A `ShadowPtr` defining the **shadow** properties of the layer. This may be null. * Child layers: * An ordered list of **child layers**. These are drawn in order; later layers @@ -74,50 +77,108 @@ GPU and accelerating animation effects. Each layer contains the following: * An optional **child clipping** region. This is a path drawn inside of the layer which clips children (for example, the *Frame* component clips children inside the border). - +* An optional **accessibility** pointer for the layer. This contains: + * The accessibility **label** for the screen reader. + * An accessibility **role**. + * An array of **actions** which can be executed on this layer. + * An accessibility **action callback** to be invoked if one of those actions + is executed. + +### Layer Changed Flags The scene graph contains a set of *changed* layers. A changed layer sets one or more of the following flags: Flag | Meaning -----| ------- -OpacityChanged | The opacity of the layer changed -PositionChanged | The position of the layer changed -SizeChanged | The size of the layer changed -TransformChanged | The transformation of the layer changed -ChildTransformChanged | The child layer transformation changed -RedrawContent | The content of the layer needs to be redrawn -RedrawShadow | The shadow path or outline needs to be redrawn -ChildrenChanged | The list of child layers has changed +`kFlagOpacityChanged` | The opacity of the layer changed +`kFlagPositionChanged` | The position of the layer changed +`kFlagSizeChanged` | The size of the layer changed +`kFlagTransformChanged` | The transformation of the layer changed +`kFlagChildOffsetChanged` | The child offset changed +`kFlagOutlineChanged` | The outline of this layer has been modified +`kFlagRedrawContent` | The content of the layer needs to be redrawn +`kFlagRedrawShadow` | The shadow path or outline needs to be redrawn +`kFlagChildrenChanged` | The list of child layers has changed +`kFlagChildClipChanged` | The clipping path for the children has been modified +`kFlagAccessibilityChanged` | The accessibility object has been modified +`kFlagInteractionChanged` | The interactivity of the layer has changed (see interaction flags) These flags are supplied to the view host and may be used for optimizing rendering. +### Layer Interaction Flags + +Each layer also has a set of **interaction** flags: + +Flag | Meaning +---- | ------- +`kInteractionDisabled` | The layer has been disabled +`kInteractionChecked` | The layer has been checked +`kInteractionPressable` | Supports onPress or onTap +`kInteractionScrollHorizontal` | Can be scrolled horizontally (including normal pagers) +`kInteractionScrollVertical` | Can be scrolled vertically. May include a pager. + +Interaction flags are used by the accessibility system in the view host. + ## Drawing Nodes Drawing nodes are used in the *content* section of a Layer to draw on the screen. + +The basic drawing node has the following properties: +* A **type** used for dynamic casting. +* A pointer to the **first child** of this node. +* A pointer to the **next sibling** of this node. +* A pointer to the **next modified** drawing node. This is the *dirty* list of nodes + that have changed since the last scene graph retrieval. +* A set of boolean **flags**. The two supported flags are: + * `kNodeFlagChildrenChanged`: The sibling child list has been modified + * `kNodeFlagModified`: Some property of this node has changed. + Drawing nodes come in the following types: Node | Children | Description ---- | -------- | ----------- -Generic | Yes | No operation -Transform | Yes | Modify the coordinate system -Clip | Yes | Set a clipping path -Opacity | Yes | Apply an opacity multiplier -Draw | No | A single path and a list of path operations -Text | No | A TextLayout and a list of path operations -Image | No | A Filter (set of one or more images with composition), source, and target rectangles -Video | No | A MediaPlayer, target rectangle, and video scaling rule +`Node::kTransform` | Yes | Modify the coordinate system +`Node::kClip` | Yes | Set a clipping path +`Node::kOpacity` | Yes | Apply an opacity multiplier +`Node::kDraw` | No | A single path and a list of path operations +`Node::kText` | No | A TextLayout and a list of path operations +`Node::kImage` | No | A Filter (set of one or more images with composition), source, and target rectangles +`Node::kVideo` | No | A MediaPlayer, target rectangle, and video scaling rule +`Node::kShadow` | Yes | Apply a shadow to the contained content +`Node::kEditText` | No | An EditTextBox, reference, and configuration information Drawing nodes may be freely changed each time the scene graph is returned; there is no guarantee that the same drawing nodes will be kept each time. However, internally -the TextLayout, Filter, and MediaPlayer objects will be reused. +the TextLayout, Filter, MediaPlayer, AudioPlayer, and EditText objects will be reused. It's possible that the view host will want to cache certain drawing elements such as paths, patterns, gradients, etc. These should be attached to the drawing node using the bridge pattern. -### Algorithm used for *Visible* +The `Node::visible() → Boolean ` method returns *true* if the node draws some content +on the screen. This is a recursive search. + +## The `SceneGraph` object + +The `SceneGraph` object holds the information about the current scene graph. +It contains just two pieces: +1. A pointer to the top *layer* for display +2. A `SceneGraphUpdates` structure which contains: + 1. A set of *layers* which have changed. + 2. A set of *layers* which are new and have been created. + +The new layers are tracked to keep them from getting into the *changed* list. +The view host typically updates the view by iterating over the *changed* list +and updating each layer appropriately, then verifying that the top-level +layer hasn't been modified. A layer with the `kFlagChildrenChanged` bit set +rebuilds its child layers, which means that newly created layers will be added +automatically. + +The normal way for a view host to build a layer is to use an `ensure` method +which changes the `LayerPtr` for an attached view-host data structure. If it +doesn't find an attached view-host data structure, it creates a new one and +attaches it to the layer. -A drawing node is visible if it renders some content on the screen. ### Algorithm used for *RedrawContent* @@ -126,43 +187,12 @@ view host performance. Note that each layer is associated with a single compone in the APL DOM. When that component is marked as dirty, one or more of the dirty changes may affect the content. The algorithm works as follows: -1. Each dirty property that affects content is mapped to a drawing *node*. -2. Values are set on the drawing nodes. If the value that is set changes the - drawing node, a *changed* flag is set on the node. If the value that is set - changes the *children* of the node, then a *childrenChanged* flag is set. -3. Each drawing node that has a flag set is added to a linked list of *changed* or - *childrenChanged* nodes. We use an intrusive list with a terminal to efficiently - determine if the drawing node is already on the list. -4. Once all dirty properties have been processed, we walk the tree to determine if - any of the changes are *visible* and hence require a content redraw. The tree-walk - algorithm is described below. -5. Finally, the linked list is walked and the *changed* and *childrenChanged* flags - are cleared. - -The tree-walk algorithm starts at the top of the content tree and performs a recursive, -depth-first search. It terminates if it finds a *changed* node that is visible. The -algorithm for each node type: - -* **Opacity Node** - * If there are no children and `!childrenChanged` → false - * If `opacity==0 && !changed` → false. - * If `opacity>0 && !changed && !childrenChanged` ask the - children if they need a redraw. - * Otherwise → true. - -* **Text/Draw/Image/Video Node** - * If the paint has no alpha and hasn't changed → false. - * Otherwise, return `changed` - -* **Generic/Transform/Clip** - * If there are no children and `!childrenChanged` → false - * If `changed || childrenChanged` → true - * Otherwise, ask the children if they need a redraw - - -## Basic Principles: - -1. Each scene graph object will only be modified when the `RootContext` calls - `getSceneGraph()` -2. Children of a scene graph element are stored in an intrusive linked list -3. \ No newline at end of file +1. If a component has dirty properties, the `CoreComponent::updateSceneeGraph` + method is invoked on it. +2. The `updateSceneGraph` method checks all of the properties that are commmon + to the *layer* held by that component. Layer-properties (see [Layer Changed Flags]()) + are updated and the appropriate flag is set for the layer. +3. The `updateSceneGraphicInternal → Boolean` virtual method handles the + component-specific updates which includes all drawing content. This method + returns *true* if the layer needs to redraw its content, which sets the + *RedrawContent* flag. diff --git a/extensions/alexaext/CMakeLists.txt b/extensions/alexaext/CMakeLists.txt index f64822b..0a11576 100644 --- a/extensions/alexaext/CMakeLists.txt +++ b/extensions/alexaext/CMakeLists.txt @@ -14,6 +14,10 @@ cmake_minimum_required(VERSION 3.5) set(CMAKE_CXX_STANDARD 11) +# Disable what needs to be disabled +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-exceptions") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") + project(AlexaExt VERSION 1.0.0 diff --git a/extensions/alexaext/include/alexaext/extensionmessage.h b/extensions/alexaext/include/alexaext/extensionmessage.h index 36d58ec..51cb6e4 100644 --- a/extensions/alexaext/include/alexaext/extensionmessage.h +++ b/extensions/alexaext/include/alexaext/extensionmessage.h @@ -650,6 +650,11 @@ class Event : public BaseMessage, public Payload { return *this; } + Event& resourceId(const std::string& resourceId) { + RESOURCE_ID().Set(*mMessage, resourceId.c_str()); + return *this; + } + static const rapidjson::Pointer& NAME() { static const rapidjson::Pointer ptr("/name"); return ptr; @@ -660,6 +665,11 @@ class Event : public BaseMessage, public Payload { return ptr; } + static const rapidjson::Pointer& RESOURCE_ID() { + static const rapidjson::Pointer ptr("/resourceId"); + return ptr; + } + }; // forward declare LiveDataUpdate builder dependencies for file readability diff --git a/extensions/alexaext/include/alexaext/extensionresourceholder.h b/extensions/alexaext/include/alexaext/extensionresourceholder.h index b631287..94808eb 100644 --- a/extensions/alexaext/include/alexaext/extensionresourceholder.h +++ b/extensions/alexaext/include/alexaext/extensionresourceholder.h @@ -16,6 +16,7 @@ #ifndef APL_EXTENSION_RESOURCE_HOLDER_H #define APL_EXTENSION_RESOURCE_HOLDER_H +#include #include namespace alexaext { diff --git a/extensions/alexaext/include/alexaext/extensionresourceprovider.h b/extensions/alexaext/include/alexaext/extensionresourceprovider.h index 42cef3b..d3fecd8 100644 --- a/extensions/alexaext/include/alexaext/extensionresourceprovider.h +++ b/extensions/alexaext/include/alexaext/extensionresourceprovider.h @@ -16,6 +16,10 @@ #ifndef APL_EXTENSION_RESOURCE_PROVIDER_H #define APL_EXTENSION_RESOURCE_PROVIDER_H +#include +#include +#include + #include "extensionresourceholder.h" namespace alexaext { diff --git a/extensions/alexaext/include/alexaext/extensionschema.h b/extensions/alexaext/include/alexaext/extensionschema.h index d03390d..194d832 100644 --- a/extensions/alexaext/include/alexaext/extensionschema.h +++ b/extensions/alexaext/include/alexaext/extensionschema.h @@ -147,6 +147,8 @@ class CommandSchema; class LiveDataSchema; +class ComponentSchema; + /** * Extension Schema builder. * The extension schema defines the extension api that is exposed to the execution environment. @@ -164,6 +166,7 @@ class ExtensionSchema : public SchemaBuilder { TYPES().Set(*mValue, rapidjson::Value(rapidjson::kArrayType), *mAllocator); COMMANDS().Set(*mValue, rapidjson::Value(rapidjson::kArrayType), *mAllocator); LIVE_DATA().Set(*mValue, rapidjson::Value(rapidjson::kArrayType), *mAllocator); + COMPONENTS().Set(*mValue, rapidjson::Value(rapidjson::kArrayType), *mAllocator); } static const rapidjson::Pointer& TYPE() { @@ -266,6 +269,20 @@ class ExtensionSchema : public SchemaBuilder { static const rapidjson::Pointer ptr("/liveData"); return ptr; } + + /** + * Add a extension component definition. + */ + ExtensionSchema& component(const std::string& name, + const std::function& builder = nullptr) { + factoryPush(COMPONENTS(), builder, name); + return *this; + } + + static const rapidjson::Pointer& COMPONENTS() { + static const rapidjson::Pointer ptr("/components"); + return ptr; + } }; // forward declare TypeSchema builder dependencies for file readability @@ -391,12 +408,12 @@ class EventSchema : public SchemaBuilder { } EventSchema fastMode(bool fastMode) { - FAST_MODE().Set(*mValue, fastMode, *mAllocator); + FAST_MODE().Set(*mValue, fastMode ? "FAST" : "NORMAL", *mAllocator); return *this; } static const rapidjson::Pointer& FAST_MODE() { - static const rapidjson::Pointer ptr("/fastMode"); + static const rapidjson::Pointer ptr("/mode"); return ptr; } }; @@ -582,6 +599,66 @@ class EventHandlerPropertySchema : public SchemaBuilder { } }; +class ComponentSchema : public SchemaBuilder { +public: + explicit ComponentSchema(rapidjson::Document::AllocatorType* allocator, + const std::string& name) + : SchemaBuilder(allocator) { + NAME().Set(*mValue, name.c_str(), *mAllocator); + PROPERTIES().Set(*mValue, rapidjson::Value(rapidjson::kObjectType), *mAllocator); + EVENTS().Set(*mValue, rapidjson::Value(rapidjson::kArrayType), *mAllocator); + } + + ComponentSchema& resourceType(const std::string& resourceType) { + RESOURCE_TYPE().Set(*mValue, resourceType.c_str(), *mAllocator); + return *this; + } + + ComponentSchema& context(const std::string& context) { + CONTEXT().Set(*mValue, context.c_str(), *mAllocator); + return *this; + } + + ComponentSchema& event(const std::string& name, + const std::function& builder = nullptr) { + factoryPush(EVENTS(), builder, name); + return *this; + } + + ComponentSchema& property(const std::string& name, const std::string& type) { + PROPERTIES().Get(*mValue)->AddMember(rapidjson::Value(name.c_str(), *mAllocator), + rapidjson::Value(type.c_str(), *mAllocator), *mAllocator); + return *this; + } + + ComponentSchema& property(const std::string& name, + const std::function& builder = nullptr) { + factoryAdd(PROPERTIES(), name, builder); + return *this; + } + + + static const rapidjson::Pointer& RESOURCE_TYPE() { + static const rapidjson::Pointer ptr("/resourceType"); + return ptr; + } + + static const rapidjson::Pointer& CONTEXT() { + static const rapidjson::Pointer ptr("/context"); + return ptr; + } + + static const rapidjson::Pointer& EVENTS() { + static const rapidjson::Pointer ptr("/events"); + return ptr; + } + + static const rapidjson::Pointer& PROPERTIES() { + static const rapidjson::Pointer ptr("/properties"); + return ptr; + } +}; + } // namespace alexaext #endif // ALEXAEXT_EXTENSION_SCHEMA_H diff --git a/extensions/alexaext/src/APLAudioPlayerExtension/AplAudioPlayerExtension.cpp b/extensions/alexaext/src/APLAudioPlayerExtension/AplAudioPlayerExtension.cpp index 4cc7ecc..91e2753 100644 --- a/extensions/alexaext/src/APLAudioPlayerExtension/AplAudioPlayerExtension.cpp +++ b/extensions/alexaext/src/APLAudioPlayerExtension/AplAudioPlayerExtension.cpp @@ -275,6 +275,7 @@ AplAudioPlayerExtension::invokeCommand(const std::string &uri, const rapidjson:: auto offset = GetWithDefault(PROPERTY_OFFSET, *params, -1); if (offset < 0) return false; + updatePlaybackProgress(offset); mObserver->onAudioPlayerSeekToPosition(offset); return true; } @@ -389,8 +390,6 @@ AplAudioPlayerExtension::updatePlayerActivity(const std::string &state, int offs if (std::find(PLAYER_ACTIVITY.begin(), PLAYER_ACTIVITY.end(), state) == PLAYER_ACTIVITY.end()) { return; } - if (offset < 100) - return; mPlaybackStateActivity = state; mPlaybackStateOffset = offset; diff --git a/extensions/alexaext/src/localextensionproxy.cpp b/extensions/alexaext/src/localextensionproxy.cpp index 065dd50..d4207d3 100644 --- a/extensions/alexaext/src/localextensionproxy.cpp +++ b/extensions/alexaext/src/localextensionproxy.cpp @@ -67,16 +67,7 @@ LocalExtensionProxy::getRegistrationInternal(const std::string& uri, } // request the schema from the extension - rapidjson::Document registration; - try { - registration = processRegistration(registrationRequest); - } catch (const std::exception& e) { - errorCode = kErrorExtensionException; - errorMsg = e.what(); - } catch (...) { - errorCode = kErrorException; - errorMsg = sErrorMessage[kErrorException]; - } + auto registration = processRegistration(registrationRequest); // failed schema creation notify failure callback if (registration.IsNull() || registration.HasParseError()) { @@ -256,16 +247,7 @@ LocalExtensionProxy::invokeCommandInternal(const std::string& uri, // invoke the extension command int errorCode = kErrorNone; std::string errorMsg; - bool result = false; - try { - result = processCommand(command); - } catch (const std::exception& e) { - errorCode = kErrorExtensionException; - errorMsg = e.what(); - } catch (...) { - errorCode = kErrorException; - errorMsg = sErrorMessage[kErrorException]; - } + auto result = processCommand(command); // failed command invocation if (!result) { diff --git a/extensions/unit/unittest_apl_audio_player.cpp b/extensions/unit/unittest_apl_audio_player.cpp index 35eacc6..3ecdb82 100644 --- a/extensions/unit/unittest_apl_audio_player.cpp +++ b/extensions/unit/unittest_apl_audio_player.cpp @@ -543,7 +543,22 @@ TEST_F(AplAudioPlayerExtensionTest, InvokeCommandSeekToPositionSuccess) .uri("aplext:audioplayer:10") .name("SeekToPosition") .property("offset", 42); + bool gotUpdate = false; + mExtension->registerLiveDataUpdateCallback( + [&](const std::string &uri, const rapidjson::Value &liveDataUpdate) { + gotUpdate = true; + ASSERT_STREQ("LiveDataUpdate", + GetWithDefault(RegistrationSuccess::METHOD(), liveDataUpdate, "")); + ASSERT_STREQ("aplext:audioplayer:10", + GetWithDefault(RegistrationSuccess::TARGET(), liveDataUpdate, "")); + const Value *ops = LiveDataUpdate::OPERATIONS().Get(liveDataUpdate); + ASSERT_TRUE(ops); + ASSERT_TRUE(ops->IsArray() && ops->Size() == 2); + ASSERT_TRUE(CheckLiveData(ops->GetArray()[1], "Set", "offset", 42)); + }); auto invoke = mExtension->invokeCommand("aplext:audioplayer:10", command); + + ASSERT_TRUE(gotUpdate); ASSERT_TRUE(invoke); ASSERT_EQ("SEEK", mObserver->mCommand); ASSERT_EQ(42, mObserver->mParaNum); @@ -1043,5 +1058,28 @@ TEST_F(AplAudioPlayerExtensionTest, UpdatePlayerActivityFailure) mExtension->updatePlayerActivity("", 100); ASSERT_FALSE(gotUpdate); mExtension->updatePlayerActivity("PAUSED", -100); - ASSERT_FALSE(gotUpdate); + ASSERT_TRUE(gotUpdate); +} + +/** + * Validates changing state always triggers an update. + */ +TEST_F(AplAudioPlayerExtensionTest, UpdatePlayerActivityStateChange) +{ + ASSERT_TRUE(registerExtension()); + + int updateCount = 0; + mExtension->registerLiveDataUpdateCallback( + [&](const std::string &uri, const rapidjson::Value &liveDataUpdate) { + updateCount++; + }); + + mExtension->updatePlayerActivity("PAUSED", 0); + ASSERT_EQ(1, updateCount); + mExtension->updatePlayerActivity("PLAYING", 0); + ASSERT_EQ(2, updateCount); + mExtension->updatePlayerActivity("PLAYING", 50); + ASSERT_EQ(3, updateCount); + mExtension->updatePlaybackProgress(100); + ASSERT_EQ(4, updateCount); } \ No newline at end of file diff --git a/extensions/unit/unittest_extension_message.cpp b/extensions/unit/unittest_extension_message.cpp index d028b2b..ae7e433 100644 --- a/extensions/unit/unittest_extension_message.cpp +++ b/extensions/unit/unittest_extension_message.cpp @@ -500,6 +500,7 @@ static const char* EVENT_MESSAGE = R"( "uri": "alexaext:test:10", "target": "alexaext:test:10", "name": "myEvent", + "resourceId": "myResource", "payload": { "key1": 1, "key2": true, @@ -522,6 +523,7 @@ TEST_F(ExtensionMessageTest, Event) { Document rhsDoc = Event("1.2.3").uri(URI) .name("myEvent") + .resourceId("myResource") .property("key1", 1) .property("key2", true) .property("key3", "three") diff --git a/extensions/unit/unittest_extension_provider.cpp b/extensions/unit/unittest_extension_provider.cpp index 6d44c52..8ca6554 100644 --- a/extensions/unit/unittest_extension_provider.cpp +++ b/extensions/unit/unittest_extension_provider.cpp @@ -31,7 +31,7 @@ class SimpleExtension final : public ExtensionBase { bool invokeCommand(const std::string& uri, const rapidjson::Value& command) override { if (getURIs().count("aplext:ugly:1")) { - throw ExtensionException::create("ugly %s %s", "exception", "error"); + return false; } auto name = Command::NAME().Get(command); return (name && *name != "nope"); @@ -39,9 +39,7 @@ class SimpleExtension final : public ExtensionBase { rapidjson::Document createRegistration(const std::string& uri, const rapidjson::Value& registerRequest) override { - if (uri == "aplext:ugly:1") { - throw ExtensionException::create("ugly %s %s", "exception", "error"); - } else if (uri == "aplext:ugly:2") { + if (uri == "aplext:ugly:2") { return RegistrationFailure("1.0").uri("aplext:ugly:2").errorCode(13).errorMessage("total failure"); } @@ -260,33 +258,6 @@ TEST_F(ExtensionProviderTest, RegistrationFailure) { ASSERT_TRUE(gotfailure); } -/** - * Test registration exception - */ -TEST_F(ExtensionProviderTest, RegistrationException) { - ASSERT_TRUE(extPro->hasExtension("aplext:ugly:1")); - auto ugly = extPro->getExtension("aplext:ugly:1"); - ASSERT_TRUE(ugly); - - document.Parse(SETTINGS); - Document req = RegistrationRequest("1.0").uri("aplext:ugly:1").settings(document); - - bool gotfailure = false; - auto invoke = ugly->getRegistration( - "aplext:ugly:1", req, - nullptr, [this, &gotfailure](const std::string& uri, const rapidjson::Value& registerFailure) { - gotfailure = true; - ASSERT_EQ("aplext:ugly:1", uri); - AssertMessage(uri, "RegisterFailure", registerFailure); - ASSERT_EQ(kErrorExtensionException, - GetWithDefault(RegistrationFailure::CODE(), registerFailure, -1)); - ASSERT_STREQ("ugly exception error", - GetWithDefault(RegistrationFailure::MESSAGE(), registerFailure, "")); - }); - ASSERT_FALSE(invoke); - ASSERT_TRUE(gotfailure); -} - /** * Test registration failure from extension. */ @@ -478,37 +449,6 @@ TEST_F(ExtensionProviderTest, InvokeCommandFailure) { ASSERT_TRUE(gotfailure); } -/** - * Test invoke event handler. This is a message from extension to doc. - */ -TEST_F(ExtensionProviderTest, InvokeCommandException) { - - int id = 31; - Document command = Command("1.0").uri("aplext:ugly:1").name("ugly").id(id).property("prop1", Value(1).Move()); - - // the extension was registered - ASSERT_TRUE(extPro->hasExtension("aplext:ugly:1")); - auto foo = extPro->getExtension("aplext:ugly:1"); - ASSERT_TRUE(foo); - - // test failure callback - bool gotfailure = false; - bool invoke = foo->invokeCommand( - "aplext:ugly:1", command, - nullptr, - [this, &gotfailure, &id](const std::string& uri, const rapidjson::Value& commandFailure) { - gotfailure = true; - ASSERT_EQ("aplext:ugly:1", uri); - AssertMessage(uri, "CommandFailure", commandFailure); - ASSERT_EQ(id, GetWithDefault(Command::ID(), commandFailure, -1)); - ASSERT_EQ(kErrorExtensionException, GetWithDefault(CommandFailure::CODE(), commandFailure, -1)); - ASSERT_STREQ("ugly exception error", - GetWithDefault(CommandFailure::MESSAGE(), commandFailure, "")); - }); - ASSERT_FALSE(invoke); - ASSERT_TRUE(gotfailure); -} - /** * Test successful receipt of event generated by extension. */ diff --git a/extensions/unit/unittest_extension_schema.cpp b/extensions/unit/unittest_extension_schema.cpp index 2e20480..96f1d4a 100644 --- a/extensions/unit/unittest_extension_schema.cpp +++ b/extensions/unit/unittest_extension_schema.cpp @@ -61,6 +61,7 @@ static const char* SCHEMA = R"( "types": [], "commands": [], "liveData": [], + "components": [], "uri": "alexaext:test:10" } )"; @@ -174,11 +175,11 @@ static const char* EVENTS = R"( }, { "name": "myEventTwo", - "fastMode": false + "mode": "NORMAL" }, { "name": "myEventThree", - "fastMode": true + "mode": "FAST" } ] } @@ -386,3 +387,93 @@ TEST_F(ExtensionSchemaTest, SchemaLiveData) { Value* actual = ExtensionSchema::LIVE_DATA().Get(rhsValue); ASSERT_TRUE(IsEqual(*expected, *actual)); } + + +// Extension Schema Component Structure +static const char* COMPONENTS = R"( +{ + "components": [ + { + "name": "ComponentA", + "properties": {}, + "events": [] + }, + { + "name": "ComponentB", + "properties": {}, + "events": [], + "context": "myContext", + "resourceType": "SURFACE" + }, + { + "name": "ComponentC", + "properties": { + "PropA": "number", + "PropB": { + "required": true, + "description": "My Property", + "type": "string", + "default": "PropBValue" + } + }, + "events": [ + { + "name": "EventA" + }, + { + "name": "EventB", + "mode": "NORMAL" + } + ], + "context": "OtherContext", + "resourceType": "WINDOW" + } + ] +} +)"; + +TEST_F(ExtensionSchemaTest, SchemaComponent) { + + ExtensionSchema schemaMsg(&testDocument.GetAllocator(), "1.0"); + + // add events + schemaMsg.uri(URI) + .component("ComponentA") + .component("ComponentB", + [](ComponentSchema& componentSchema) { + componentSchema + .context("myContext") + .resourceType("SURFACE"); + }) + .component("ComponentC", + [](ComponentSchema& componentSchema) { + componentSchema + .context("OtherContext") + .resourceType("WINDOW") + .event("EventA") + .event("EventB", [](EventSchema& eventSchema) { + eventSchema.fastMode(false); + }) + .property("PropA", "number") + .property("PropB", [](TypePropertySchema& typePropertySchema) { + typePropertySchema + .required(true) + .description("My Property") + .type("string") + .defaultValue("PropBValue"); + }); + }); + + Value rhsValue = schemaMsg; + ASSERT_FALSE(rhsValue.IsNull()); + + // creat an "expected" document for comparison + Document lhsDoc; + lhsDoc.Parse(COMPONENTS); + ASSERT_FALSE(lhsDoc.HasParseError() && lhsDoc.Empty()); + + // get the Command and compare to expected + Value* expected = ExtensionSchema::COMPONENTS().Get(lhsDoc); + Value* actual = ExtensionSchema::COMPONENTS().Get(rhsValue); + ASSERT_TRUE(IsEqual(*expected, *actual)); +} \ No newline at end of file diff --git a/options.cmake b/options.cmake index d62b41a..75871d1 100644 --- a/options.cmake +++ b/options.cmake @@ -21,6 +21,7 @@ option(VALIDATE_FORBIDDEN_FUNCTIONS "Validate that there are no calls to forbidd option(USER_DATA_RELEASE_CALLBACKS "Enable release callbacks in UserData" ON) option(BUILD_SHARED "Build as shared library." OFF) option(ENABLE_PIC "Build position independent code (i.e. -fPIC)" OFF) +option(DISABLE_RTTI "Build code without RTTI (i.e. -fno-rtti)" OFF) option(USE_SYSTEM_RAPIDJSON "Use the system-provided RapidJSON instead of the bundled one." OFF) diff --git a/patches/pegtl.patch b/patches/pegtl.patch index d38b03f..937aebe 100644 --- a/patches/pegtl.patch +++ b/patches/pegtl.patch @@ -1,35 +1,132 @@ diff --git a/.gitignore b/.gitignore -index 454bccb..adfdd12 100644 +index 454bccb4..188270c9 100644 --- a/.gitignore +++ b/.gitignore -@@ -1,3 +1,4 @@ +@@ -1,3 +1,6 @@ *~ build private +.vscode ++.idea ++cmake-build-debug +diff --git a/doc/Changelog.md b/doc/Changelog.md +index 6d7e71cc..ee82f97f 100644 +--- a/doc/Changelog.md ++++ b/doc/Changelog.md +@@ -1,5 +1,10 @@ + # Changelog + ++## Patches ++ ++* Removed some use of iostreams. ++* Added support for disabling exceptions with [`-fno-exceptions`](Installing-and-Using.md#disabling-exceptions). ++ + ## 2.8.3 + + Released 2020-04-22 +diff --git a/doc/Installing-and-Using.md b/doc/Installing-and-Using.md +index c1cd5e42..b0a972cb 100644 +--- a/doc/Installing-and-Using.md ++++ b/doc/Installing-and-Using.md +@@ -4,6 +4,8 @@ + + * [Requirements](#requirements) + * [Installation Packages](#installation-packages) ++* [Disabling Exceptions](#disabling-exceptions) ++* [Disabling RTTI](#disabling-rtti) + * [Using Conan](#using-conan) + * [Using CMake](#using-cmake) + * [CMake Installation](#cmake-installation) +@@ -40,6 +42,33 @@ sufficiently compatible platform). + The PEGTL is written with an emphasis on clean code and is compatible with + the `-pedantic`, `-Wall`, `-Wextra` and `-Werror` compiler switches. + ++## Disabling Exceptions ++ ++The PEGTL is compatible with `-fno-exceptions`, however: ++ ++* The following rules are unavailable: ++ * `raise<>`. ++ * `try_catch<>`. ++ * `try_catch_type<>`. ++ * `must<>`. ++ * `if_must<>`. ++ * `if_must_else<>`. ++ * `list_must<>`. ++ * `opt_must<>`. ++ * `star_must<>`. ++* The following headers are unavailable: ++ * `tao/pegtl/contrib/http.hpp`. ++ * `tao/pegtl/contrib/integer.hpp`. ++ * `tao/pegtl/contrib/uri.hpp`. ++* The error control class template `must_if<>` is unavailable. ++* Some of our tests and examples are disabled (via `#if`). ++ ++## Disabling RTTI ++ ++The PEGTL is compatible with `-fno-rtti` on GCC, Clang, and MSVC. ++An exception are GCC 9.1 and GCC 9.2, see [bug #91155](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91155). ++On unknown compilers, we use RTTI by default (for demangling), please report any such compiler and it might be possible to extend support for disabling RTTI for those compilers as well. ++ + ## Installation Packages + + Installation packages are available from several package managers. +diff --git a/doc/README.md b/doc/README.md +index 63e73be2..048c60d6 100644 +--- a/doc/README.md ++++ b/doc/README.md +@@ -4,6 +4,8 @@ + * [Getting Started](Getting-Started.md) + * [Installing and Using](Installing-and-Using.md) + * [Requirements](Installing-and-Using.md#requirements) ++ * [Disabling Exceptions](Installing-and-Using.md#disabling-exceptions) ++ * [Disabling RTTI](Installing-and-Using.md#disabling-rtti) + * [Installation Packages](Installing-and-Using.md#installation-packages) + * [Using Conan](Installing-and-Using.md#using-conan) + * [Using CMake](Installing-and-Using.md#using-cmake) diff --git a/include/tao/pegtl/analysis/analyze_cycles.hpp b/include/tao/pegtl/analysis/analyze_cycles.hpp -index 697023f..71e059c 100644 +index 6222b04b..1fa2a96c 100644 --- a/include/tao/pegtl/analysis/analyze_cycles.hpp +++ b/include/tao/pegtl/analysis/analyze_cycles.hpp -@@ -10,7 +10,6 @@ +@@ -8,11 +8,15 @@ + + #include #include - #include +-#include -#include #include ++#if !defined( __cpp_exceptions ) ++#include ++#else ++#include ++#endif ++ #include "../config.hpp" -@@ -90,7 +89,7 @@ namespace tao + + #include "grammar_info.hpp" +@@ -85,12 +89,17 @@ namespace tao + return m_cache[ start->first ] = a; + } + } ++#if defined( __cpp_exceptions ) + throw std::logic_error( "code should be unreachable: invalid rule_type value" ); // NOLINT, LCOV_EXCL_LINE ++#else ++ std::perror( "code should be unreachable: invalid rule_type value" ); ++ std::terminate(); ++#endif + } if( !accum ) { ++m_problems; if( m_verbose ) { - std::cout << "problem: cycle without progress detected at rule class " << start->first << std::endl; // LCOV_EXCL_LINE -+ printf("problem: cycle without progress detected at rule class %s", start->first); // NOLINT, LCOV_EXCL_LINE ++ printf("problem: cycle without progress detected at rule class %s", start->first.c_str()); // NOLINT, LCOV_EXCL_LINE } } return m_cache[ start->first ] = accum; diff --git a/include/tao/pegtl/argv_input.hpp b/include/tao/pegtl/argv_input.hpp -index e0b119f..da7d70e 100644 +index 75744a32..b8656b53 100644 --- a/include/tao/pegtl/argv_input.hpp +++ b/include/tao/pegtl/argv_input.hpp @@ -5,7 +5,6 @@ @@ -51,8 +148,304 @@ index e0b119f..da7d70e 100644 } } // namespace internal +diff --git a/include/tao/pegtl/ascii.hpp b/include/tao/pegtl/ascii.hpp +index 51cb0e40..c69c4826 100644 +--- a/include/tao/pegtl/ascii.hpp ++++ b/include/tao/pegtl/ascii.hpp +@@ -40,7 +40,7 @@ namespace tao + template< char Lo, char Hi > struct range : internal::range< internal::result_on_found::success, internal::peek_char, Lo, Hi > {}; + template< char... Cs > struct ranges : internal::ranges< internal::peek_char, Cs... > {}; + struct seven : internal::range< internal::result_on_found::success, internal::peek_char, char( 0 ), char( 127 ) > {}; +- struct shebang : internal::if_must< false, internal::string< '#', '!' >, internal::until< internal::eolf > > {}; ++ struct shebang : internal::seq< internal::string< '#', '!' >, internal::until< internal::eolf > > {}; + struct space : internal::one< internal::result_on_found::success, internal::peek_char, ' ', '\n', '\r', '\t', '\v', '\f' > {}; + template< char... Cs > struct string : internal::string< Cs... > {}; + template< char C > struct three : internal::string< C, C, C > {}; +diff --git a/include/tao/pegtl/buffer_input.hpp b/include/tao/pegtl/buffer_input.hpp +index f43513ab..5c267dc8 100644 +--- a/include/tao/pegtl/buffer_input.hpp ++++ b/include/tao/pegtl/buffer_input.hpp +@@ -10,9 +10,15 @@ + #include + #include + #include +-#include + #include + ++#if defined( __cpp_exceptions ) ++#include ++#else ++#include ++#include ++#endif ++ + #include "config.hpp" + #include "eol.hpp" + #include "memory_input.hpp" +@@ -155,7 +161,12 @@ namespace tao + return; + } + if( m_current.data + amount > m_buffer.get() + m_maximum ) { +- throw std::overflow_error( "require beyond end of buffer" ); ++#if defined( __cpp_exceptions ) ++ throw std::overflow_error( "require() beyond end of buffer" ); ++#else ++ std::fputs( "overflow error: require() beyond end of buffer\n", stderr ); ++ std::terminate(); ++#endif + } + if( const auto r = m_reader( m_end, ( std::min )( buffer_free_after_end(), ( std::max )( amount - buffer_occupied(), Chunk ) ) ) ) { + m_end += r; +diff --git a/include/tao/pegtl/contrib/http.hpp b/include/tao/pegtl/contrib/http.hpp +index 829b40ce..789e17e4 100644 +--- a/include/tao/pegtl/contrib/http.hpp ++++ b/include/tao/pegtl/contrib/http.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_CONTRIB_HTTP_HPP + #define TAO_PEGTL_CONTRIB_HTTP_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required tao/pegtl/contrib/http.hpp" ++#else ++ + #include "../ascii.hpp" + #include "../config.hpp" + #include "../nothing.hpp" +@@ -280,3 +284,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/contrib/integer.hpp b/include/tao/pegtl/contrib/integer.hpp +index 5c83145d..9511fcc5 100644 +--- a/include/tao/pegtl/contrib/integer.hpp ++++ b/include/tao/pegtl/contrib/integer.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_CONTRIB_INTEGER_HPP + #define TAO_PEGTL_CONTRIB_INTEGER_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required tao/pegtl/contrib/integer.hpp" ++#else ++ + #include + #include + +@@ -106,3 +110,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/contrib/json.hpp b/include/tao/pegtl/contrib/json.hpp +index cd71148d..ce1e8edc 100644 +--- a/include/tao/pegtl/contrib/json.hpp ++++ b/include/tao/pegtl/contrib/json.hpp +@@ -36,27 +36,27 @@ namespace tao + struct null : string< 'n', 'u', 'l', 'l' > {}; + struct true_ : string< 't', 'r', 'u', 'e' > {}; // NOLINT + +- struct digits : plus< abnf::DIGIT > {}; +- struct exp : seq< one< 'e', 'E' >, opt< one< '-', '+'> >, must< digits > > {}; +- struct frac : if_must< one< '.' >, digits > {}; +- struct int_ : sor< one< '0' >, digits > {}; // NOLINT ++ struct digits : plus< digit > {}; ++ struct exp : seq< one< 'e', 'E' >, opt< one< '-', '+'> >, digits > {}; ++ struct frac : seq< one< '.' >, digits > {}; ++ struct int_ : sor< one< '0' >, plus< digit > > {}; // NOLINT(readability-identifier-naming) + struct number : seq< opt< one< '-' > >, int_, opt< frac >, opt< exp > > {}; + +- struct xdigit : abnf::HEXDIG {}; +- struct unicode : list< seq< one< 'u' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; ++ struct xdigit : pegtl::xdigit {}; ++ struct unicode : list< seq< one< 'u' >, rep< 4, xdigit > >, one< '\\' > > {}; + struct escaped_char : one< '"', '\\', '/', 'b', 'f', 'n', 'r', 't' > {}; + struct escaped : sor< escaped_char, unicode > {}; + struct unescaped : utf8::range< 0x20, 0x10FFFF > {}; +- struct char_ : if_then_else< one< '\\' >, must< escaped >, unescaped > {}; // NOLINT ++ struct char_ : if_then_else< one< '\\' >, escaped, unescaped > {}; // NOLINT(readability-identifier-naming) + +- struct string_content : until< at< one< '"' > >, must< char_ > > {}; +- struct string : seq< one< '"' >, must< string_content >, any > ++ struct string_content : until< at< one< '"' > >, char_ > {}; ++ struct string : seq< one< '"' >, string_content, any > + { + using content = string_content; + }; + +- struct key_content : until< at< one< '"' > >, must< char_ > > {}; +- struct key : seq< one< '"' >, must< key_content >, any > ++ struct key_content : until< at< one< '"' > >, char_ > {}; ++ struct key : seq< one< '"' >, key_content, any > + { + using content = key_content; + }; +@@ -64,8 +64,8 @@ namespace tao + struct value; + + struct array_element; +- struct array_content : opt< list_must< array_element, value_separator > > {}; +- struct array : seq< begin_array, array_content, must< end_array > > ++ struct array_content : opt< array_element, star< value_separator, seq > > {}; ++ struct array : seq< begin_array, array_content, end_array > + { + using begin = begin_array; + using end = end_array; +@@ -73,9 +73,11 @@ namespace tao + using content = array_content; + }; + +- struct member : if_must< key, name_separator, value > {}; +- struct object_content : opt< list_must< member, value_separator > > {}; +- struct object : seq< begin_object, object_content, must< end_object > > ++ struct member_value : padr< value > {}; ++ struct member : seq< key, name_separator, member_value > {}; ++ struct next_member : seq< member > {}; ++ struct object_content : opt< member, star< value_separator, next_member > > {}; ++ struct object : seq< begin_object, object_content, end_object > + { + using begin = begin_object; + using end = end_object; +diff --git a/include/tao/pegtl/contrib/parse_tree.hpp b/include/tao/pegtl/contrib/parse_tree.hpp +index 4abe7eb4..4c77f1f3 100644 +--- a/include/tao/pegtl/contrib/parse_tree.hpp ++++ b/include/tao/pegtl/contrib/parse_tree.hpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++//#if !defined( __cpp_exceptions ) ++//int main() {} ++//#else ++ + #ifndef TAO_PEGTL_CONTRIB_PARSE_TREE_HPP + #define TAO_PEGTL_CONTRIB_PARSE_TREE_HPP + +@@ -30,7 +34,7 @@ + #include "../internal/integer_sequence.hpp" + #include "../internal/iterator.hpp" + #include "../internal/skip_control.hpp" +-#include "../internal/try_catch_type.hpp" ++//#include "../internal/try_catch_type.hpp" + + namespace tao + { +@@ -169,11 +173,11 @@ namespace tao + { + }; + +- template< typename Exception, typename... Rules > +- struct is_try_catch_type< TAO_PEGTL_NAMESPACE::internal::try_catch_type< Exception, Rules... > > +- : std::true_type +- { +- }; ++// template< typename Exception, typename... Rules > ++// struct is_try_catch_type< TAO_PEGTL_NAMESPACE::internal::try_catch_type< Exception, Rules... > > ++// : std::true_type ++// { ++// }; + + template< typename Node > + struct state +@@ -515,3 +519,5 @@ namespace tao + } // namespace tao + + #endif ++ ++//#endif +diff --git a/include/tao/pegtl/contrib/raw_string.hpp b/include/tao/pegtl/contrib/raw_string.hpp +index 1e66e3b4..e1f76df9 100644 +--- a/include/tao/pegtl/contrib/raw_string.hpp ++++ b/include/tao/pegtl/contrib/raw_string.hpp +@@ -11,14 +11,7 @@ + #include "../config.hpp" + #include "../rewind_mode.hpp" + +-#include "../internal/bytes.hpp" +-#include "../internal/eof.hpp" +-#include "../internal/eol.hpp" +-#include "../internal/must.hpp" +-#include "../internal/not_at.hpp" +-#include "../internal/seq.hpp" +-#include "../internal/skip_control.hpp" +-#include "../internal/star.hpp" ++#include "../rules.hpp" + + #include "../analysis/generic.hpp" + +@@ -39,9 +32,8 @@ namespace tao + class Action, + template< typename... > + class Control, +- typename Input, +- typename... States > +- static bool match( Input& in, std::size_t& marker_size, States&&... /*unused*/ ) noexcept( noexcept( in.size( 0 ) ) ) ++ typename Input > ++ static bool match( Input& in, std::size_t& marker_size ) noexcept( noexcept( in.size( 0 ) ) ) + { + if( in.empty() || ( in.peek_char( 0 ) != Open ) ) { + return false; +@@ -51,7 +43,7 @@ namespace tao + case Open: + marker_size = i + 1; + in.bump_in_this_line( marker_size ); +- eol::match( in ); ++ (void)eol::match( in ); + return true; + case Marker: + break; +@@ -79,9 +71,8 @@ namespace tao + class Action, + template< typename... > + class Control, +- typename Input, +- typename... States > +- static bool match( Input& in, const std::size_t& marker_size, States&&... /*unused*/ ) noexcept( noexcept( in.size( 0 ) ) ) ++ typename Input > ++ static bool match( Input& in, const std::size_t& marker_size ) noexcept( noexcept( in.size( 0 ) ) ) + { + if( in.size( marker_size ) < marker_size ) { + return false; +@@ -122,11 +113,11 @@ namespace tao + class Control, + typename Input, + typename... States > +- static bool match( Input& in, const std::size_t& marker_size, States&&... st ) ++ static bool match( Input& in, const std::size_t& marker_size, States&&... /*unused*/ ) + { + auto m = in.template mark< M >(); + +- while( !Control< Cond >::template match< A, rewind_mode::required, Action, Control >( in, marker_size, st... ) ) { ++ while( !Control< Cond >::template match< A, rewind_mode::required, Action, Control >( in, marker_size ) ) { + if( in.empty() ) { + return false; + } +@@ -154,7 +145,7 @@ namespace tao + auto m = in.template mark< M >(); + using m_t = decltype( m ); + +- while( !Control< Cond >::template match< A, rewind_mode::required, Action, Control >( in, marker_size, st... ) ) { ++ while( !Control< Cond >::template match< A, rewind_mode::required, Action, Control >( in, marker_size ) ) { + if( in.empty() || ( !Control< seq< Rules... > >::template match< A, m_t::next_rewind_mode, Action, Control >( in, st... ) ) ) { + return false; + } +@@ -218,10 +209,11 @@ namespace tao + static bool match( Input& in, States&&... st ) + { + std::size_t marker_size; +- if( internal::raw_string_open< Open, Marker >::template match< A, M, Action, Control >( in, marker_size, st... ) ) { +- internal::must< content >::template match< A, M, Action, Control >( in, marker_size, st... ); +- in.bump_in_this_line( marker_size ); +- return true; ++ if( Control< internal::raw_string_open< Open, Marker > >::template match< A, M, Action, Control >( in, marker_size ) ) { ++ if( Control< content >::template match< A, M, Action, Control >( in, marker_size, st... ) ) { ++ in.bump_in_this_line( marker_size ); ++ return true; ++ } + } + return false; + } diff --git a/include/tao/pegtl/contrib/tracer.hpp b/include/tao/pegtl/contrib/tracer.hpp -index 245fbf6..2479c25 100644 +index 4174e91b..b85700ba 100644 --- a/include/tao/pegtl/contrib/tracer.hpp +++ b/include/tao/pegtl/contrib/tracer.hpp @@ -6,7 +6,6 @@ @@ -197,7 +590,7 @@ index 245fbf6..2479c25 100644 static void start( const Input& in, States&&... st ) { - std::cerr << in.position() << " start " << internal::demangle< Rule >() << "; current "; -+ fprintf( stderr, "%d start %d; current", in.position(), internal::demangle< Rule >() ); ++ fprintf( stderr, "%lu start %lu; current", in.position().byte, internal::demangle< Rule >().size() ); print_current( in ); - std::cerr << std::endl; + fprintf( stderr, "\n" ); @@ -217,7 +610,7 @@ index 245fbf6..2479c25 100644 static void success( const Input& in, States&&... st ) { - std::cerr << in.position() << " success " << internal::demangle< Rule >() << "; next "; -+ fprintf( stderr, "%d success %d; next ", in.position(), internal::demangle< Rule >() ); ++ fprintf( stderr, "%lu success %lu; next ", in.position().byte, internal::demangle< Rule >().size() ); print_current( in ); + fprintf( stderr, "\n" ); std::cerr << std::endl; @@ -237,7 +630,7 @@ index 245fbf6..2479c25 100644 static void failure( const Input& in, States&&... st ) { - std::cerr << in.position() << " failure " << internal::demangle< Rule >() << std::endl; -+ fprintf( stderr, "%d failure %d\n", in.position(), internal::demangle< Rule >() ); ++ fprintf( stderr, "%lu failure %lu\n", in.position().byte, internal::demangle< Rule >().size() ); Base< Rule >::failure( in, st... ); } @@ -255,7 +648,7 @@ index 245fbf6..2479c25 100644 -> decltype( Base< Rule >::template apply< Action >( begin, in, st... ) ) { - std::cerr << in.position() << " apply " << internal::demangle< Rule >() << std::endl; -+ fprintf( stderr, "%d apply %d\n", in.position(), internal::demangle< Rule >() ); ++ fprintf( stderr, "%lu apply %lu\n", in.position().byte, internal::demangle< Rule >().size() ); return Base< Rule >::template apply< Action >( begin, in, st... ); } @@ -273,7 +666,7 @@ index 245fbf6..2479c25 100644 -> decltype( Base< Rule >::template apply0< Action >( in, st... ) ) { - std::cerr << in.position() << " apply0 " << internal::demangle< Rule >() << std::endl; -+ fprintf( stderr, "%d apply0 %d\n", in.position(), internal::demangle< Rule >() ); ++ fprintf( stderr, "%lu apply0 %lu\n", in.position().byte, internal::demangle< Rule >().size() ); return Base< Rule >::template apply0< Action >( in, st... ); } @@ -286,8 +679,98 @@ index 245fbf6..2479c25 100644 return apply0< Action >( in, st... ); } }; +diff --git a/include/tao/pegtl/contrib/unescape.hpp b/include/tao/pegtl/contrib/unescape.hpp +index 9b6090a6..2ec2d011 100644 +--- a/include/tao/pegtl/contrib/unescape.hpp ++++ b/include/tao/pegtl/contrib/unescape.hpp +@@ -5,6 +5,7 @@ + #define TAO_PEGTL_CONTRIB_UNESCAPE_HPP + + #include ++#include + #include + + #include "../ascii.hpp" +@@ -83,8 +84,8 @@ namespace tao + case 'E': + case 'F': + return I( c - 'A' + 10 ); +- default: // LCOV_EXCL_LINE +- throw std::runtime_error( "invalid character in unhex" ); // NOLINT, LCOV_EXCL_LINE ++ default: // LCOV_EXCL_LINE ++ std::terminate(); // NOLINT, LCOV_EXCL_LINE + } + } + +@@ -137,7 +138,7 @@ namespace tao + return *( r.begin() + i ); + } + } +- throw parse_error( "invalid character in unescape", in ); // NOLINT, LCOV_EXCL_LINE ++ std::terminate(); // NOLINT, LCOV_EXCL_LINE + } + }; + +@@ -147,6 +148,7 @@ namespace tao + + struct unescape_u + { ++#if defined( __cpp_exceptions ) + template< typename Input > + static void apply( const Input& in, std::string& s ) + { +@@ -155,6 +157,14 @@ namespace tao + throw parse_error( "invalid escaped unicode code point", in ); + } + } ++#else ++ template< typename Input > ++ static bool apply( const Input& in, std::string& s ) ++ { ++ assert( !in.empty() ); // First character MUST be present, usually 'u' or 'U'. ++ return utf8_append_utf32( s, unhex_string< unsigned >( in.begin() + 1, in.end() ) ); ++ } ++#endif + }; + + struct unescape_x +@@ -192,7 +202,12 @@ namespace tao + } + } + if( !utf8_append_utf32( s, c ) ) { ++#if defined( __cpp_exceptions ) + throw parse_error( "invalid escaped unicode code point", in ); ++#else ++ // TODO: Set error? ++ return; ++#endif + } + } + } +diff --git a/include/tao/pegtl/contrib/uri.hpp b/include/tao/pegtl/contrib/uri.hpp +index f86d2de8..38fa8d96 100644 +--- a/include/tao/pegtl/contrib/uri.hpp ++++ b/include/tao/pegtl/contrib/uri.hpp +@@ -4,6 +4,12 @@ + #ifndef TAO_PEGTL_CONTRIB_URI_HPP + #define TAO_PEGTL_CONTRIB_URI_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required tao/pegtl/contrib/uri.hpp" ++#else ++ ++#include ++ + #include "../ascii.hpp" + #include "../config.hpp" + #include "../rules.hpp" +@@ -113,3 +119,4 @@ namespace tao + } // namespace tao + + #endif ++#endif diff --git a/include/tao/pegtl/input_error.hpp b/include/tao/pegtl/input_error.hpp -index 4c7aec5..007661a 100644 +index 08cb00b9..4f46a113 100644 --- a/include/tao/pegtl/input_error.hpp +++ b/include/tao/pegtl/input_error.hpp @@ -5,7 +5,7 @@ @@ -321,134 +804,619 @@ index 4c7aec5..007661a 100644 } while( false ) #endif +diff --git a/include/tao/pegtl/internal/cstream_reader.hpp b/include/tao/pegtl/internal/cstream_reader.hpp +index 412d45e3..b1946022 100644 +--- a/include/tao/pegtl/internal/cstream_reader.hpp ++++ b/include/tao/pegtl/internal/cstream_reader.hpp +@@ -8,6 +8,10 @@ + #include + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#endif ++ + #include "../config.hpp" + #include "../input_error.hpp" + +@@ -33,9 +37,15 @@ namespace tao + if( std::feof( m_cstream ) != 0 ) { + return 0; + } ++ ++#if defined( __cpp_exceptions ) + // Please contact us if you know how to provoke the following exception. + // The example on cppreference.com doesn't work, at least not on macOS. + TAO_PEGTL_THROW_INPUT_ERROR( "error in fread() from cstream" ); // LCOV_EXCL_LINE ++#else ++ std::fputs( "std::fread() failed\n", stderr ); ++ std::terminate(); ++#endif + } + + std::FILE* m_cstream; +diff --git a/include/tao/pegtl/internal/demangle.hpp b/include/tao/pegtl/internal/demangle.hpp +index f0bd799c..9677d921 100644 +--- a/include/tao/pegtl/internal/demangle.hpp ++++ b/include/tao/pegtl/internal/demangle.hpp +@@ -64,6 +64,8 @@ namespace tao + #if defined( __clang__ ) || defined( __GNUC__ ) + start = std::strchr( __PRETTY_FUNCTION__, '=' ) + 2; + stop = std::strrchr( start, ';' ); ++ if (stop == nullptr) { stop = std::strrchr( start, ']' ); } ++ if (stop == nullptr) { stop = start + strnlen(start, 256); } + #elif defined( _MSC_VER ) + start = std::strstr( __FUNCSIG__, "demangle<" ) + ( sizeof( "demangle<" ) - 1 ); + stop = std::strrchr( start, '>' ); diff --git a/include/tao/pegtl/internal/file_mapper_posix.hpp b/include/tao/pegtl/internal/file_mapper_posix.hpp -index ff58eda..8d6409d 100644 +index c77529e1..e1169607 100644 --- a/include/tao/pegtl/internal/file_mapper_posix.hpp +++ b/include/tao/pegtl/internal/file_mapper_posix.hpp -@@ -32,7 +32,7 @@ namespace tao +@@ -7,6 +7,11 @@ + #include + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#include ++#endif ++ + #include "../config.hpp" + + #include "file_opener.hpp" +@@ -32,7 +37,12 @@ namespace tao m_data( static_cast< const char* >( ::mmap( nullptr, m_size, PROT_READ, MAP_PRIVATE, reader.m_fd, 0 ) ) ) { if( ( m_size != 0 ) && ( intptr_t( m_data ) == -1 ) ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to mmap() file " << reader.m_source << " descriptor " << reader.m_fd ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to mmap() file " + std::string(reader.m_source) + " descriptor " + std::to_string(reader.m_fd) ); ++#else ++ std::perror( "mmap() failed" ); ++ std::terminate(); ++#endif } } diff --git a/include/tao/pegtl/internal/file_mapper_win32.hpp b/include/tao/pegtl/internal/file_mapper_win32.hpp -index 11b249e..82ef1db 100644 +index a8cc1a4e..deb01b7f 100644 --- a/include/tao/pegtl/internal/file_mapper_win32.hpp +++ b/include/tao/pegtl/internal/file_mapper_win32.hpp -@@ -58,7 +58,7 @@ namespace tao +@@ -29,36 +29,46 @@ + #include "../config.hpp" + #include "../input_error.hpp" + ++#if !defined( __cpp_exceptions ) ++#include ++#include ++#endif ++ + namespace tao + { + namespace TAO_PEGTL_NAMESPACE + { + namespace internal + { +- struct win32_file_opener ++ struct file_opener + { +- explicit win32_file_opener( const char* filename ) ++ explicit file_opener( const char* filename ) + : m_source( filename ), + m_handle( open() ) + { + } + +- win32_file_opener( const win32_file_opener& ) = delete; +- win32_file_opener( win32_file_opener&& ) = delete; ++ file_opener( const file_opener& ) = delete; ++ file_opener( file_opener&& ) = delete; + +- ~win32_file_opener() noexcept ++ ~file_opener() noexcept + { + ::CloseHandle( m_handle ); + } + +- void operator=( const win32_file_opener& ) = delete; +- void operator=( win32_file_opener&& ) = delete; ++ void operator=( const file_opener& ) = delete; ++ void operator=( file_opener&& ) = delete; + + std::size_t size() const { LARGE_INTEGER size; if( !::GetFileSizeEx( m_handle, &size ) ) { - TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to GetFileSizeEx() file " << m_source << " handle " << m_handle ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to GetFileSizeEx() file " + std::string(m_source) ); ++#else ++ std::perror( "GetFileSizeEx() failed" ); ++ std::terminate(); ++#endif } return std::size_t( size.QuadPart ); } -@@ -81,7 +81,7 @@ namespace tao +@@ -81,7 +91,12 @@ namespace tao if( handle != INVALID_HANDLE_VALUE ) { return handle; } - TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "CreateFile2() failed opening file " << m_source << " for reading" ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "CreateFile2() failed opening file " + std::string(m_source) + " for reading" ); ++#else ++ std::perror( "CreateFile2() failed" ); ++ std::terminate(); ++#endif #else const HANDLE handle = ::CreateFileW( ws.c_str(), GENERIC_READ, -@@ -93,7 +93,7 @@ namespace tao +@@ -93,7 +108,12 @@ namespace tao if( handle != INVALID_HANDLE_VALUE ) { return handle; } - TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "CreateFileW() failed opening file " << m_source << " for reading" ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "CreateFileW() failed opening file " + std::string(m_source) + " for reading" ); ++#else ++ std::perror( "CreateFileW() failed" ); ++ std::terminate(); ++#endif #endif } }; -@@ -143,7 +143,7 @@ namespace tao +@@ -101,11 +121,11 @@ namespace tao + struct win32_file_mapper + { + explicit win32_file_mapper( const char* filename ) +- : win32_file_mapper( win32_file_opener( filename ) ) ++ : win32_file_mapper( file_opener( filename ) ) + { + } + +- explicit win32_file_mapper( const win32_file_opener& reader ) ++ explicit win32_file_mapper( const file_opener& reader ) + : m_size( reader.size() ), + m_handle( open( reader ) ) + { +@@ -126,7 +146,7 @@ namespace tao + const HANDLE m_handle; + + private: +- HANDLE open( const win32_file_opener& reader ) const ++ HANDLE open( const file_opener& reader ) const + { + const uint64_t file_size = reader.size(); + SetLastError( 0 ); +@@ -143,7 +163,12 @@ namespace tao if( handle != NULL || file_size == 0 ) { return handle; } - TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to CreateFileMappingW() file " << reader.m_source << " for reading" ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to CreateFileMappingW() file " + std::string(reader.m_source) + " for reading" ); ++#else ++ std::perror( "CreateFileMappingW() failed" ); ++ std::terminate(); ++#endif } }; -@@ -164,7 +164,7 @@ namespace tao +@@ -164,7 +189,12 @@ namespace tao 0 ) ) ) { if( ( m_size != 0 ) && ( intptr_t( m_data ) == 0 ) ) { - TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to MapViewOfFile() file mapping object with handle " << mapper.m_handle ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_WIN32_ERROR( "unable to MapViewOfFile() file mapping object" ); ++#else ++ std::perror( "MapViewOfFile() failed" ); ++ std::terminate(); ++#endif } } diff --git a/include/tao/pegtl/internal/file_opener.hpp b/include/tao/pegtl/internal/file_opener.hpp -index 50f4a4e..32be374 100644 +index 33eccb63..a42c7a62 100644 --- a/include/tao/pegtl/internal/file_opener.hpp +++ b/include/tao/pegtl/internal/file_opener.hpp -@@ -44,7 +44,7 @@ namespace tao +@@ -11,6 +11,10 @@ + + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#endif ++ + #include "../config.hpp" + #include "../input_error.hpp" + +@@ -44,7 +48,12 @@ namespace tao struct stat st; // NOLINT errno = 0; if( ::fstat( m_fd, &st ) < 0 ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to fstat() file " << m_source << " descriptor " << m_fd ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to fstat() file " + std::string(m_source) + " descriptor " + std::to_string(m_fd) ); ++#else ++ std::perror( std::string("unable to fstat() file " + std::string(m_source) + " descriptor " + std::to_string(m_fd) ).c_str() ); ++ std::terminate(); ++#endif } return std::size_t( st.st_size ); } -@@ -65,7 +65,7 @@ namespace tao +@@ -65,7 +74,12 @@ namespace tao if( fd >= 0 ) { return fd; } - TAO_PEGTL_THROW_INPUT_ERROR( "unable to open() file " << m_source << " for reading" ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to open() file " + std::string(m_source) + " for reading" ); ++#else ++ std::perror( std::string("unable to open() file " + std::string(m_source) + " for reading").c_str() ); ++ std::terminate(); ++#endif } }; diff --git a/include/tao/pegtl/internal/file_reader.hpp b/include/tao/pegtl/internal/file_reader.hpp -index b61386d..58c72b7 100644 +index c309aef3..12a49b7e 100644 --- a/include/tao/pegtl/internal/file_reader.hpp +++ b/include/tao/pegtl/internal/file_reader.hpp -@@ -32,7 +32,7 @@ namespace tao +@@ -9,6 +9,10 @@ + #include + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#endif ++ + #include "../config.hpp" + #include "../input_error.hpp" + +@@ -32,7 +36,12 @@ namespace tao { return file; } - TAO_PEGTL_THROW_INPUT_ERROR( "unable to fopen() file " << filename << " for reading" ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to fopen() file " + std::string(filename) + " for reading" ); ++#else ++ std::perror( "fopen_s() failed" ); ++ std::terminate(); ++#endif } struct file_close -@@ -70,16 +70,16 @@ namespace tao +@@ -70,16 +79,31 @@ namespace tao { errno = 0; if( std::fseek( m_file.get(), 0, SEEK_END ) != 0 ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to fseek() to end of file " << m_source ); // LCOV_EXCL_LINE ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to fseek() to end of file " + std::string(m_source) ); // LCOV_EXCL_LINE ++#else ++ std::perror( "std::fseek() failed [SEEK_END]" ); ++ std::terminate(); ++#endif } errno = 0; const auto s = std::ftell( m_file.get() ); if( s < 0 ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to ftell() file size of file " << m_source ); // LCOV_EXCL_LINE ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to ftell() file size of file " + std::string(m_source) ); // LCOV_EXCL_LINE ++#else ++ std::perror( "std::ftell() failed" ); ++ std::terminate(); ++#endif } errno = 0; if( std::fseek( m_file.get(), 0, SEEK_SET ) != 0 ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to fseek() to beginning of file " << m_source ); // LCOV_EXCL_LINE ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to fseek() to beginning of file " + std::string(m_source) ); // LCOV_EXCL_LINE ++#else ++ std::perror( "std::fseek() failed [SEEK_SET]" ); ++ std::terminate(); ++#endif } return std::size_t( s ); } -@@ -90,7 +90,7 @@ namespace tao +@@ -90,7 +114,12 @@ namespace tao nrv.resize( size() ); errno = 0; if( !nrv.empty() && ( std::fread( &nrv[ 0 ], nrv.size(), 1, m_file.get() ) != 1 ) ) { - TAO_PEGTL_THROW_INPUT_ERROR( "unable to fread() file " << m_source << " size " << nrv.size() ); // LCOV_EXCL_LINE ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "unable to fread() file " + std::string(m_source) + " size " + std::to_string(nrv.size()) ); // LCOV_EXCL_LINE ++#else ++ std::perror( "std::fread() failed" ); ++ std::terminate(); ++#endif } return nrv; } +diff --git a/include/tao/pegtl/internal/if_must.hpp b/include/tao/pegtl/internal/if_must.hpp +index 0cbd08c0..8b902d38 100644 +--- a/include/tao/pegtl/internal/if_must.hpp ++++ b/include/tao/pegtl/internal/if_must.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_IF_MUST_HPP + #define TAO_PEGTL_INTERNAL_IF_MUST_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/if_must.hpp" ++#else ++ + #include "../config.hpp" + + #include "must.hpp" +@@ -56,3 +60,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/if_must_else.hpp b/include/tao/pegtl/internal/if_must_else.hpp +index 74db045f..a31eacf4 100644 +--- a/include/tao/pegtl/internal/if_must_else.hpp ++++ b/include/tao/pegtl/internal/if_must_else.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_IF_MUST_ELSE_HPP + #define TAO_PEGTL_INTERNAL_IF_MUST_ELSE_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/if_must_else.hpp" ++#else ++ + #include "../config.hpp" + + #include "if_then_else.hpp" +@@ -25,3 +29,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/istream_reader.hpp b/include/tao/pegtl/internal/istream_reader.hpp +index 77744e0b..5add564a 100644 +--- a/include/tao/pegtl/internal/istream_reader.hpp ++++ b/include/tao/pegtl/internal/istream_reader.hpp +@@ -6,6 +6,11 @@ + + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#include ++#endif ++ + #include "../config.hpp" + #include "../input_error.hpp" + +@@ -32,7 +37,12 @@ namespace tao + if( m_istream.eof() ) { + return 0; + } ++#if defined( __cpp_exceptions ) + TAO_PEGTL_THROW_INPUT_ERROR( "error in istream.read()" ); ++#else ++ std::fputs( "std::istream::read() failed\n", stderr ); ++ std::terminate(); ++#endif + } + + std::istream& m_istream; +diff --git a/include/tao/pegtl/internal/list_must.hpp b/include/tao/pegtl/internal/list_must.hpp +index 9f03ed5f..08c313b9 100644 +--- a/include/tao/pegtl/internal/list_must.hpp ++++ b/include/tao/pegtl/internal/list_must.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_LIST_MUST_HPP + #define TAO_PEGTL_INTERNAL_LIST_MUST_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/list_must.hpp" ++#else ++ + #include "../config.hpp" + + #include "must.hpp" +@@ -26,3 +30,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/must.hpp b/include/tao/pegtl/internal/must.hpp +index 3572728d..1a178ffc 100644 +--- a/include/tao/pegtl/internal/must.hpp ++++ b/include/tao/pegtl/internal/must.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_MUST_HPP + #define TAO_PEGTL_INTERNAL_MUST_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/must.hpp" ++#else ++ + #include "../config.hpp" + + #include "raise.hpp" +@@ -68,3 +72,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/raise.hpp b/include/tao/pegtl/internal/raise.hpp +index d4f66430..7f1916e9 100644 +--- a/include/tao/pegtl/internal/raise.hpp ++++ b/include/tao/pegtl/internal/raise.hpp +@@ -4,7 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_RAISE_HPP + #define TAO_PEGTL_INTERNAL_RAISE_HPP + +-#include ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/raise.hpp" ++#else ++ + #include + #include + +@@ -61,3 +64,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/rules.hpp b/include/tao/pegtl/internal/rules.hpp +index fc56a6e8..1c468f26 100644 +--- a/include/tao/pegtl/internal/rules.hpp ++++ b/include/tao/pegtl/internal/rules.hpp +@@ -23,22 +23,17 @@ + #include "eolf.hpp" + #include "identifier.hpp" + #include "if_apply.hpp" +-#include "if_must.hpp" +-#include "if_must_else.hpp" + #include "if_then_else.hpp" + #include "istring.hpp" + #include "list.hpp" +-#include "list_must.hpp" + #include "list_tail.hpp" + #include "list_tail_pad.hpp" +-#include "must.hpp" + #include "not_at.hpp" + #include "one.hpp" + #include "opt.hpp" + #include "pad.hpp" + #include "pad_opt.hpp" + #include "plus.hpp" +-#include "raise.hpp" + #include "range.hpp" + #include "ranges.hpp" + #include "rematch.hpp" +@@ -51,11 +46,19 @@ + #include "skip_control.hpp" + #include "sor.hpp" + #include "star.hpp" +-#include "star_must.hpp" + #include "state.hpp" + #include "string.hpp" + #include "trivial.hpp" +-#include "try_catch_type.hpp" + #include "until.hpp" + ++#if defined( __cpp_exceptions ) ++#include "if_must.hpp" ++#include "if_must_else.hpp" ++#include "list_must.hpp" ++#include "must.hpp" ++#include "raise.hpp" ++#include "star_must.hpp" ++#include "try_catch_type.hpp" ++#endif ++ + #endif +diff --git a/include/tao/pegtl/internal/star_must.hpp b/include/tao/pegtl/internal/star_must.hpp +index 84e5a441..38faf931 100644 +--- a/include/tao/pegtl/internal/star_must.hpp ++++ b/include/tao/pegtl/internal/star_must.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_STAR_MUST_HPP + #define TAO_PEGTL_INTERNAL_STAR_MUST_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/star_must.hpp" ++#else ++ + #include "../config.hpp" + + #include "if_must.hpp" +@@ -25,3 +29,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/internal/try_catch_type.hpp b/include/tao/pegtl/internal/try_catch_type.hpp +index 3612c198..8bff0d16 100644 +--- a/include/tao/pegtl/internal/try_catch_type.hpp ++++ b/include/tao/pegtl/internal/try_catch_type.hpp +@@ -4,6 +4,10 @@ + #ifndef TAO_PEGTL_INTERNAL_TRY_CATCH_TYPE_HPP + #define TAO_PEGTL_INTERNAL_TRY_CATCH_TYPE_HPP + ++#if !defined( __cpp_exceptions ) ++#error "Exception support required for tao/pegtl/internal/try_catch_type.hpp" ++#else ++ + #include + + #include "../config.hpp" +@@ -74,3 +78,4 @@ namespace tao + } // namespace tao + + #endif ++#endif +diff --git a/include/tao/pegtl/normal.hpp b/include/tao/pegtl/normal.hpp +index 9db70d46..a9109fd6 100644 +--- a/include/tao/pegtl/normal.hpp ++++ b/include/tao/pegtl/normal.hpp +@@ -7,12 +7,17 @@ + #include + #include + ++#if !defined( __cpp_exceptions ) ++#include ++#endif ++ + #include "apply_mode.hpp" + #include "config.hpp" + #include "match.hpp" + #include "parse_error.hpp" + #include "rewind_mode.hpp" + ++#include "internal/always_false.hpp" + #include "internal/demangle.hpp" + #include "internal/has_match.hpp" + +@@ -41,7 +46,13 @@ namespace tao + template< typename Input, typename... States > + static void raise( const Input& in, States&&... /*unused*/ ) + { ++#if defined( __cpp_exceptions ) + throw parse_error( "parse error matching " + internal::demangle< Rule >(), in ); ++#else ++ static_assert( internal::always_false< Rule >::value, "exception support required for normal< Rule >::raise()" ); ++ (void)in; ++ std::terminate(); ++#endif + } + + template< template< typename... > class Action, typename Input, typename... States > +diff --git a/include/tao/pegtl/parse.hpp b/include/tao/pegtl/parse.hpp +index d6093e01..17856ea9 100644 +--- a/include/tao/pegtl/parse.hpp ++++ b/include/tao/pegtl/parse.hpp +@@ -41,6 +41,7 @@ namespace tao + typename... States > + bool parse_nested( const Outer& oi, Input&& in, States&&... st ) + { ++#ifdef __cpp_exceptions + try { + return parse< Rule, Action, Control, A, M >( in, st... ); + } +@@ -48,6 +49,10 @@ namespace tao + e.positions.push_back( oi.position() ); + throw; + } ++#else ++ (void)oi; ++ return parse< Rule, Action, Control, A, M >( in, st... ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE diff --git a/include/tao/pegtl/position.hpp b/include/tao/pegtl/position.hpp -index 60904a0..379f707 100644 +index 46a982df..090ef91f 100644 --- a/include/tao/pegtl/position.hpp +++ b/include/tao/pegtl/position.hpp @@ -5,8 +5,6 @@ @@ -478,3 +1446,2481 @@ index 60904a0..379f707 100644 } } // namespace TAO_PEGTL_NAMESPACE +diff --git a/include/tao/pegtl/rules.hpp b/include/tao/pegtl/rules.hpp +index 8f0ce396..2f195ce8 100644 +--- a/include/tao/pegtl/rules.hpp ++++ b/include/tao/pegtl/rules.hpp +@@ -28,24 +28,17 @@ namespace tao + struct eof : internal::eof {}; + struct failure : internal::trivial< false > {}; + template< typename Rule, typename... Actions > struct if_apply : internal::if_apply< Rule, Actions... > {}; +- template< typename Cond, typename... Thens > struct if_must : internal::if_must< false, Cond, Thens... > {}; +- template< typename Cond, typename Then, typename Else > struct if_must_else : internal::if_must_else< Cond, Then, Else > {}; + template< typename Cond, typename Then, typename Else > struct if_then_else : internal::if_then_else< Cond, Then, Else > {}; + template< typename Rule, typename Sep, typename Pad = void > struct list : internal::list< Rule, internal::pad< Sep, Pad > > {}; + template< typename Rule, typename Sep > struct list< Rule, Sep, void > : internal::list< Rule, Sep > {}; +- template< typename Rule, typename Sep, typename Pad = void > struct list_must : internal::list_must< Rule, internal::pad< Sep, Pad > > {}; +- template< typename Rule, typename Sep > struct list_must< Rule, Sep, void > : internal::list_must< Rule, Sep > {}; + template< typename Rule, typename Sep, typename Pad = void > struct list_tail : internal::list_tail_pad< Rule, Sep, Pad > {}; + template< typename Rule, typename Sep > struct list_tail< Rule, Sep, void > : internal::list_tail< Rule, Sep > {}; + template< typename M, typename S > struct minus : internal::rematch< M, internal::not_at< S, internal::eof > > {}; +- template< typename... Rules > struct must : internal::must< Rules... > {}; + template< typename... Rules > struct not_at : internal::not_at< Rules... > {}; + template< typename... Rules > struct opt : internal::opt< Rules... > {}; +- template< typename Cond, typename... Rules > struct opt_must : internal::if_must< true, Cond, Rules... > {}; + template< typename Rule, typename Pad1, typename Pad2 = Pad1 > struct pad : internal::pad< Rule, Pad1, Pad2 > {}; + template< typename Rule, typename Pad > struct pad_opt : internal::pad_opt< Rule, Pad > {}; + template< typename Rule, typename... Rules > struct plus : internal::plus< Rule, Rules... > {}; +- template< typename Exception > struct raise : internal::raise< Exception > {}; + template< typename Head, typename... Rules > struct rematch : internal::rematch< Head, Rules... > {}; + template< unsigned Num, typename... Rules > struct rep : internal::rep< Num, Rules... > {}; + template< unsigned Max, typename... Rules > struct rep_max : internal::rep_min_max< 0, Max, Rules... > {}; +@@ -56,12 +49,22 @@ namespace tao + template< typename... Rules > struct seq : internal::seq< Rules... > {}; + template< typename... Rules > struct sor : internal::sor< Rules... > {}; + template< typename Rule, typename... Rules > struct star : internal::star< Rule, Rules... > {}; +- template< typename Cond, typename... Rules > struct star_must : internal::star_must< Cond, Rules... > {}; + template< typename State, typename... Rules > struct state : internal::state< State, Rules... > {}; + struct success : internal::trivial< true > {}; ++ template< typename Cond, typename... Rules > struct until : internal::until< Cond, Rules... > {}; ++ ++#if defined( __cpp_exceptions ) ++ template< typename Cond, typename... Thens > struct if_must : internal::if_must< false, Cond, Thens... > {}; ++ template< typename Cond, typename Then, typename Else > struct if_must_else : internal::if_must_else< Cond, Then, Else > {}; ++ template< typename Rule, typename Sep, typename Pad = void > struct list_must : internal::list_must< Rule, internal::pad< Sep, Pad > > {}; ++ template< typename Rule, typename Sep > struct list_must< Rule, Sep, void > : internal::list_must< Rule, Sep > {}; ++ template< typename... Rules > struct must : internal::must< Rules... > {}; ++ template< typename Cond, typename... Rules > struct opt_must : internal::if_must< true, Cond, Rules... > {}; ++ template< typename Exception > struct raise : internal::raise< Exception > {}; ++ template< typename Cond, typename... Rules > struct star_must : internal::star_must< Cond, Rules... > {}; + template< typename... Rules > struct try_catch : internal::seq< internal::try_catch_type< parse_error, Rules... > > {}; + template< typename Exception, typename... Rules > struct try_catch_type : internal::seq< internal::try_catch_type< Exception, Rules... > > {}; +- template< typename Cond, typename... Rules > struct until : internal::until< Cond, Rules... > {}; ++#endif + // clang-format on + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/example/pegtl/abnf2pegtl.cpp b/src/example/pegtl/abnf2pegtl.cpp +index fa79c538..18ee3372 100644 +--- a/src/example/pegtl/abnf2pegtl.cpp ++++ b/src/example/pegtl/abnf2pegtl.cpp +@@ -2,6 +2,8 @@ + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + + #include ++#include ++#include + #include + #include + #include +@@ -222,28 +224,28 @@ namespace tao + + // The rest is according to the RFC(s): + struct comment_cont : until< CRLF, sor< WSP, VCHAR > > {}; +- struct comment : if_must< one< ';' >, comment_cont > {}; ++ struct comment : seq< one< ';' >, comment_cont > {}; + struct c_nl : sor< comment, CRLF > {}; + struct c_wsp : sor< WSP, seq< c_nl, WSP > > {}; + + struct rulename : seq< ALPHA, star< ranges< 'a', 'z', 'A', 'Z', '0', '9', '-' > > > {}; + + struct quoted_string_cont : until< DQUOTE, print > {}; +- struct quoted_string : if_must< DQUOTE, quoted_string_cont > {}; ++ struct quoted_string : seq< DQUOTE, quoted_string_cont > {}; + struct case_insensitive_string : seq< opt< istring< '%', 'i' > >, quoted_string > {}; + struct case_sensitive_string : seq< istring< '%', 's' >, quoted_string > {}; + struct char_val : sor< case_insensitive_string, case_sensitive_string > {}; + + struct prose_val_cont : until< one< '>' >, print > {}; +- struct prose_val : if_must< one< '<' >, prose_val_cont > {}; ++ struct prose_val : seq< one< '<' >, prose_val_cont > {}; + + template< char First, typename Digit > + struct gen_val + { + struct value : plus< Digit > {}; +- struct range : if_must< one< '-' >, value > {}; +- struct next_value : must< value > {}; +- struct type : seq< istring< First >, must< value >, sor< range, star< one< '.' >, next_value > > > {}; ++ struct range : seq< one< '-' >, value > {}; ++ struct next_value : seq< value > {}; ++ struct type : seq< istring< First >, value, sor< range, star< one< '.' >, next_value > > > {}; + }; + + using hex_val = gen_val< 'x', HEXDIG >; +@@ -251,29 +253,29 @@ namespace tao + using bin_val = gen_val< 'b', BIT >; + + struct num_val_choice : sor< bin_val::type, dec_val::type, hex_val::type > {}; +- struct num_val : if_must< one< '%' >, num_val_choice > {}; ++ struct num_val : seq< one< '%' >, num_val_choice > {}; + + struct alternation; + struct option_close : one< ']' > {}; +- struct option : seq< one< '[' >, pad< must< alternation >, c_wsp >, must< option_close > > {}; ++ struct option : seq< one< '[' >, pad< alternation, c_wsp >, option_close > {}; + struct group_close : one< ')' > {}; +- struct group : seq< one< '(' >, pad< must< alternation >, c_wsp >, must< group_close > > {}; ++ struct group : seq< one< '(' >, pad< alternation, c_wsp >, group_close > {}; + struct element : sor< rulename, group, option, char_val, num_val, prose_val > {}; + + struct repeat : sor< seq< star< DIGIT >, one< '*' >, star< DIGIT > >, plus< DIGIT > > {}; + struct repetition : seq< opt< repeat >, element > {}; + +- struct and_predicate : if_must< one< '&' >, repetition > {}; +- struct not_predicate : if_must< one< '!' >, repetition > {}; ++ struct and_predicate : seq< one< '&' >, repetition > {}; ++ struct not_predicate : seq< one< '!' >, repetition > {}; + struct predicate : sor< and_predicate, not_predicate, repetition > {}; + + struct concatenation : list< predicate, plus< c_wsp > > {}; +- struct alternation : list_must< concatenation, pad< one< '/' >, c_wsp > > {}; ++ struct alternation : seq< concatenation, pad< one< '/' >, c_wsp > > {}; + + struct defined_as_op : sor< string< '=', '/' >, one< '=' > > {}; + struct defined_as : pad< defined_as_op, c_wsp > {}; +- struct rule : seq< if_must< rulename, defined_as, alternation >, star< c_wsp >, must< c_nl > > {}; +- struct rulelist : until< eof, sor< seq< star< c_wsp >, c_nl >, must< rule > > > {}; ++ struct rule : seq< seq< rulename, defined_as, alternation >, star< c_wsp >, c_nl > {}; ++ struct rulelist : until< eof, sor< seq< star< c_wsp >, c_nl >, rule > > {}; + + // end of grammar + +@@ -285,7 +287,12 @@ namespace tao + template< typename Input, typename... States > + static void raise( const Input& in, States&&... /*unused*/ ) + { ++#if defined( __cpp_exceptions ) + throw parse_error( error_message, in ); ++#else ++ (void)in; ++ std::terminate(); ++#endif + } + }; + +@@ -414,7 +421,12 @@ namespace tao + return *it; + } + if( keywords.count( v ) != 0 || v.find( "__" ) != std::string::npos ) { ++#if defined( __cpp_exceptions ) + throw parse_error( '\'' + n->content() + "' is a reserved rulename", n->begin() ); // NOLINT ++#else ++ std::cerr << '\'' + n->string() + "' is a reserved rulename" << std::endl; ++ std::terminate(); ++#endif + } + if( print_forward_declarations && find_rule( rules_defined, v ) != rules_defined.rend() ) { + std::cout << "struct " << v << ";\n"; +@@ -461,14 +473,24 @@ namespace tao + // when we insert a normal rule, we need to check for duplicates + if( op == "=" ) { + if( !previous_rules.insert( { rname, n.get() } ).second ) { +- throw parse_error( "rule '" + rname + "' is already defined", n->begin() ); // NOLINT ++#if defined( __cpp_exceptions ) ++ throw parse_error( "rule '" + rname + "' is already defined", n->begin() ); ++#else ++ std::cerr << "rule '" + rname + "' is already defined" << std::endl; ++ std::terminate(); ++#endif + } + } + // if it is an "incremental alternation", we need to consolidate the assigned alternations + else if( op == "=/" ) { + const auto p = previous_rules.find( rname ); + if( p == previous_rules.end() ) { +- throw parse_error( "incremental alternation '" + rname + "' without previous rule definition", n->begin() ); // NOLINT ++#if defined( __cpp_exceptions ) ++ throw parse_error( "incremental alternation '" + rname + "' without previous rule definition", n->begin() ); ++#else ++ std::cerr << "incremental alternation '" + rname + "' without previous rule definition" << std::endl; ++ std::terminate(); ++#endif + } + auto& previous = p->second->children.back(); + +@@ -499,7 +521,12 @@ namespace tao + n.reset(); + } + else { ++#if defined( __cpp_exceptions ) + throw parse_error( "invalid operator '" + op + "', this should not happen!", n->begin() ); // NOLINT ++#else ++ std::cerr << "invalid operator '" + op + "', this should not happen!" << std::endl; ++ std::terminate(); ++#endif + } + } + }; +@@ -531,7 +558,12 @@ namespace tao + { + stringifier nrv; + nrv.default_ = []( const node_ptr& n ) -> std::string { ++#if defined( __cpp_exceptions ) + throw parse_error( "missing to_string() for " + n->name(), n->begin() ); // NOLINT ++#else ++ std::cerr << "missing to_string() for " + n->name() << std::endl; ++ std::terminate(); ++#endif + }; + + nrv.add< grammar::rulename >( []( const node_ptr& n ) { return get_rulename( n, true ); } ); +@@ -615,14 +647,24 @@ namespace tao + if( star == std::string::npos ) { + const auto v = remove_leading_zeroes( rep ); + if( v.empty() ) { +- throw parse_error( "repetition of zero not allowed", n->begin() ); // NOLINT ++#if defined( __cpp_exceptions ) ++ throw parse_error( "repetition of zero not allowed", n->begin() ); ++#else ++ std::cerr << "repetition of zero not allowed" << std::endl; ++ std::terminate(); ++#endif + } + return prefix + "rep< " + v + ", " + content + " >"; + } + const auto min = remove_leading_zeroes( rep.substr( 0, star ) ); + const auto max = remove_leading_zeroes( rep.substr( star + 1 ) ); + if( ( star != rep.size() - 1 ) && max.empty() ) { +- throw parse_error( "repetition maximum of zero not allowed", n->begin() ); // NOLINT ++#if defined( __cpp_exceptions ) ++ throw parse_error( "repetition maximum of zero not allowed", n->begin() ); ++#else ++ std::cerr << "repetition maximum of zero not allowed" << std::endl; ++ std::terminate(); ++#endif + } + if( min.empty() && max.empty() ) { + return prefix + "star< " + content + " >"; +@@ -650,7 +692,12 @@ namespace tao + s >> max_val; + } + if( min_val > max_val ) { +- throw parse_error( "repetition minimum which is greater than the repetition maximum not allowed", n->begin() ); // NOLINT ++#if defined( __cpp_exceptions ) ++ throw parse_error( "repetition minimum which is greater than the repetition maximum not allowed", n->begin() ); ++#else ++ std::cerr << "repetition minimum which is greater than the repetition maximum not allowed" << std::endl; ++ std::terminate(); ++#endif + } + if( ( min_val == 1 ) && ( max_val == 1 ) ) { + // note: content can not be used here! +@@ -704,6 +751,7 @@ int main( int argc, char** argv ) // NOLINT + } + + file_input<> in( argv[ 1 ] ); ++#if defined( __cpp_exceptions ) + try { + const auto root = parse_tree::parse< abnf::grammar::rulelist, abnf::selector, nothing, abnf::grammar::error_control >( in ); + +@@ -721,6 +769,21 @@ int main( int argc, char** argv ) // NOLINT + << in.line_at( p ) << std::endl + << std::string( p.byte_in_line, ' ' ) << '^' << std::endl; + } ++#else ++ if( const auto root = parse_tree::parse< abnf::grammar::rulelist, abnf::selector, nothing, abnf::grammar::error_control >( in ) ) { ++ for( const auto& rule : root->children ) { ++ abnf::rules_defined.push_back( abnf::get_rulename( rule->children.front() ) ); ++ } ++ ++ for( const auto& rule : root->children ) { ++ std::cout << abnf::to_string( rule ) << '\n'; ++ } ++ } ++ else { ++ std::cerr << "error occurred" << std::endl; ++ return 1; ++ } ++#endif + + return 0; + } +diff --git a/src/example/pegtl/analyze.cpp b/src/example/pegtl/analyze.cpp +index 6d841594..127d3e47 100644 +--- a/src/example/pegtl/analyze.cpp ++++ b/src/example/pegtl/analyze.cpp +@@ -21,7 +21,6 @@ struct bar + int main() // NOLINT + { + if( analyze< foo >() != 0 ) { +- std::cout << "there are problems" << std::endl; + return 1; + } + return 0; +diff --git a/src/example/pegtl/calculator.cpp b/src/example/pegtl/calculator.cpp +index ba609850..9ada05b0 100644 +--- a/src/example/pegtl/calculator.cpp ++++ b/src/example/pegtl/calculator.cpp +@@ -187,7 +187,7 @@ namespace calculator + // Comments are introduced by a '#' and proceed to the end-of-line/file. + + struct comment +- : if_must< one< '#' >, until< eolf > > ++ : seq< one< '#' >, until< eolf > > + { + }; + +@@ -262,7 +262,7 @@ namespace calculator + // proceed with an expression and a ')'. + + struct bracket +- : if_must< one< '(' >, expression, one< ')' > > ++ : seq< one< '(' >, expression, one< ')' > > + { + }; + +@@ -286,7 +286,7 @@ namespace calculator + // The top-level grammar allows one expression and then expects eof. + + struct grammar +- : must< expression, eof > ++ : seq< expression, eof > + { + }; + +@@ -353,11 +353,13 @@ int main( int argc, char** argv ) // NOLINT + // Parse and process the command-line arguments as calculator expressions... + + pegtl::argv_input<> in( argv, i ); +- pegtl::parse< calculator::grammar, calculator::action >( in, b, s ); +- +- // ...and print the respective results to std::cout. +- +- std::cout << s.finish() << std::endl; ++ if( pegtl::parse< calculator::grammar, calculator::action >( in, b, s ) ) { ++ // ...and print the respective results to std::cout. ++ std::cout << s.finish() << std::endl; ++ } ++ else { ++ std::cerr << "parse error for: " << argv[ i ] << std::endl; ++ } + } + return 0; + } +diff --git a/src/example/pegtl/csv1.cpp b/src/example/pegtl/csv1.cpp +index 756c7a6b..a030aabb 100644 +--- a/src/example/pegtl/csv1.cpp ++++ b/src/example/pegtl/csv1.cpp +@@ -30,8 +30,8 @@ namespace csv1 + // clang-format off + struct value : pegtl::plus< pegtl::digit > {}; + struct value_item : pegtl::pad< value, pegtl::blank > {}; +- struct value_list : pegtl::list_must< value_item, pegtl::one< ',' > > {}; +- struct value_line : pegtl::if_must< value_list, pegtl::eolf > {}; ++ struct value_list : pegtl::list< value_item, pegtl::one< ',' > > {}; ++ struct value_line : pegtl::seq< value_list, pegtl::eolf > {}; + struct comment_line : pegtl::seq< pegtl::one< '#' >, pegtl::until< pegtl::eolf > > {}; + struct line : pegtl::sor< comment_line, value_line > {}; + struct file : pegtl::until< pegtl::eof, line > {}; +@@ -93,7 +93,10 @@ int main( int argc, char** argv ) // NOLINT + for( int i = 1; i < argc; ++i ) { + pegtl::file_input<> in( argv[ i ] ); + csv1::result_data data; +- pegtl::parse< pegtl::must< csv1::file >, csv1::action, csv1::control >( in, data ); ++ if( !pegtl::parse< pegtl::seq< csv1::file >, csv1::action, csv1::control >( in, data ) ) { ++ std::cerr << "parse error" << std::endl; ++ return 1; ++ } + for( const auto& line : data ) { + assert( !line.empty() ); // The grammar doesn't allow empty lines. + std::cout << line.front(); +diff --git a/src/example/pegtl/csv2.cpp b/src/example/pegtl/csv2.cpp +index 5de569f4..9c1856cf 100644 +--- a/src/example/pegtl/csv2.cpp ++++ b/src/example/pegtl/csv2.cpp +@@ -1,6 +1,7 @@ + // Copyright (c) 2016-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#include + #include + #include + +@@ -25,7 +26,7 @@ namespace csv2 + // clang-format off + template< char C > struct string_without : pegtl::star< pegtl::not_one< C, 10, 13 > > {}; + struct plain_value : string_without< ',' > {}; +- struct quoted_value : pegtl::if_must< pegtl::one< '"' >, string_without< '"' >, pegtl::one< '"' > > {}; ++ struct quoted_value : pegtl::seq< pegtl::one< '"' >, string_without< '"' >, pegtl::one< '"' > > {}; + struct value : pegtl::sor< quoted_value, plain_value > {}; + template< unsigned N > struct line : pegtl::seq< value, pegtl::rep< N - 1, pegtl::one< ',' >, value >, pegtl::eol > {}; + template< unsigned N > struct file : pegtl::until< pegtl::eof, line< N > > { static_assert( N, "N must be positive" ); }; +@@ -114,7 +115,8 @@ namespace csv2 + static void apply( const Input& in, result_data< N >& data ) + { + if( data.temp.size() != N ) { +- throw pegtl::parse_error( "column count mismatch", in ); ++ std::cerr << "column count mismatch " << to_string(in.position()) << std::endl; ++ std::terminate(); + } + tuple_t temp; + tuple_init< N - 1 >::init( temp, data.temp ); +@@ -176,9 +178,13 @@ int main( int argc, char** argv ) // NOLINT + pegtl::file_input<> in( argv[ i ] ); + constexpr unsigned number_of_columns = 3; + csv2::result_data< number_of_columns > data; +- pegtl::parse< pegtl::must< csv2::file< number_of_columns > >, csv2::action >( in, data ); +- for( const auto& line : data.result ) { +- csv2::print_tuple( line ); ++ if( pegtl::parse< pegtl::seq< csv2::file< number_of_columns > >, csv2::action >( in, data ) ) { ++ for( const auto& line : data.result ) { ++ csv2::print_tuple( line ); ++ } ++ } ++ else { ++ std::cerr << "parse error" << std::endl; + } + } + return 0; +diff --git a/src/example/pegtl/dynamic_match.cpp b/src/example/pegtl/dynamic_match.cpp +index b860d8cb..6c5bd0b3 100644 +--- a/src/example/pegtl/dynamic_match.cpp ++++ b/src/example/pegtl/dynamic_match.cpp +@@ -55,9 +55,8 @@ namespace dynamic + }; + + struct grammar +- : pegtl::if_must< long_literal_open, pegtl::until< long_literal_close, long_literal_body >, pegtl::eof > +- { +- }; ++ : pegtl::seq< long_literal_open, pegtl::until< long_literal_close, long_literal_body >, pegtl::eof > ++ {}; + + template< typename Rule > + struct action +@@ -93,10 +92,14 @@ int main( int argc, char** argv ) // NOLINT + std::string body; + + pegtl::argv_input<> in( argv, 1 ); +- pegtl::parse< dynamic::grammar, dynamic::action >( in, id, body ); +- +- std::cout << "long literal id was: " << id << std::endl; +- std::cout << "long literal body was: " << body << std::endl; ++ if( pegtl::parse< dynamic::grammar, dynamic::action >( in, id, body ) ) { ++ std::cout << "long literal id was: " << id << std::endl; ++ std::cout << "long literal body was: " << body << std::endl; ++ } ++ else { ++ std::cerr << "parse error for: " << argv[ 1 ] << std::endl; ++ return 1; ++ } + } + return 0; + } +diff --git a/src/example/pegtl/hello_world.cpp b/src/example/pegtl/hello_world.cpp +index 5e53e089..23976edb 100644 +--- a/src/example/pegtl/hello_world.cpp ++++ b/src/example/pegtl/hello_world.cpp +@@ -13,7 +13,7 @@ namespace hello + // clang-format off + struct prefix : pegtl::string< 'H', 'e', 'l', 'l', 'o', ',', ' ' > {}; + struct name : pegtl::plus< pegtl::alpha > {}; +- struct grammar : pegtl::must< prefix, name, pegtl::one< '!' >, pegtl::eof > {}; ++ struct grammar : pegtl::seq< prefix, name, pegtl::one< '!' >, pegtl::eof > {}; + // clang-format on + + template< typename Rule > +@@ -39,8 +39,11 @@ int main( int argc, char** argv ) // NOLINT + std::string name; + + pegtl::argv_input<> in( argv, 1 ); +- pegtl::parse< hello::grammar, hello::action >( in, name ); +- +- std::cout << "Good bye, " << name << "!" << std::endl; ++ if( pegtl::parse< hello::grammar, hello::action >( in, name ) ) { ++ std::cout << "Good bye, " << name << "!" << std::endl; ++ } ++ else { ++ std::cerr << "I don't understand." << std::endl; ++ } + } + } +diff --git a/src/example/pegtl/indent_aware.cpp b/src/example/pegtl/indent_aware.cpp +index 7fd09def..79373b4e 100644 +--- a/src/example/pegtl/indent_aware.cpp ++++ b/src/example/pegtl/indent_aware.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2018-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + #include +@@ -214,3 +218,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/json_build.cpp b/src/example/pegtl/json_build.cpp +index dca2e399..a6d785c4 100644 +--- a/src/example/pegtl/json_build.cpp ++++ b/src/example/pegtl/json_build.cpp +@@ -2,6 +2,8 @@ + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + + #include ++#include ++#include + #include + #include + +@@ -156,19 +158,35 @@ namespace examples + } + }; + +- using grammar = pegtl::must< pegtl::json::text, pegtl::eof >; ++ using grammar = pegtl::seq< pegtl::json::text, pegtl::eof >; + + } // namespace examples + + int main( int argc, char** argv ) // NOLINT + { + if( argc != 2 ) { +- std::cerr << "usage: " << argv[ 0 ] << " "; ++ std::cerr << "usage: " << argv[ 0 ] << " \n"; + } + else { + examples::json_state state; + pegtl::file_input<> in( argv[ 1 ] ); +- pegtl::parse< examples::grammar, examples::action, examples::errors >( in, state ); ++#if defined( __cpp_exceptions ) ++ try { ++ pegtl::parse< examples::grammar, examples::action, examples::errors >( in, state ); ++ } ++ catch( const pegtl::parse_error& e ) { ++ const auto p = e.positions.front(); ++ std::cerr << e.what() << '\n' ++ << in.line_at( p ) << '\n' ++ << std::setw( p.byte_in_line ) << '^' << std::endl; ++ return 1; ++ } ++#else ++ if( !pegtl::parse< examples::grammar, examples::action, examples::errors >( in, state ) ) { ++ std::cerr << "error occurred" << std::endl; ++ return 1; ++ } ++#endif + assert( state.keys.empty() ); + assert( state.arrays.empty() ); + assert( state.objects.empty() ); +diff --git a/src/example/pegtl/json_count.cpp b/src/example/pegtl/json_count.cpp +index 1f2f041b..3eda8211 100644 +--- a/src/example/pegtl/json_count.cpp ++++ b/src/example/pegtl/json_count.cpp +@@ -12,7 +12,7 @@ + #include + + using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT +-using grammar = must< json::text, eof >; ++using grammar = seq< json::text, eof >; + + int main( int argc, char** argv ) // NOLINT + { +diff --git a/src/example/pegtl/json_errors.hpp b/src/example/pegtl/json_errors.hpp +index b1c537c8..74666c44 100644 +--- a/src/example/pegtl/json_errors.hpp ++++ b/src/example/pegtl/json_errors.hpp +@@ -10,10 +10,7 @@ + namespace examples + { + // This file shows how to throw exceptions with +- // custom error messages for parse errors. A custom +- // control class is created that delegates everything +- // to the PEGTL default control class tao::TAO_PEGTL_NAMESPACE::normal<> +- // except for the throwing of exceptions: ++ // custom error messages for parse errors. + + template< typename Rule > + struct errors +@@ -24,13 +21,20 @@ namespace examples + template< typename Input, typename... States > + static void raise( const Input& in, States&&... /*unused*/ ) + { ++#if defined( __cpp_exceptions ) + throw tao::TAO_PEGTL_NAMESPACE::parse_error( error_message, in ); ++#else ++ (void)in; ++ std::terminate(); ++#endif + } + }; + + // The following specialisations of the static string + // member are then used in the exception messages: + ++#if defined( __cpp_exceptions ) ++ + // clang-format off + template<> const std::string errors< tao::TAO_PEGTL_NAMESPACE::json::text >::error_message = "no valid JSON"; // NOLINT + +@@ -51,6 +55,13 @@ namespace examples + template<> const std::string errors< tao::TAO_PEGTL_NAMESPACE::eof >::error_message = "unexpected character after JSON value"; // NOLINT + // clang-format on + ++#else ++ ++ template< typename Rule > ++ using control = tao::pegtl::normal< Rule >; ++ ++#endif ++ + // The raise()-function-template is instantiated exactly + // for the specialisations of errors< Rule > for which a + // parse error can be generated, therefore the string +diff --git a/src/example/pegtl/json_parse.cpp b/src/example/pegtl/json_parse.cpp +index 63cf5033..ab722d12 100644 +--- a/src/example/pegtl/json_parse.cpp ++++ b/src/example/pegtl/json_parse.cpp +@@ -6,13 +6,20 @@ + #include "json_errors.hpp" + + using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT +-using grammar = must< json::text, eof >; ++using grammar = seq< json::text, eof >; + + int main( int argc, char** argv ) // NOLINT + { + for( int i = 1; i < argc; ++i ) { + argv_input<> in( argv, i ); ++#if defined( __cpp_exceptions ) + parse< grammar, nothing, examples::errors >( in ); ++#else ++ if( !parse< grammar, nothing, examples::errors >( in ) ) { ++ std::perror("error occurred"); ++ return 1; ++ } ++#endif + } + return 0; + } +diff --git a/src/example/pegtl/lua53_parse.cpp b/src/example/pegtl/lua53_parse.cpp +index 0c2e8be2..84c8ccbf 100644 +--- a/src/example/pegtl/lua53_parse.cpp ++++ b/src/example/pegtl/lua53_parse.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + #include +@@ -345,3 +349,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/modulus_match.cpp b/src/example/pegtl/modulus_match.cpp +index b659cec9..67bd59ee 100644 +--- a/src/example/pegtl/modulus_match.cpp ++++ b/src/example/pegtl/modulus_match.cpp +@@ -29,9 +29,8 @@ namespace modulus + }; + + struct grammar +- : until< eolf, must< my_rule< 3 > > > +- { +- }; ++ : until< eolf, my_rule< 3 > > ++ {}; + + } // namespace modulus + +@@ -39,7 +38,9 @@ int main( int argc, char** argv ) // NOLINT + { + if( argc > 1 ) { + argv_input<> in( argv, 1 ); +- parse< modulus::grammar >( in ); ++ if( !parse< modulus::grammar >( in ) ) { ++ return 1; ++ } + } + return 0; + } +diff --git a/src/example/pegtl/parse_tree.cpp b/src/example/pegtl/parse_tree.cpp +index f7e1b21f..7493ac3e 100644 +--- a/src/example/pegtl/parse_tree.cpp ++++ b/src/example/pegtl/parse_tree.cpp +@@ -28,12 +28,12 @@ namespace example + struct close_bracket : seq< star< space >, one< ')' > > {}; + + struct expression; +- struct bracketed : if_must< open_bracket, expression, close_bracket > {}; ++ struct bracketed : seq< open_bracket, expression, close_bracket > {}; + struct value : sor< integer, variable, bracketed >{}; +- struct product : list_must< value, sor< multiply, divide > > {}; +- struct expression : list_must< product, sor< plus, minus > > {}; ++ struct product : list< value, sor< multiply, divide > > {}; ++ struct expression : list< product, sor< plus, minus > > {}; + +- struct grammar : must< expression, eof > {}; ++ struct grammar : seq< expression, eof > {}; + // clang-format on + + // after a node is stored successfully, you can add an optional transformer like this: +@@ -104,19 +104,7 @@ int main( int argc, char** argv ) + return 1; + } + argv_input<> in( argv, 1 ); +- try { +- const auto root = parse_tree::parse< example::grammar, example::selector >( in ); +- parse_tree::print_dot( std::cout, *root ); +- return 0; +- } +- catch( const parse_error& e ) { +- const auto p = e.positions.front(); +- std::cerr << e.what() << std::endl +- << in.line_at( p ) << std::endl +- << std::string( p.byte_in_line, ' ' ) << '^' << std::endl; +- } +- catch( const std::exception& e ) { +- std::cerr << e.what() << std::endl; +- } +- return 1; ++ const auto root = parse_tree::parse< example::grammar, example::selector >( in ); ++ parse_tree::print_dot( std::cout, *root ); ++ return 0; + } +diff --git a/src/example/pegtl/proto3.cpp b/src/example/pegtl/proto3.cpp +index 174ede3f..75057d1c 100644 +--- a/src/example/pegtl/proto3.cpp ++++ b/src/example/pegtl/proto3.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #define TAO_PEGTL_PRETTY_DEMANGLE 1 + + #include +@@ -144,3 +148,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/recover.cpp b/src/example/pegtl/recover.cpp +index 3a26b4da..f779bcf6 100644 +--- a/src/example/pegtl/recover.cpp ++++ b/src/example/pegtl/recover.cpp +@@ -11,6 +11,10 @@ + // + // Try: build/src/example/pegtl/recover '1+2*3;1+2*(3-)-4;-5;6/;7*(8+9)' + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + +@@ -57,7 +61,7 @@ struct my_action< skipping< T > > + static void apply( const Input& in, bool& error ) + { + if( !error ) { +- std::cout << in.position() << ": Invalid expression \"" << in.string() << "\"" << std::endl; ++ std::cout << to_string(in.position()) << ": Invalid expression \"" << in.string() << "\"" << std::endl; + } + error = true; + } +@@ -70,7 +74,7 @@ struct found + static void apply( const Input& in, bool& error ) + { + if( !error ) { +- std::cout << in.position() << ": Found " << internal::demangle< R >() << ": \"" << in.string() << "\"" << std::endl; ++ std::cout << to_string(in.position()) << ": Found " << internal::demangle< R >() << ": \"" << in.string() << "\"" << std::endl; + } + } + }; +@@ -104,7 +108,7 @@ struct my_control + template< typename Input, typename... States > + static void raise( const Input& in, States&&... /*unused*/ ) + { +- std::cout << in.position() << ": Parse error matching " << internal::demangle< Rule >() << std::endl; ++ std::cout << to_string(in.position()) << ": Parse error matching " << internal::demangle< Rule >() << std::endl; + throw parse_error( "parse error matching " + internal::demangle< Rule >(), in ); + } + }; +@@ -118,3 +122,5 @@ int main( int argc, char** argv ) + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/s_expression.cpp b/src/example/pegtl/s_expression.cpp +index 04e80793..79f832d7 100644 +--- a/src/example/pegtl/s_expression.cpp ++++ b/src/example/pegtl/s_expression.cpp +@@ -1,6 +1,11 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ ++#include + #include + + #include +@@ -120,3 +125,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/symbol_table.cpp b/src/example/pegtl/symbol_table.cpp +index 5d072688..8cbcc615 100644 +--- a/src/example/pegtl/symbol_table.cpp ++++ b/src/example/pegtl/symbol_table.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2018-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + #include +@@ -103,3 +107,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/unescape.cpp b/src/example/pegtl/unescape.cpp +index 0a11a9ad..96a9b979 100644 +--- a/src/example/pegtl/unescape.cpp ++++ b/src/example/pegtl/unescape.cpp +@@ -4,7 +4,6 @@ + #include + + #include +- + #include + + using namespace tao::TAO_PEGTL_NAMESPACE; // NOLINT +@@ -18,9 +17,9 @@ namespace example + // - A backslash followed by one of the characters listed in the grammar below. + + // clang-format off +- struct escaped_x : seq< one< 'x' >, rep< 2, must< xdigit > > > {}; +- struct escaped_u : seq< one< 'u' >, rep< 4, must< xdigit > > > {}; +- struct escaped_U : seq< one< 'U' >, rep< 8, must< xdigit > > > {}; ++ struct escaped_x : seq< one< 'x' >, rep< 2, xdigit > > {}; ++ struct escaped_u : seq< one< 'u' >, rep< 4, xdigit > > {}; ++ struct escaped_U : seq< one< 'U' >, rep< 8, xdigit > > {}; + struct escaped_c : one< '\'', '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' > {}; + + struct escaped : sor< escaped_x, +@@ -28,10 +27,10 @@ namespace example + escaped_U, + escaped_c > {}; + +- struct character : if_must_else< one< '\\' >, escaped, utf8::range< 0x20, 0x10FFFF > > {}; +- struct literal : if_must< one< '"' >, until< one< '"' >, character > > {}; ++ struct character : if_then_else< one< '\\' >, escaped, utf8::range< 0x20, 0x10FFFF > > {}; ++ struct literal : seq< one< '"' >, until< one< '"' >, character > > {}; + +- struct padded : must< pad< literal, blank >, eof > {}; ++ struct padded : seq< pad< literal, blank >, eof > {}; + + // Action class that uses the actions from tao/pegtl/contrib/unescape.hpp to + // produce a UTF-8 encoded result string where all escape sequences are +@@ -53,8 +52,12 @@ int main( int argc, char** argv ) // NOLINT + for( int i = 1; i < argc; ++i ) { + std::string s; + argv_input<> in( argv, i ); +- parse< example::padded, example::action >( in, s ); +- std::cout << "argv[ " << i << " ] = " << s << std::endl; ++ if( parse< example::padded, example::action >( in, s ) ) { ++ std::cout << "argv[ " << i << " ] = " << s << std::endl; ++ } ++ else { ++ std::cerr << "error parsing: " << argv[ i ] << std::endl; ++ } + } + return 0; + } +diff --git a/src/example/pegtl/uri.cpp b/src/example/pegtl/uri.cpp +index f0db1ca7..c7711817 100644 +--- a/src/example/pegtl/uri.cpp ++++ b/src/example/pegtl/uri.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + +@@ -87,3 +91,5 @@ int main( int argc, char** argv ) + } + return 0; + } ++ ++#endif +diff --git a/src/example/pegtl/uri_trace.cpp b/src/example/pegtl/uri_trace.cpp +index ca3733e5..1b41a748 100644 +--- a/src/example/pegtl/uri_trace.cpp ++++ b/src/example/pegtl/uri_trace.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + + #include +@@ -20,3 +24,5 @@ int main( int argc, char** argv ) // NOLINT + } + return 0; + } ++ ++#endif +diff --git a/src/test/pegtl/CMakeLists.txt b/src/test/pegtl/CMakeLists.txt +index e9c51912..cf26d88c 100644 +--- a/src/test/pegtl/CMakeLists.txt ++++ b/src/test/pegtl/CMakeLists.txt +@@ -132,8 +132,8 @@ foreach(testsourcefile ${test_sources}) + endif() + endforeach(testsourcefile) + +-if(glob_test_sources) +- foreach(ignored_source_file ${glob_test_sources}) +- message(SEND_ERROR "File ${ignored_source_file} in src/test/pegtl is ignored") +- endforeach(ignored_source_file) +-endif() ++#if(glob_test_sources) ++# foreach(ignored_source_file ${glob_test_sources}) ++# message(SEND_ERROR "File ${ignored_source_file} in src/test/pegtl is ignored") ++# endforeach(ignored_source_file) ++#endif() +diff --git a/src/test/pegtl/actions_one.cpp b/src/test/pegtl/actions_one.cpp +index 21bacb80..e94f3db5 100644 +--- a/src/test/pegtl/actions_one.cpp ++++ b/src/test/pegtl/actions_one.cpp +@@ -9,7 +9,7 @@ namespace tao + { + namespace test1 + { +- struct fiz : if_must< at< one< 'a' > >, two< 'a' > > ++ struct fiz : seq< at< one< 'a' > >, two< 'a' > > + { + }; + +diff --git a/src/test/pegtl/actions_two.cpp b/src/test/pegtl/actions_two.cpp +index 3fc9add1..fe4c5f65 100644 +--- a/src/test/pegtl/actions_two.cpp ++++ b/src/test/pegtl/actions_two.cpp +@@ -121,8 +121,7 @@ namespace tao + { + const char* foo = "f"; + memory_input<> in( foo, foo + 1, count_source, count_byte, count_line, count_byte_in_line ); +- const auto result = parse< must< alpha >, count_action >( in ); +- TAO_PEGTL_TEST_ASSERT( result ); ++ TAO_PEGTL_TEST_ASSERT( parse< alpha, count_action >( in ) ); + } + + } // namespace test1 +diff --git a/src/test/pegtl/analyze_cycles.cpp b/src/test/pegtl/analyze_cycles.cpp +index 3ad27072..e4029a11 100644 +--- a/src/test/pegtl/analyze_cycles.cpp ++++ b/src/test/pegtl/analyze_cycles.cpp +@@ -260,6 +260,7 @@ namespace tao + }; + verify_analyze< tst >( __LINE__, __FILE__, false, true ); + } ++#if defined( __cpp_exceptions ) + { + struct tst : list_must< any, any > + { +@@ -284,6 +285,7 @@ namespace tao + }; + verify_analyze< tst >( __LINE__, __FILE__, false, true ); + } ++#endif + { + struct tst : plus< pad_opt< alpha, digit > > + { +diff --git a/src/test/pegtl/buffer_input.cpp b/src/test/pegtl/buffer_input.cpp +index 2f45336f..999b35b8 100644 +--- a/src/test/pegtl/buffer_input.cpp ++++ b/src/test/pegtl/buffer_input.cpp +@@ -27,15 +27,19 @@ namespace tao + TAO_PEGTL_TEST_ASSERT( parse_cstring< seq< string< 'a', 'b', 'c' >, eof > >( "abc", TAO_TEST_LINE, 128 ) ); + + // We need one extra byte in the buffer so that eof calling in.empty() calling in.require( 1 ) does not throw a "require beyond end of buffer" exception. ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< seq< rep< chunk_size + 2, one< 'a' > >, eof > >( std::string( std::size_t( chunk_size + 2 ), 'a' ).c_str(), TAO_TEST_LINE, 2 ) ); ++#endif + TAO_PEGTL_TEST_ASSERT( parse_cstring< seq< rep< chunk_size + 2, one< 'a' > >, eof > >( std::string( std::size_t( chunk_size + 2 ), 'a' ).c_str(), TAO_TEST_LINE, 3 ) ); + + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< chunk_size + 9, one< 'a' > > >( std::string( std::size_t( chunk_size + 9 ), 'a' ).c_str(), TAO_TEST_LINE, 9 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< chunk_size + 9, one< 'a' > > >( std::string( std::size_t( chunk_size + 10 ), 'a' ).c_str(), TAO_TEST_LINE, 9 ) ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< chunk_size + 10, one< 'a' > > >( std::string( std::size_t( chunk_size + 10 ), 'a' ).c_str(), TAO_TEST_LINE, 9 ) ); + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< chunk_size + 10, one< 'a' > > >( std::string( std::size_t( chunk_size + 11 ), 'a' ).c_str(), TAO_TEST_LINE, 9 ) ); + TAO_PEGTL_TEST_THROWS( parse_cstring< seq< rep< chunk_size + 10, one< 'a' > >, eof > >( std::string( std::size_t( chunk_size + 10 ), 'a' ).c_str(), TAO_TEST_LINE, 9 ) ); + TAO_PEGTL_TEST_THROWS( parse_cstring< seq< rep< chunk_size + 10, one< 'a' > >, eof > >( std::string( std::size_t( chunk_size + 10 ), 'a' ).c_str(), TAO_TEST_LINE, 10 ) ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/change_action_and_state.cpp b/src/test/pegtl/change_action_and_state.cpp +index 05a8e122..3fbe829b 100644 +--- a/src/test/pegtl/change_action_and_state.cpp ++++ b/src/test/pegtl/change_action_and_state.cpp +@@ -26,9 +26,7 @@ namespace tao + { + static void apply0( int& c ) + { +- if( c != 0 ) { +- throw std::runtime_error( "fail1" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 0 ); + c = 1; + } + }; +@@ -44,9 +42,7 @@ namespace tao + v = 6; + } + else { +- if( c != 1 ) { +- throw std::runtime_error( "fail2" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 1 ); + v = 2; + } + } +@@ -54,9 +50,7 @@ namespace tao + template< typename Input > + void success( const Input& /*unused*/, int& c ) + { +- if( v != 3 ) { +- throw std::runtime_error( "fail4" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 3 ); + c = 4; + } + }; +@@ -71,7 +65,7 @@ namespace tao + { + static void apply0( S& /*s*/ ) + { +- throw std::runtime_error( "fail4" ); ++ TAO_PEGTL_TEST_UNREACHABLE; + } + }; + +@@ -80,9 +74,7 @@ namespace tao + { + static void apply0( S& s ) + { +- if( s.v != 2 ) { +- throw std::runtime_error( "fail5" ); +- } ++ TAO_PEGTL_TEST_ASSERT( s.v == 2 ); + s.v = 3; + } + }; +diff --git a/src/test/pegtl/change_action_and_states.cpp b/src/test/pegtl/change_action_and_states.cpp +index 00382760..eee00458 100644 +--- a/src/test/pegtl/change_action_and_states.cpp ++++ b/src/test/pegtl/change_action_and_states.cpp +@@ -26,9 +26,7 @@ namespace tao + { + static void apply0( int& c ) + { +- if( c != 0 ) { +- throw std::runtime_error( "fail1" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 0 ); + c = 1; + } + }; +@@ -40,18 +38,14 @@ namespace tao + // not called because my_action_2 is active + static void apply0( int& /*v*/ ) + { +- throw std::runtime_error( "fail2" ); ++ TAO_PEGTL_TEST_UNREACHABLE; + } + + template< typename Input > + static void success( const Input& /*unused*/, int& v, int& c ) + { +- if( v != 2 ) { +- throw std::runtime_error( "fail3" ); +- } +- if( c != 1 ) { +- throw std::runtime_error( "fail4" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 2 ); ++ TAO_PEGTL_TEST_ASSERT( c == 1 ); + c = 3; + } + }; +@@ -61,7 +55,7 @@ namespace tao + { + static void apply0( int& /*c*/ ) + { +- throw std::runtime_error( "fail5" ); ++ TAO_PEGTL_TEST_UNREACHABLE; + } + }; + +@@ -70,9 +64,7 @@ namespace tao + { + static void apply0( int& v ) + { +- if( v != 0 ) { +- throw std::runtime_error( "fail6" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 0 ); + v = 2; + } + }; +diff --git a/src/test/pegtl/change_state.cpp b/src/test/pegtl/change_state.cpp +index 2db651ea..95d655db 100644 +--- a/src/test/pegtl/change_state.cpp ++++ b/src/test/pegtl/change_state.cpp +@@ -22,9 +22,7 @@ namespace tao + { + static void apply0( int& c ) + { +- if( c != 0 ) { +- throw std::runtime_error( "fail1" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 0 ); + c = 1; + } + }; +@@ -40,9 +38,7 @@ namespace tao + v = 6; + } + else { +- if( c != 1 ) { +- throw std::runtime_error( "fail2" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 1 ); + v = 2; + } + } +@@ -50,9 +46,7 @@ namespace tao + template< typename Input > + void success( const Input& /*unused*/, int& c ) + { +- if( v != 3 ) { +- throw std::runtime_error( "fail4" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 3 ); + c = 4; + } + }; +@@ -63,9 +57,7 @@ namespace tao + { + static void apply0( S& s ) + { +- if( s.v != 2 ) { +- throw std::runtime_error( "fail5" ); +- } ++ TAO_PEGTL_TEST_ASSERT( s.v == 2 ); + s.v = 3; + } + }; +diff --git a/src/test/pegtl/change_states.cpp b/src/test/pegtl/change_states.cpp +index e286aa2e..6a5d3b97 100644 +--- a/src/test/pegtl/change_states.cpp ++++ b/src/test/pegtl/change_states.cpp +@@ -22,9 +22,7 @@ namespace tao + { + static void apply0( int& c ) + { +- if( c != 0 ) { +- throw std::runtime_error( "fail1" ); +- } ++ TAO_PEGTL_TEST_ASSERT( c == 0 ); + c = 1; + } + }; +@@ -35,21 +33,15 @@ namespace tao + { + static void apply0( int& v ) + { +- if( v != 0 ) { +- throw std::runtime_error( "fail6" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 0 ); + v = 2; + } + + template< typename Input > + static void success( const Input& /*unused*/, int& v, int& c ) + { +- if( v != 2 ) { +- throw std::runtime_error( "fail3" ); +- } +- if( c != 1 ) { +- throw std::runtime_error( "fail4" ); +- } ++ TAO_PEGTL_TEST_ASSERT( v == 2 ); ++ TAO_PEGTL_TEST_ASSERT( c == 1 ); + c = 3; + } + }; +diff --git a/src/test/pegtl/contrib_http.cpp b/src/test/pegtl/contrib_http.cpp +index ed23bca3..75fc9d54 100644 +--- a/src/test/pegtl/contrib_http.cpp ++++ b/src/test/pegtl/contrib_http.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2019-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" + +@@ -97,3 +101,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +\ No newline at end of file +diff --git a/src/test/pegtl/contrib_integer.cpp b/src/test/pegtl/contrib_integer.cpp +index 149c42e6..89a6ff54 100644 +--- a/src/test/pegtl/contrib_integer.cpp ++++ b/src/test/pegtl/contrib_integer.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2018-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include + #include + +@@ -147,3 +151,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/contrib_json.cpp b/src/test/pegtl/contrib_json.cpp +index a0803b49..60f7e274 100644 +--- a/src/test/pegtl/contrib_json.cpp ++++ b/src/test/pegtl/contrib_json.cpp +@@ -3,7 +3,6 @@ + + #include "test.hpp" + #include "verify_analyze.hpp" +-#include "verify_fail.hpp" + #include "verify_rule.hpp" + + #include +@@ -13,19 +12,7 @@ namespace tao + { + namespace TAO_PEGTL_NAMESPACE + { +- template< typename Rule > +- void verify_file_fail( const std::size_t line, const char* file, const std::string& s ) +- { +- file_input<> in( s ); +- try { +- parse< Rule >( in ); +- TAO_PEGTL_TEST_FAILED( "expected exception" ); +- } +- catch( ... ) { +- } +- } +- +- using GRAMMAR = must< json::text, eof >; ++ using GRAMMAR = seq< json::text, eof >; + + void unit_test() + { +@@ -58,34 +45,34 @@ namespace tao + verify_rule< GRAMMAR >( __LINE__, __FILE__, "[\"\xF4\x8F\xBF\xBF\"]", result_type::success, 0 ); // largest allowed codepoint U+10FFFF + verify_rule< GRAMMAR >( __LINE__, __FILE__, "[\"\U0010FFFF\"]", result_type::success, 0 ); // largest allowed codepoint U+10FFFF + +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " [" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " ]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[ " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "] " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " [ " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, " ] " ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\a\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\c\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\d\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\e\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\v\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\'\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\b\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\f\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\n\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\r\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\t\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\\\\\\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\\u12\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\xFF\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\xF4\x90\x80\x80\"]" ); +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "[\"\xF7\xBF\xBF\xBF\"]" ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " [", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " ]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[ ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "] ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " [ ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( " ] ", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\a\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\c\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\d\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\e\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\v\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\'\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\b\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\f\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\n\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\r\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\t\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\\\\\\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\\u12\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\xFF\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\xF4\x90\x80\x80\"]", "" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( memory_input<>( "[\"\xF7\xBF\xBF\xBF\"]", "" ) ) ); + + TAO_PEGTL_TEST_ASSERT( parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/pass1.json" ) ) ); + TAO_PEGTL_TEST_ASSERT( parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/pass2.json" ) ) ); +@@ -93,45 +80,45 @@ namespace tao + + TAO_PEGTL_TEST_ASSERT( parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/blns.json" ) ) ); + +- // verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail1.json" ); // disabled as it is valid now +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail2.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail3.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail4.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail5.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail6.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail7.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail8.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail9.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail10.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail11.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail12.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail13.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail14.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail15.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail16.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail17.json" ); +- // verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail18.json" ); // disabled as deep nesting is allowed +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail19.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail20.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail21.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail22.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail23.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail24.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail25.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail26.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail27.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail28.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail29.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail30.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail31.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail32.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail33.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail34.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail35.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail36.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail37.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail38.json" ); +- verify_file_fail< GRAMMAR >( __LINE__, __FILE__, "src/test/pegtl/data/fail39.json" ); ++ // TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input( "src/test/pegtl/data/fail1.json" ) )); // disabled as it is valid now ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail2.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail3.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail4.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail5.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail6.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail7.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail8.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail9.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail10.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail11.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail12.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail13.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail14.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail15.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail16.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail17.json" ) ) ); ++ // TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input( "src/test/pegtl/data/fail18.json" ) )); // disabled as deep nesting is allowed ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail19.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail20.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail21.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail22.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail23.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail24.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail25.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail26.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail27.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail28.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail29.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail30.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail31.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail32.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail33.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail34.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail35.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail36.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail37.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail38.json" ) ) ); ++ TAO_PEGTL_TEST_ASSERT( !parse< GRAMMAR >( file_input<>( "src/test/pegtl/data/fail39.json" ) ) ); + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/contrib_parse_tree.cpp b/src/test/pegtl/contrib_parse_tree.cpp +index 60a45f95..97bdda5f 100644 +--- a/src/test/pegtl/contrib_parse_tree.cpp ++++ b/src/test/pegtl/contrib_parse_tree.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2018-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + + #include +@@ -58,3 +62,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/contrib_raw_string.cpp b/src/test/pegtl/contrib_raw_string.cpp +index d7037728..93960fff 100644 +--- a/src/test/pegtl/contrib_raw_string.cpp ++++ b/src/test/pegtl/contrib_raw_string.cpp +@@ -2,7 +2,6 @@ + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + + #include "test.hpp" +-#include "verify_fail.hpp" + + #include + +@@ -46,12 +45,12 @@ namespace tao + }; + + struct rgrammar +- : must< rstring, eof > ++ : seq< rstring, eof > + { + }; + + struct qgrammar +- : must< qstring, eof > ++ : seq< qstring, eof > + { + }; + +@@ -72,6 +71,16 @@ namespace tao + } + } + ++ template< typename Rule > ++ void verify_fail( const std::size_t line, const char* file, const std::string& s ) ++ { ++ memory_input<> in( s, "expect exception" ); ++ if( parse< Rule >( in ) ) { ++ TAO_PEGTL_TEST_FAILED( "expected exception" ); // LCOV_EXCL_LINE ++ } ++ } ++ ++ + void unit_test() + { + verify_data< rgrammar, raction >( __LINE__, __FILE__, "[[]]", "" ); +diff --git a/src/test/pegtl/contrib_unescape.cpp b/src/test/pegtl/contrib_unescape.cpp +index e1cdf15c..876460a4 100644 +--- a/src/test/pegtl/contrib_unescape.cpp ++++ b/src/test/pegtl/contrib_unescape.cpp +@@ -2,7 +2,6 @@ + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + + #include "test.hpp" +-#include "verify_fail.hpp" + + #include + +@@ -11,107 +10,122 @@ namespace tao + namespace TAO_PEGTL_NAMESPACE + { + // clang-format off +- struct escaped_c : one< '"', '\\', 't' > {}; +- struct escaped_u : seq< one< 'u' >, rep< 4, must< xdigit > > > {}; +- struct escaped_U : seq< one< 'U' >, rep< 8, must< xdigit > > > {}; +- struct escaped_j : list< seq< one< 'j' >, rep< 4, must< xdigit > > >, one< '\\' > > {}; +- struct escaped_x : seq< one< 'x' >, rep< 2, must< xdigit > > > {}; +- struct escaped : sor< escaped_c, escaped_u, escaped_U, escaped_j, escaped_x > {}; +- struct character : if_then_else< one< '\\' >, must< escaped >, utf8::any > {}; +- struct unstring : until< eof, character > {}; +- +- template< typename Rule > struct unaction {}; +- +- template<> struct unaction< escaped_c > : unescape::unescape_c< escaped_c, '"', '\\', '\t' > {}; +- template<> struct unaction< escaped_u > : unescape::unescape_u {}; +- template<> struct unaction< escaped_U > : unescape::unescape_u {}; +- template<> struct unaction< escaped_j > : unescape::unescape_j {}; +- template<> struct unaction< escaped_x > : unescape::unescape_x {}; +- template<> struct unaction< utf8::any > : unescape::append_all {}; ++ struct escaped_c : one< '"', '\\', 't' > {}; ++ struct escaped_u : seq< one< 'u' >, rep< 4, xdigit > > {}; ++ struct escaped_U : seq< one< 'U' >, rep< 8, xdigit > > {}; ++ struct escaped_j : list< seq< one< 'j' >, rep< 4, xdigit > >, one< '\\' > > {}; ++ struct escaped_x : seq< one< 'x' >, rep< 2, xdigit > > {}; ++ struct escaped : sor< escaped_c, escaped_u, escaped_U, escaped_j, escaped_x > {}; ++ struct character : if_then_else< one< '\\' >, escaped, utf8::any > {}; ++ struct unstring : until< eof, character > {}; ++ ++ template< typename Rule > struct unaction {}; ++ ++ template<> struct unaction< escaped_c > : unescape::unescape_c< escaped_c, '"', '\\', '\t' > {}; ++ template<> struct unaction< escaped_u > : unescape::unescape_u {}; ++ template<> struct unaction< escaped_U > : unescape::unescape_u {}; ++ template<> struct unaction< escaped_j > : unescape::unescape_j {}; ++ template<> struct unaction< escaped_x > : unescape::unescape_x {}; ++ template<> struct unaction< utf8::any > : unescape::append_all {}; + // clang-format on + + template< unsigned M, unsigned N > +- void verify_data( const char ( &m )[ M ], const char ( &n )[ N ] ) // NOLINT ++ bool verify_data( const char ( &m )[ M ], const char ( &n )[ N ] ) // NOLINT + { + std::string s; + memory_input<> in( m, M - 1, __FUNCTION__ ); +- parse< unstring, unaction >( in, s ); +- if( s != std::string( n, N - 1 ) ) { +- throw std::runtime_error( "test failed!" ); // NOLINT ++ if( !parse< unstring, unaction >( in, s ) ) { ++ return false; + } ++ return s == std::string( n, N - 1 ); + } + +- void unit_test() ++ bool verify_fail( const std::string& m ) + { +- verify_data( "\\t", "\t" ); +- verify_data( "\\\\", "\\" ); +- verify_data( "abc", "abc" ); +- verify_data( "\\\"foo\\\"", "\"foo\"" ); +- verify_data( "\\x20", " " ); +- verify_data( "\\x30", "0" ); +- verify_data( "\\x2000", " 00" ); +- verify_data( "\\u0020", " " ); +- verify_data( "\\u0020\\u0020", " " ); +- verify_data( "\\u00e4", "\xc3\xa4" ); +- verify_data( "\\u00E4", "\xC3\xA4" ); +- verify_data( "\\u20ac", "\xe2\x82\xac" ); +- +- TAO_PEGTL_TEST_THROWS( verify_data( "\\ud800", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\ud800X", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\ud800\\u0020", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\ud800\\udc00", "" ) ); // unescape_u does not support surrogate pairs. +- TAO_PEGTL_TEST_THROWS( verify_data( "\\udc00\\ud800", "" ) ); +- +- verify_data( "\\j0020", " " ); +- verify_data( "\\j0020\\j0020", " " ); +- verify_data( "\\j20ac", "\xe2\x82\xac" ); +- +- verify_data( "\\jd800\\jdc00", "\xf0\x90\x80\x80" ); // unescape_j does support proper surrogate pairs. +- +- TAO_PEGTL_TEST_THROWS( verify_data( "\\jd800", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\jd800X", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\jd800\\j0020", "" ) ); +- TAO_PEGTL_TEST_THROWS( verify_data( "\\jdc00\\jd800", "" ) ); +- +- verify_data( "\\j0000\\u0000\x00", "\x00\x00\x00" ); +- + std::string s; +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\\\\\", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\x", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\xx", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\xa", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\x1", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\x1h", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\x", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\xx", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\xa", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\x1", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "a\\x1h", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\a", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\_", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\z", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\1", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\a00", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\_1111", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\z22222222", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\13333333333333333", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\u", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\uu", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\uuuu", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\u123", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\u999", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\u444h", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\j", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\ju", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\juuu", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\j123", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\j999", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\j444h", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\U00110000", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\U80000000", s ); +- verify_fail< unstring, unaction >( __LINE__, __FILE__, "\\Uffffffff", s ); ++ memory_input<> in( m, __FUNCTION__ ); ++#if defined( __cpp_exceptions ) ++ try { ++ return !parse< unstring, unaction >( in, s ); ++ } ++ catch( const parse_error& ) { ++ } ++ return true; ++#else ++ return !parse< unstring, unaction >( in, s ); ++#endif ++ } ++ ++ void unit_test() ++ { ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\t", "\t" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\\\", "\\" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "abc", "abc" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\\"foo\\\"", "\"foo\"" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\x20", " " ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\x30", "0" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\x2000", " 00" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\u0020", " " ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\u0020\\u0020", " " ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\u00e4", "\xc3\xa4" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\u00E4", "\xC3\xA4" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\u20ac", "\xe2\x82\xac" ) ); ++ ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\ud800" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\ud800X" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\ud800\\u0020" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\ud800\\udc00" ) ); // unescape_u does not support surrogate pairs. ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\udc00\\ud800" ) ); ++ ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\j0020", " " ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\j0020\\j0020", " " ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\j20ac", "\xe2\x82\xac" ) ); ++ ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\jd800\\jdc00", "\xf0\x90\x80\x80" ) ); // unescape_j does support proper surrogate pairs. ++ ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\jd800" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\jd800X" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\jd800\\j0020" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\jdc00\\jd800" ) ); ++ ++ TAO_PEGTL_TEST_ASSERT( verify_data( "\\j0000\\u0000\x00", "\x00\x00\x00" ) ); ++ ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\\\\\" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\x" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\xx" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\xa" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\x1" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\x1h" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\x" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\xx" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\xa" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\x1" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "a\\x1h" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\a" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\_" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\z" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\1" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\a00" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\_1111" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\z22222222" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\13333333333333333" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\u" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\uu" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\uuuu" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\u123" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\u999" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\u444h" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\j" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\ju" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\juuu" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\j123" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\j999" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\j444h" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\U00110000" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\U80000000" ) ); ++ TAO_PEGTL_TEST_ASSERT( verify_fail( "\\Uffffffff" ) ); + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/contrib_uri.cpp b/src/test/pegtl/contrib_uri.cpp +index 14bda5cc..6d3e48ab 100644 +--- a/src/test/pegtl/contrib_uri.cpp ++++ b/src/test/pegtl/contrib_uri.cpp +@@ -1,9 +1,12 @@ +-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey ++// Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" +-#include "verify_fail.hpp" + #include "verify_rule.hpp" + + #include +@@ -36,7 +39,7 @@ namespace tao + verify_rule< GRAMMAR >( __LINE__, __FILE__, "crid://broadcaster.com/movies/BestActionMovieEver", result_type::success, 0 ); + verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://nobody:password@example.org:8080/cgi-bin/script.php?action=submit&pageid=86392001#section_2", result_type::success, 0 ); + +- verify_fail< GRAMMAR >( __LINE__, __FILE__, "" ); ++ TAO_PEGTL_TEST_THROWS( parse< GRAMMAR >( memory_input<>( "", "" ) ) ); + } + + } // namespace TAO_PEGTL_NAMESPACE +@@ -44,3 +47,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/discard_input.cpp b/src/test/pegtl/discard_input.cpp +index 6d5a7f2a..d9d7ec0e 100644 +--- a/src/test/pegtl/discard_input.cpp ++++ b/src/test/pegtl/discard_input.cpp +@@ -32,20 +32,28 @@ namespace tao + + void unit_test() + { ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< 4, sor< n, n > >, my_action >( "nnnn", TAO_TEST_LINE, 2 ) ); ++#endif + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< a, n > >, my_action >( "nnnn", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< f, n > >, my_action >( "nnnn", TAO_TEST_LINE, 2 ) ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< 4, sor< s, n > >, my_action >( "nnnn", TAO_TEST_LINE, 2 ) ); ++#endif + + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< n, a > >, my_action >( "aaaa", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< a, a > >, my_action >( "aaaa", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< f, a > >, my_action >( "aaaa", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< s, a > >, my_action >( "aaaa", TAO_TEST_LINE, 2 ) ); + ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< 4, sor< n, f > >, my_action >( "ffff", TAO_TEST_LINE, 2 ) ); ++#endif + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< a, f > >, my_action >( "ffff", TAO_TEST_LINE, 2 ) ); ++#if defined( __cpp_exceptions ) + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< 4, sor< f, f > >, my_action >( "ffff", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_THROWS( parse_cstring< rep< 4, sor< s, f > >, my_action >( "ffff", TAO_TEST_LINE, 2 ) ); ++#endif + + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< n, s > >, my_action >( "ssss", TAO_TEST_LINE, 2 ) ); + TAO_PEGTL_TEST_ASSERT( parse_cstring< rep< 4, sor< a, s > >, my_action >( "ssss", TAO_TEST_LINE, 2 ) ); +diff --git a/src/test/pegtl/file_istream.cpp b/src/test/pegtl/file_istream.cpp +index cf6a618a..0cc92834 100644 +--- a/src/test/pegtl/file_istream.cpp ++++ b/src/test/pegtl/file_istream.cpp +@@ -19,6 +19,7 @@ namespace tao + + void unit_test() + { ++#if defined( __cpp_exceptions ) + try { + const char* filename = "src/test/pegtl/no_such_file.txt"; + std::ifstream stream( filename ); +@@ -28,6 +29,7 @@ namespace tao + catch( const input_error& e ) { + TAO_PEGTL_TEST_ASSERT( std::string( e.what() ).find( "error in istream.read()" ) != std::string::npos ); + } ++#endif + const char* filename = "src/test/pegtl/file_data.txt"; + std::ifstream stream( filename ); + TAO_PEGTL_TEST_ASSERT( parse< file_grammar >( istream_input<>( stream, 16, filename ) ) ); +diff --git a/src/test/pegtl/internal_file_mapper.cpp b/src/test/pegtl/internal_file_mapper.cpp +index bc5e16d0..fa04d17f 100644 +--- a/src/test/pegtl/internal_file_mapper.cpp ++++ b/src/test/pegtl/internal_file_mapper.cpp +@@ -1,9 +1,11 @@ + // Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + +-#include ++#if !defined( __cpp_exceptions ) || !defined( _POSIX_MAPPED_FILES ) ++int main() {} ++#else + +-#if defined( _POSIX_MAPPED_FILES ) ++#include + + #include "test.hpp" + +@@ -32,11 +34,4 @@ namespace tao + + #include "main.hpp" + +-#else +- +-int main( int, char** ) +-{ +- return 0; +-} +- + #endif +diff --git a/src/test/pegtl/internal_file_opener.cpp b/src/test/pegtl/internal_file_opener.cpp +index 64d0bbde..0d3e6a54 100644 +--- a/src/test/pegtl/internal_file_opener.cpp ++++ b/src/test/pegtl/internal_file_opener.cpp +@@ -1,9 +1,11 @@ + // Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + +-#include ++#if !defined( __cpp_exceptions ) || !defined( _POSIX_MAPPED_FILES ) ++int main() {} ++#else + +-#if defined( _POSIX_MAPPED_FILES ) ++#include + + #include "test.hpp" + +@@ -13,15 +15,17 @@ namespace tao + { + void unit_test() + { ++#if defined( __cpp_exceptions ) + const internal::file_opener fo( "Makefile" ); + ::close( fo.m_fd ); // Provoke exception, nobody would normally do this. + try { +- fo.size(); ++ (void)fo.size(); + std::cerr << "pegtl: unit test failed for [ internal::file_opener ] " << std::endl; + ++failed; + } + catch( const std::exception& ) { + } ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE +@@ -30,11 +34,4 @@ namespace tao + + #include "main.hpp" + +-#else +- +-int main( int, char** ) +-{ +- return 0; +-} +- + #endif +diff --git a/src/test/pegtl/position.cpp b/src/test/pegtl/position.cpp +index 30a06649..8bc6b92a 100644 +--- a/src/test/pegtl/position.cpp ++++ b/src/test/pegtl/position.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2016-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + + #include +@@ -179,3 +183,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_apply.cpp b/src/test/pegtl/rule_apply.cpp +index 502964a7..4a158a62 100644 +--- a/src/test/pegtl/rule_apply.cpp ++++ b/src/test/pegtl/rule_apply.cpp +@@ -69,16 +69,15 @@ namespace tao + { + int state_r = 0; + int state_s = 0; +- parse< must< apply< test1::action_a, test1::action_b > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< apply< test1::action_a, test1::action_b > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( state_r == 1 ); + TAO_PEGTL_TEST_ASSERT( state_s == 2 ); +- parse< must< disable< apply< test1::action_a, test1::action_b > > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< disable< apply< test1::action_a, test1::action_b > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( state_r == 1 ); + TAO_PEGTL_TEST_ASSERT( state_s == 2 ); + + bool state_b = false; +- const bool result = parse< apply< test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ); +- TAO_PEGTL_TEST_ASSERT( !result ); ++ TAO_PEGTL_TEST_ASSERT( !parse< apply< test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ) ); + TAO_PEGTL_TEST_ASSERT( state_b ); + + verify_analyze< apply<> >( __LINE__, __FILE__, false, false ); +diff --git a/src/test/pegtl/rule_apply0.cpp b/src/test/pegtl/rule_apply0.cpp +index 2cef49c5..8c8f14e6 100644 +--- a/src/test/pegtl/rule_apply0.cpp ++++ b/src/test/pegtl/rule_apply0.cpp +@@ -64,16 +64,15 @@ namespace tao + { + int state_r = 0; + int state_s = 0; +- parse< must< apply0< test1::action_a, test1::action_b > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< apply0< test1::action_a, test1::action_b > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( state_r == 1 ); + TAO_PEGTL_TEST_ASSERT( state_s == 2 ); +- parse< must< disable< apply< test1::action_a, test1::action_b > > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< disable< apply< test1::action_a, test1::action_b > > >( memory_input<>( "", __FUNCTION__ ), state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( state_r == 1 ); + TAO_PEGTL_TEST_ASSERT( state_s == 2 ); + + bool state_b = false; +- const bool result = parse< apply0< test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ); +- TAO_PEGTL_TEST_ASSERT( !result ); ++ TAO_PEGTL_TEST_ASSERT( !parse< apply0< test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ) ); + TAO_PEGTL_TEST_ASSERT( state_b ); + + verify_analyze< apply0<> >( __LINE__, __FILE__, false, false ); +diff --git a/src/test/pegtl/rule_at.cpp b/src/test/pegtl/rule_at.cpp +index 8a332a8e..54c073c3 100644 +--- a/src/test/pegtl/rule_at.cpp ++++ b/src/test/pegtl/rule_at.cpp +@@ -39,8 +39,11 @@ namespace tao + verify_rule< at< any > >( __LINE__, __FILE__, "a", result_type::success, 1 ); + verify_rule< at< any > >( __LINE__, __FILE__, "aa", result_type::success, 2 ); + verify_rule< at< any > >( __LINE__, __FILE__, "aaaa", result_type::success, 4 ); ++ ++#if defined( __cpp_exceptions ) + verify_rule< must< at< alpha > > >( __LINE__, __FILE__, "1", result_type::global_failure, 1 ); + verify_rule< must< at< alpha, alpha > > >( __LINE__, __FILE__, "a1a", result_type::global_failure, 3 ); ++#endif + { + memory_input<> in( "f", 1, __FILE__ ); + parse< any, at_action >( in ); +diff --git a/src/test/pegtl/rule_if_apply.cpp b/src/test/pegtl/rule_if_apply.cpp +index 9596949c..71944ee8 100644 +--- a/src/test/pegtl/rule_if_apply.cpp ++++ b/src/test/pegtl/rule_if_apply.cpp +@@ -94,27 +94,25 @@ namespace tao + std::string state_s; + TAO_PEGTL_TEST_ASSERT( test1::flag == 0 ); + memory_input<> in1( "-", __FUNCTION__ ); +- parse< must< if_apply< one< '-' >, test1::action_a, test1::action_b > >, test1::action >( in1, state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< if_apply< one< '-' >, test1::action_a, test1::action_b >, test1::action >( in1, state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( test1::flag == 1 ); + TAO_PEGTL_TEST_ASSERT( state_r == "-" ); + TAO_PEGTL_TEST_ASSERT( state_s == "-*-" ); + memory_input<> in2( "-", __FUNCTION__ ); +- parse< must< disable< if_apply< one< '-' >, test1::action_a, test1::action_b > > >, test1::action >( in2, state_r, state_s ); ++ TAO_PEGTL_TEST_ASSERT( parse< disable< if_apply< one< '-' >, test1::action_a, test1::action_b > >, test1::action >( in2, state_r, state_s ) ); + TAO_PEGTL_TEST_ASSERT( test1::flag == 1 ); + TAO_PEGTL_TEST_ASSERT( state_r == "-" ); + TAO_PEGTL_TEST_ASSERT( state_s == "-*-" ); + + { + bool state_b = false; +- const bool result = parse< if_apply< plus< alpha >, test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "foo bar", __FUNCTION__ ), state_b ); +- TAO_PEGTL_TEST_ASSERT( !result ); ++ TAO_PEGTL_TEST_ASSERT( !parse< if_apply< plus< alpha >, test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "foo bar", __FUNCTION__ ), state_b ) ); + TAO_PEGTL_TEST_ASSERT( state_b ); + } + + { + bool state_b = false; +- const bool result = parse< if_apply< plus< alpha >, test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ); +- TAO_PEGTL_TEST_ASSERT( !result ); ++ TAO_PEGTL_TEST_ASSERT( !parse< if_apply< plus< alpha >, test1::action2_a, test1::action2_b, test1::action2_c > >( memory_input<>( "", __FUNCTION__ ), state_b ) ); + TAO_PEGTL_TEST_ASSERT( !state_b ); + } + +diff --git a/src/test/pegtl/rule_if_must.cpp b/src/test/pegtl/rule_if_must.cpp +index 19ba5a41..d8418886 100644 +--- a/src/test/pegtl/rule_if_must.cpp ++++ b/src/test/pegtl/rule_if_must.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" + #include "verify_rule.hpp" +@@ -46,3 +50,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_if_must_else.cpp b/src/test/pegtl/rule_if_must_else.cpp +index 9f851e3d..ce2e6e8c 100644 +--- a/src/test/pegtl/rule_if_must_else.cpp ++++ b/src/test/pegtl/rule_if_must_else.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_ifmt.hpp" + +@@ -18,3 +22,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_list_must.cpp b/src/test/pegtl/rule_list_must.cpp +index 89856dce..14370b85 100644 +--- a/src/test/pegtl/rule_list_must.cpp ++++ b/src/test/pegtl/rule_list_must.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" + #include "verify_rule.hpp" +@@ -58,3 +62,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_list_tail.cpp b/src/test/pegtl/rule_list_tail.cpp +index d53cc61a..761b338e 100644 +--- a/src/test/pegtl/rule_list_tail.cpp ++++ b/src/test/pegtl/rule_list_tail.cpp +@@ -43,9 +43,9 @@ namespace tao + verify_rule< list_tail< one< 'a' >, one< ',' > > >( __LINE__, __FILE__, "a ,a", result_type::success, 3 ); + verify_rule< list_tail< one< 'a' >, one< ',' > > >( __LINE__, __FILE__, "a, a", result_type::success, 2 ); + +- verify_rule< list_must< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, "", result_type::local_failure, 0 ); +- verify_rule< list_must< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, " ", result_type::local_failure, 1 ); +- verify_rule< list_must< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, ",", result_type::local_failure, 1 ); ++ verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, "", result_type::local_failure, 0 ); ++ verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, " ", result_type::local_failure, 1 ); ++ verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, ",", result_type::local_failure, 1 ); + verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, "a ", result_type::success, 1 ); + verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, " a", result_type::local_failure, 2 ); + verify_rule< list_tail< one< 'a' >, one< ',' >, blank > >( __LINE__, __FILE__, "a ,a", result_type::success, 0 ); +diff --git a/src/test/pegtl/rule_minus.cpp b/src/test/pegtl/rule_minus.cpp +index cea081b6..30cf7c52 100644 +--- a/src/test/pegtl/rule_minus.cpp ++++ b/src/test/pegtl/rule_minus.cpp +@@ -20,8 +20,10 @@ namespace tao + verify_rule< minus< alnum, digit > >( __LINE__, __FILE__, "%", result_type::local_failure, 1 ); + verify_rule< minus< alnum, digit > >( __LINE__, __FILE__, "a%", result_type::success, 1 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< minus< alnum, digit > > >( __LINE__, __FILE__, "%", result_type::global_failure, 1 ); + verify_rule< must< minus< alnum, digit > > >( __LINE__, __FILE__, "1", result_type::global_failure, 0 ); ++#endif + + verify_rule< minus< plus< alnum >, digit > >( __LINE__, __FILE__, "", result_type::local_failure, 0 ); + verify_rule< minus< plus< alnum >, digit > >( __LINE__, __FILE__, "a", result_type::success, 0 ); +diff --git a/src/test/pegtl/rule_must.cpp b/src/test/pegtl/rule_must.cpp +index b8808f36..6d513bdf 100644 +--- a/src/test/pegtl/rule_must.cpp ++++ b/src/test/pegtl/rule_must.cpp +@@ -1,6 +1,10 @@ +-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey ++// Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_seqs.hpp" + +@@ -18,3 +22,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_not_at.cpp b/src/test/pegtl/rule_not_at.cpp +index acbb4011..86e5b444 100644 +--- a/src/test/pegtl/rule_not_at.cpp ++++ b/src/test/pegtl/rule_not_at.cpp +@@ -39,8 +39,12 @@ namespace tao + verify_rule< not_at< any > >( __LINE__, __FILE__, "a", result_type::local_failure, 1 ); + verify_rule< not_at< any > >( __LINE__, __FILE__, "aa", result_type::local_failure, 2 ); + verify_rule< not_at< any > >( __LINE__, __FILE__, "aaaa", result_type::local_failure, 4 ); ++ ++#if defined( __cpp_exceptions ) + verify_rule< must< not_at< alpha > > >( __LINE__, __FILE__, "a", result_type::global_failure, 1 ); + verify_rule< must< not_at< alpha, alpha > > >( __LINE__, __FILE__, "aa1", result_type::global_failure, 3 ); ++#endif ++ + { + memory_input<> in( "a", 1, __FILE__ ); + parse< alpha, at_action >( in ); +diff --git a/src/test/pegtl/rule_opt.cpp b/src/test/pegtl/rule_opt.cpp +index 09f74a22..c3ad7580 100644 +--- a/src/test/pegtl/rule_opt.cpp ++++ b/src/test/pegtl/rule_opt.cpp +@@ -42,6 +42,7 @@ namespace tao + verify_rule< opt< one< 'a' >, one< 'b' > > >( __LINE__, __FILE__, "bab", result_type::success, 3 ); + verify_rule< opt< one< 'a' >, one< 'b' > > >( __LINE__, __FILE__, "cb", result_type::success, 2 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< opt< one< 'a' > > > >( __LINE__, __FILE__, "", result_type::success, 0 ); + verify_rule< must< opt< one< 'a' > > > >( __LINE__, __FILE__, "a", result_type::success, 0 ); + verify_rule< must< opt< one< 'a' > > > >( __LINE__, __FILE__, "aa", result_type::success, 1 ); +@@ -55,10 +56,10 @@ namespace tao + verify_rule< must< opt< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "abab", result_type::success, 2 ); + verify_rule< must< opt< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "bab", result_type::success, 3 ); + verify_rule< must< opt< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "cb", result_type::success, 2 ); ++#endif + + bool success = false; +- const bool result = parse< opt< eof >, my_action >( memory_input<>( "", __FUNCTION__ ), success ); +- TAO_PEGTL_TEST_ASSERT( result ); ++ TAO_PEGTL_TEST_ASSERT( parse< opt< eof >, my_action >( memory_input<>( "", __FUNCTION__ ), success ) ); + TAO_PEGTL_TEST_ASSERT( success ); + } + +diff --git a/src/test/pegtl/rule_opt_must.cpp b/src/test/pegtl/rule_opt_must.cpp +index 232714f7..40d28eda 100644 +--- a/src/test/pegtl/rule_opt_must.cpp ++++ b/src/test/pegtl/rule_opt_must.cpp +@@ -1,6 +1,10 @@ +-// Copyright (c) 2018-2020 Dr. Colin Hirsch and Daniel Frey ++// Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" + #include "verify_rule.hpp" +@@ -46,3 +50,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_rep.cpp b/src/test/pegtl/rule_rep.cpp +index e76a278f..8ae15b5c 100644 +--- a/src/test/pegtl/rule_rep.cpp ++++ b/src/test/pegtl/rule_rep.cpp +@@ -54,6 +54,7 @@ namespace tao + verify_rule< rep< 2, one< 'a' >, one< 'b' > > >( __LINE__, __FILE__, "ababa", result_type::success, 1 ); + verify_rule< rep< 2, one< 'a' >, one< 'b' > > >( __LINE__, __FILE__, "ababab", result_type::success, 2 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< rep< 2, one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "", result_type::global_failure, 0 ); + verify_rule< must< rep< 2, one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "a", result_type::global_failure, 1 ); + verify_rule< must< rep< 2, one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "ab", result_type::global_failure, 0 ); +@@ -69,6 +70,7 @@ namespace tao + verify_rule< try_catch< must< rep< 2, one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "abab", result_type::success, 0 ); + verify_rule< try_catch< must< rep< 2, one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "ababa", result_type::success, 1 ); + verify_rule< try_catch< must< rep< 2, one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "ababab", result_type::success, 2 ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/rule_rep_min_max.cpp b/src/test/pegtl/rule_rep_min_max.cpp +index f468f1cf..90f3dd57 100644 +--- a/src/test/pegtl/rule_rep_min_max.cpp ++++ b/src/test/pegtl/rule_rep_min_max.cpp +@@ -41,9 +41,11 @@ namespace tao + verify_rule< rep_min_max< 2, 4, one< 'a' > > >( __LINE__, __FILE__, "baaa", result_type::local_failure, 4 ); + verify_rule< rep_min_max< 2, 4, one< 'a' > > >( __LINE__, __FILE__, "baaaa", result_type::local_failure, 5 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< rep_min_max< 3, 4, one< 'a' > > > >( __LINE__, __FILE__, "aa", result_type::global_failure, 0 ); + + verify_rule< try_catch< must< rep_min_max< 3, 4, one< 'a' > > > > >( __LINE__, __FILE__, "aa", result_type::local_failure, 2 ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/rule_star_must.cpp b/src/test/pegtl/rule_star_must.cpp +index bd1ff064..95c18e7a 100644 +--- a/src/test/pegtl/rule_star_must.cpp ++++ b/src/test/pegtl/rule_star_must.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_analyze.hpp" + #include "verify_rule.hpp" +@@ -38,3 +42,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_try_catch.cpp b/src/test/pegtl/rule_try_catch.cpp +index 182deb9a..8d23ca26 100644 +--- a/src/test/pegtl/rule_try_catch.cpp ++++ b/src/test/pegtl/rule_try_catch.cpp +@@ -1,6 +1,10 @@ + // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey + // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/ + ++#if !defined( __cpp_exceptions ) ++int main() {} ++#else ++ + #include "test.hpp" + #include "verify_seqs.hpp" + +@@ -22,3 +26,5 @@ namespace tao + } // namespace tao + + #include "main.hpp" ++ ++#endif +diff --git a/src/test/pegtl/rule_until.cpp b/src/test/pegtl/rule_until.cpp +index 378d3a5b..23009a30 100644 +--- a/src/test/pegtl/rule_until.cpp ++++ b/src/test/pegtl/rule_until.cpp +@@ -57,9 +57,11 @@ namespace tao + verify_rule< until< one< 'a' > > >( __LINE__, __FILE__, "bbab", result_type::success, 1 ); + verify_rule< until< one< 'a' > > >( __LINE__, __FILE__, "bbbbbbbbbbbbbbbab", result_type::success, 1 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< until< one< 'a' > > > >( __LINE__, __FILE__, "bbb", result_type::global_failure, 0 ); + + verify_rule< try_catch< must< until< one< 'a' > > > > >( __LINE__, __FILE__, "bbb", result_type::local_failure, 3 ); ++#endif + + verify_rule< until< eof, any > >( __LINE__, __FILE__, "", result_type::success, 0 ); + verify_rule< until< any, any > >( __LINE__, __FILE__, "", result_type::local_failure, 0 ); +@@ -104,11 +106,13 @@ namespace tao + verify_rule< until< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "cbcbc", result_type::local_failure, 5 ); + verify_rule< until< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "bcbcbc", result_type::local_failure, 6 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< until< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "bbb", result_type::global_failure, 0 ); + verify_rule< must< until< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "bbbc", result_type::global_failure, 1 ); + + verify_rule< try_catch< must< until< one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "bbb", result_type::local_failure, 3 ); + verify_rule< try_catch< must< until< one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "bbbc", result_type::local_failure, 4 ); ++#endif + + bool success = false; + const bool result = parse< until< my_rule, eof >, my_action >( memory_input<>( "", __FUNCTION__ ), success ); +diff --git a/src/test/pegtl/test.hpp b/src/test/pegtl/test.hpp +index b4a2704f..b8eaef0f 100644 +--- a/src/test/pegtl/test.hpp ++++ b/src/test/pegtl/test.hpp +@@ -5,6 +5,7 @@ + #define TAO_PEGTL_SRC_TEST_PEGTL_TEST_HPP + + #include ++#include + #include + #include + #include +@@ -75,6 +76,12 @@ namespace tao + } \ + } while( false ) + ++#define TAO_PEGTL_TEST_UNREACHABLE \ ++ do { \ ++ std::cerr << "Code should be unreachable in " << __FUNCTION__ << " (" << __FILE__ << ':' << __LINE__ << ')' << std::endl; \ ++ std::terminate(); \ ++ } while( false ) ++ + namespace tao + { + namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/tester.cpp b/src/test/pegtl/tester.cpp +index b57abd0a..88c13be0 100644 +--- a/src/test/pegtl/tester.cpp ++++ b/src/test/pegtl/tester.cpp +@@ -36,26 +36,5 @@ int main() + std::cout << "_MSC_VER: " << _MSC_VER << std::endl; + std::cout << "_MSC_FULL_VER: " << _MSC_FULL_VER << std::endl; + #endif +- +-#ifdef __cplusplus +- std::cout << "__cplusplus: " << __cplusplus << std::endl; +-#endif +-#ifdef __cpp_constexpr +- std::cout << "__cpp_constexpr: " << __cpp_constexpr << std::endl; +-#endif +-#ifdef __cpp_variadic_templates +- std::cout << "__cpp_variadic_templates: " << __cpp_variadic_templates << std::endl; +-#endif +-#ifdef __cpp_variable_templates +- std::cout << "__cpp_variable_templates: " << __cpp_variable_templates << std::endl; +-#endif +-#ifdef __cpp_fold_expressions +- std::cout << "__cpp_fold_expressions: " << __cpp_fold_expressions << std::endl; +-#endif +-#ifdef __cpp_deduction_guides +- std::cout << "__cpp_deduction_guides: " << __cpp_deduction_guides << std::endl; +-#endif +-#ifdef __cpp_lib_integer_sequence +- std::cout << "__cpp_lib_integer_sequence: " << __cpp_lib_integer_sequence << std::endl; +-#endif ++ return 0; + } +diff --git a/src/test/pegtl/verify_file.hpp b/src/test/pegtl/verify_file.hpp +index fc4b70c8..e8c09fc6 100644 +--- a/src/test/pegtl/verify_file.hpp ++++ b/src/test/pegtl/verify_file.hpp +@@ -54,6 +54,7 @@ namespace tao + template< typename T > + void verify_file() + { ++#if defined( __cpp_exceptions ) + { + const std::string f{ "src/test/pegtl/no_such_file.txt" }; + try { +@@ -64,6 +65,7 @@ namespace tao + catch( const input_error& ) { + } + } ++#endif + { + const std::string f{ "src/test/pegtl/file_data.txt" }; + T in( f ); +diff --git a/src/test/pegtl/verify_ifmt.hpp b/src/test/pegtl/verify_ifmt.hpp +index 59cffbad..36129c27 100644 +--- a/src/test/pegtl/verify_ifmt.hpp ++++ b/src/test/pegtl/verify_ifmt.hpp +@@ -31,6 +31,7 @@ namespace tao + verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ab", result_type::success, 0 ); + verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "ac", failure, 2 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "", result_type::global_failure, 0 ); + verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "a", result_type::global_failure, 0 ); + verify_rule< must< S< one< 'a' >, one< 'b' >, one< 'c' > > > >( __LINE__, __FILE__, "ac", result_type::global_failure, 1 ); +@@ -38,6 +39,7 @@ namespace tao + + verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "c", result_type::global_failure, 0 ); + verify_rule< must< S< one< 'a' >, one< 'b' >, seq< one< 'c' >, one< 'd' > > > > >( __LINE__, __FILE__, "cc", result_type::global_failure, 1 ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE +diff --git a/src/test/pegtl/verify_impl.hpp b/src/test/pegtl/verify_impl.hpp +index 8014c13b..abe8f849 100644 +--- a/src/test/pegtl/verify_impl.hpp ++++ b/src/test/pegtl/verify_impl.hpp +@@ -23,6 +23,7 @@ namespace tao + template< typename Rule, template< typename... > class Action, typename Input > + result_type verify_impl_two( Input& in ) + { ++#if defined( __cpp_exceptions ) + try { + if( normal< Rule >::template match< apply_mode::action, rewind_mode::required, Action, normal >( in ) ) { + return result_type::success; +@@ -36,6 +37,14 @@ namespace tao + std::cerr << "Code should be unreachable in " << __FUNCTION__ << " (" << __FILE__ << ':' << __LINE__ << ')' << std::endl; + std::abort(); + } ++#else ++ ++ if( normal< Rule >::template match< apply_mode::action, rewind_mode::required, Action, normal >( in ) ) { ++ return result_type::success; ++ } ++ return result_type::local_failure; ++ ++#endif + } + + template< typename Rule, template< typename... > class Action, typename Input > +diff --git a/src/test/pegtl/verify_seqs.hpp b/src/test/pegtl/verify_seqs.hpp +index ede538de..77fbbb86 100644 +--- a/src/test/pegtl/verify_seqs.hpp ++++ b/src/test/pegtl/verify_seqs.hpp +@@ -49,6 +49,7 @@ namespace tao + verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' >, eof > >( __LINE__, __FILE__, "abc", result_type::success, 0 ); + verify_rule< S< one< 'a' >, one< 'b' >, one< 'c' > > >( __LINE__, __FILE__, "abcd", result_type::success, 1 ); + ++#if defined( __cpp_exceptions ) + verify_rule< must< S< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "", result_type::global_failure, 0 ); + verify_rule< must< S< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "a", result_type::global_failure, 0 ); + verify_rule< must< S< one< 'a' >, one< 'b' > > > >( __LINE__, __FILE__, "b", result_type::global_failure, 1 ); +@@ -62,6 +63,7 @@ namespace tao + verify_rule< try_catch< must< S< one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "c", result_type::local_failure, 1 ); + verify_rule< try_catch< must< S< one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "ab", result_type::success, 0 ); + verify_rule< try_catch< must< S< one< 'a' >, one< 'b' > > > > >( __LINE__, __FILE__, "aba", result_type::success, 1 ); ++#endif + } + + } // namespace TAO_PEGTL_NAMESPACE diff --git a/patches/rapidjson.patch b/patches/rapidjson.patch index 3c89e91..7f54918 100644 --- a/patches/rapidjson.patch +++ b/patches/rapidjson.patch @@ -1,70 +1,56 @@ -From b3b191f9eb3cc59acb548d03506bc7fd5749874c Mon Sep 17 00:00:00 2001 -From: ylavic -Date: Sat, 1 Dec 2018 23:36:45 +0100 -Subject: [PATCH 1/3] Fix a memory leak for invalid std::regex in Schema. - ---- - include/rapidjson/schema.h | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h -index b182aa27..f9695dad 100644 ---- a/include/rapidjson/schema.h -+++ b/include/rapidjson/schema.h -@@ -1017,10 +1017,12 @@ private: - template - RegexType* CreatePattern(const ValueType& value) { - if (value.IsString()) -+ RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); - try { -- return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); -+ return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); - } - catch (const std::regex_error&) { -+ AllocatorType::Free(r); - } - return 0; +diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h +index e3e20dfb..ab2cbbd1 100644 +--- a/include/rapidjson/document.h ++++ b/include/rapidjson/document.h +@@ -1425,7 +1425,7 @@ public: + MemberIterator pos = MemberBegin() + (first - MemberBegin()); + for (MemberIterator itr = pos; itr != last; ++itr) + itr->~Member(); +- std::memmove(&*pos, &*last, static_cast(MemberEnd() - last) * sizeof(Member)); ++ std::memmove(static_cast(&*pos), &*last, static_cast(MemberEnd() - last) * sizeof(Member)); + data_.o.size -= static_cast(last - first); + return pos; } --- -2.21.0 (Apple Git-122) - - -From 07b286eb2c65a7117b9ace68bcfaaf67ad97cfc2 Mon Sep 17 00:00:00 2001 -From: Veselin Georgiev -Date: Fri, 27 Jul 2018 13:33:09 -0500 -Subject: [PATCH 2/3] Fix SIGBUS due to unaligned access - -Update RAPIDJSON_ALIGN() to always align on an 8-byte boundary -unless otherwise overridden. - -On some platforms (such as ARM), 64-bit items (such as doubles and -64-bit integers) must be aligned to an 8 byte address, even though the -architecture is only 32-bits. On these platforms, MemoryPoolAllocator -must match the malloc() behavior and return a 8 byte aligned allocation. -This eliminates any alignment issues that may occur at the expense of -additional memory overhead. - -Failure to do so caused a SIGBUS signal when calling -GenericValue::SetNull(). The size of the data_ member of the -GenericValue class is 16 bytes in 32-bit mode and its constructor -requires an 8-byte aligned access. - -While parsing a JSON formatted string using Document::ParseStream(), a -stack object containing GenericValue items was constructed. Since the -stack was 8-byte aligned, the constructor calls would succeed. When the -lifetime of the object ends, SetObjectRaw() is invoked. This triggered -an allocation with 4-byte alignment to which the previously 8-byte -aligned GenericValue array was copied. After this, any call to a -GenericValue API that triggered the constructor and thus the placement -new operation on the Data type member would trigger a SIGBUS. - -Signed-off-by: Veselin Georgiev -Signed-off-by: Joshua Watt ---- - include/rapidjson/rapidjson.h | 9 ++------- - test/unittest/allocatorstest.cpp | 26 ++++++++++++-------------- - 2 files changed, 14 insertions(+), 21 deletions(-) - +@@ -1629,7 +1629,7 @@ public: + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); +- std::memmove(pos, last, static_cast(End() - last) * sizeof(GenericValue)); ++ std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } +@@ -1936,7 +1936,7 @@ private: + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); +- std::memcpy(e, values, count * sizeof(GenericValue)); ++ std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); +@@ -1949,7 +1949,7 @@ private: + if (count) { + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); +- std::memcpy(m, members, count * sizeof(Member)); ++ std::memcpy(static_cast(m), members, count * sizeof(Member)); + } + else + SetMembersPointer(0); +diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h +index 9d3e88c9..949cbb1f 100644 +--- a/include/rapidjson/internal/biginteger.h ++++ b/include/rapidjson/internal/biginteger.h +@@ -133,7 +133,7 @@ public: + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { +- std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); ++ std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 053b2ce4..ab925012 100644 --- a/include/rapidjson/rapidjson.h @@ -88,6 +74,59 @@ index 053b2ce4..ab925012 100644 #endif /////////////////////////////////////////////////////////////////////////////// +diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h +index b182aa27..8a21edba 100644 +--- a/include/rapidjson/schema.h ++++ b/include/rapidjson/schema.h +@@ -400,7 +400,7 @@ public: + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; +- char buffer[256 + 24]; ++ char buffer[256u + 24]; + MemoryPoolAllocator<> hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); +@@ -1017,10 +1017,12 @@ private: + template + RegexType* CreatePattern(const ValueType& value) { + if (value.IsString()) ++ RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { +- return new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); ++ return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error&) { ++ AllocatorType::Free(r); + } + return 0; + } +diff --git a/test/perftest/schematest.cpp b/test/perftest/schematest.cpp +index 468f5fe6..5e1862f9 100644 +--- a/test/perftest/schematest.cpp ++++ b/test/perftest/schematest.cpp +@@ -11,6 +11,12 @@ + + using namespace rapidjson; + ++ ++RAPIDJSON_DIAG_PUSH ++#if defined(__GNUC__) && __GNUC__ >= 7 ++RAPIDJSON_DIAG_OFF(format-overflow) ++#endif ++ + template + static char* ReadFile(const char* filename, Allocator& allocator) { + const char *paths[] = { +@@ -42,6 +48,8 @@ static char* ReadFile(const char* filename, Allocator& allocator) { + return json; + } + ++RAPIDJSON_DIAG_POP ++ + class Schema : public PerfTest { + public: + Schema() {} diff --git a/test/unittest/allocatorstest.cpp b/test/unittest/allocatorstest.cpp index a5958de1..2202c11f 100644 --- a/test/unittest/allocatorstest.cpp @@ -128,32 +167,31 @@ index a5958de1..2202c11f 100644 } TEST(Allocator, Issue399) { --- -2.21.0 (Apple Git-122) - - -From 5269266744d8b61575463df7d8b03615904cc2f4 Mon Sep 17 00:00:00 2001 -From: abolz -Date: Sat, 16 Jun 2018 09:41:04 +0200 -Subject: [PATCH 3/3] Fix offset computation in BigInteger::operator<< - ---- - include/rapidjson/internal/biginteger.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/rapidjson/internal/biginteger.h b/include/rapidjson/internal/biginteger.h -index 9d3e88c9..949cbb1f 100644 ---- a/include/rapidjson/internal/biginteger.h -+++ b/include/rapidjson/internal/biginteger.h -@@ -133,7 +133,7 @@ public: - RAPIDJSON_ASSERT(count_ + offset <= kCapacity); +diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp +index d75b1e59..6c6de8e4 100644 +--- a/test/unittest/schematest.cpp ++++ b/test/unittest/schematest.cpp +@@ -1117,7 +1117,7 @@ private: + typename DocumentType::AllocatorType documentAllocator_; + typename SchemaDocumentType::AllocatorType schemaAllocator_; + char documentBuffer_[16384]; +- char schemaBuffer_[128 * 1024]; ++ char schemaBuffer_[128u * 1024]; + }; - if (interShift == 0) { -- std::memmove(&digits_[count_ - 1 + offset], &digits_[count_ - 1], count_ * sizeof(Type)); -+ std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); - count_ += offset; - } - else { --- -2.21.0 (Apple Git-122) - + TEST(SchemaValidator, TestSuite) { +diff --git a/test/unittest/simdtest.cpp b/test/unittest/simdtest.cpp +index b01b559f..531d74c6 100644 +--- a/test/unittest/simdtest.cpp ++++ b/test/unittest/simdtest.cpp +@@ -105,8 +105,8 @@ struct ScanCopyUnescapedStringHandler : BaseReaderHandler, ScanCopyUnesca + + template + void TestScanCopyUnescapedString() { +- char buffer[1024 + 5 + 32]; +- char backup[1024 + 5 + 32]; ++ char buffer[1024u + 5 + 32]; ++ char backup[1024u + 5 + 32]; + + // Test "ABCDABCD...\\" + for (size_t offset = 0; offset < 32; offset++) { diff --git a/patches/yoga.patch b/patches/yoga.patch index 3ce01eb..1941dbb 100644 --- a/patches/yoga.patch +++ b/patches/yoga.patch @@ -1,3 +1,12 @@ +diff --git a/.gitignore b/.gitignore +index 44f230ed..ddb5fae1 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -68,3 +68,4 @@ Carthage/Build + .gradle + # NDK/CMake + .externalNativeBuild ++/cmake-build-debug/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 018c269f..5a1a73c8 100644 --- a/CMakeLists.txt @@ -162,6 +171,33 @@ index 56aa299d..79f38e2c 100644 TEST(YogaTest, copy_style_same) { const YGNodeRef node0 = YGNodeNew(); +diff --git a/yoga/Utils.cpp b/yoga/Utils.cpp +index eaa74b06..a431cffb 100644 +--- a/yoga/Utils.cpp ++++ b/yoga/Utils.cpp +@@ -6,7 +6,11 @@ + */ + + #include "Utils.h" ++#if defined( __cpp_exceptions ) + #include ++#else ++#include ++#endif + + using namespace facebook; + +@@ -75,5 +79,10 @@ YGFloatOptional YGFloatOptionalMax(YGFloatOptional op1, YGFloatOptional op2) { + } + + void throwLogicalErrorWithMessage(const char* message) { ++#if defined( __cpp_exceptions ) + throw std::logic_error(message); ++#else ++ perror(message); ++ std::terminate(); ++#endif + } diff --git a/yoga/YGNode.cpp b/yoga/YGNode.cpp index f4c14bf3..aaa5cf7a 100644 --- a/yoga/YGNode.cpp diff --git a/test/parseDirective.cpp b/test/parseDirective.cpp index 55f2caf..828f5fc 100644 --- a/test/parseDirective.cpp +++ b/test/parseDirective.cpp @@ -26,6 +26,7 @@ #endif #include "apl/content/directive.h" +#include "apl/utils/stringfunctions.h" #include "utils.h" @@ -60,22 +61,6 @@ struct Options { Options gOptions; -inline std::string -doubleToString(double value) -{ - static char *separator = std::localeconv()->decimal_point; - - if (value > 100000000000) return "INF"; - if (value < -100000000000) return "-INF"; - - auto s = std::to_string(value); - auto it = s.find_last_not_of('0'); - if (it != s.find(separator)) // Remove a trailing decimal point - it++; - s.erase(it, std::string::npos); - return s; -} - /** * Simplify the transform */ @@ -92,7 +77,7 @@ void fixArray(rapidjson::Value& tree, const char *name, rapidjson::Document::All auto s = "["+ std::accumulate(values.begin(), values.end(), std::string(), [](const std::string& a, double b) -> std::string { - return a + (a.empty() ? "" : ",") + doubleToString(b); + return a + (a.empty() ? "" : ",") + doubleToAplFormattedString(b); }) + "]"; transform.SetString(s.c_str(), s.size(), alloc); } diff --git a/test/parseExpression.cpp b/test/parseExpression.cpp index d24bbfb..5e01b4b 100644 --- a/test/parseExpression.cpp +++ b/test/parseExpression.cpp @@ -83,7 +83,7 @@ main(int argc, char *argv[]) // and evaluate it "N" times if (optimize) { auto cbc = apl::getDataBinding(*c, m); - if (cbc.isByteCode()) { + if (cbc.is()) { apl::SymbolReferenceMap map; cbc.symbols(map); } @@ -110,16 +110,16 @@ main(int argc, char *argv[]) auto cbc = apl::getDataBinding(*c, m); - if (verbose && cbc.isByteCode()) - cbc.getByteCode()->dump(); + if (verbose && cbc.is()) + cbc.get()->dump(); std::cout << "Evaluates to " << cbc.eval().asString() << std::endl; - if (optimize && cbc.isByteCode()) { + if (optimize && cbc.is()) { apl::SymbolReferenceMap map; cbc.symbols(map); std::cout << std::endl << "Optimized version" << std::endl; - cbc.getByteCode()->dump(); + cbc.get()->dump(); std::cout << "optimized version evaluates to " << cbc.eval().asString() << std::endl; } } diff --git a/thirdparty/thirdparty.cmake b/thirdparty/thirdparty.cmake index f99f570..5bffbb0 100644 --- a/thirdparty/thirdparty.cmake +++ b/thirdparty/thirdparty.cmake @@ -20,7 +20,7 @@ foreach(CACHE_VAR ${CACHE_VARS}) endif() endforeach() -set(EXT_CXX_ARGS "-std=c++11 ${WASM_FLAGS} ${CMAKE_CXX_FLAGS}") +set(EXT_CXX_ARGS "-std=c++11 -fno-exceptions ${WASM_FLAGS} ${CMAKE_CXX_FLAGS}") if (BUILD_SHARED OR ENABLE_PIC) set(EXT_CXX_ARGS "${EXT_CXX_ARGS} -fPIC") endif() diff --git a/unit/CMakeLists.txt b/unit/CMakeLists.txt index f58520e..f5f3c79 100644 --- a/unit/CMakeLists.txt +++ b/unit/CMakeLists.txt @@ -23,8 +23,7 @@ add_executable( unittest testeventloop.cpp debugtools.cpp unittest_simpletextmeasurement.cpp - unittest_testeventloop.cpp -) + unittest_testeventloop.cpp) if (BUILD_ENUMGEN) target_sources(unittest PRIVATE diff --git a/unit/animation/unittest_easing.cpp b/unit/animation/unittest_easing.cpp index 3656b6e..df10add 100644 --- a/unit/animation/unittest_easing.cpp +++ b/unit/animation/unittest_easing.cpp @@ -18,7 +18,6 @@ #include #include "apl/animation/coreeasing.h" -#include "apl/animation/easinggrammar.h" using namespace apl; @@ -264,17 +263,20 @@ static std::vector sFailureCases = { "path(0.2,0.2,0.1,0.5)", // Out of order "cubic-bezier()", "cubic-bezier(1,2,3)", - "cubic-bezier(1,2,3,4,5)" + "cubic-bezier(1,2,3,4,5)", "line() end(1,1)", // Wrong number of arguments + "line(0,0) end(1,1,1)", // Wrong arguments "line(1) end(1,1)", "line(a", "line(1, end(1,1)", "line(1,1)", // No end value "line(1,2,3)", "line(1,1) end(0,1)", // Invalid times + "line(0,0) line(0.25, 1) line(0,0) end(1,1)", // Invalid times "curve(0,0) end(1,1)", // Wrong number of arguments "curve(0,1,2,3,4,5,6,7,8) end(1,2)", "curve(1,0,1,1,1,1) end(0,1)", // Invalid times + "curve(0, 0, 0.25, 0.10, 0.25, 1.0) curve(0, 0, 0, 0, 0, 0) end(1,1)", // Invalid times "end(0,1)", "line(0,1) line(2,1) end(1,1)", // Invalid times "send(1,2,3)", @@ -287,6 +289,8 @@ static std::vector sFailureCases = { "spatial(2,-1) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) send(1,0,0)", // Invalid spatial index "spatial(3,0) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) send(1,0,0)", // DOF mismatch "spatial(2,0) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) send(-1,0,0)", // Invalid time + "spatial(2,0,0) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) send(1,0,0)", // Invalid arguments + "spatial(2,0) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) scurve(0, 0,0, 0,0, 0,0, 0.25,0.25,0.25,0.25) send(1,0,0)", }; TEST_F(EasingTest, EasingFail) @@ -367,9 +371,9 @@ TEST_F(EasingTest, Rotate) ASSERT_TRUE(component); auto graphicObject = component->getCalculated(kPropertyGraphic); - ASSERT_TRUE(graphicObject.isGraphic()); + ASSERT_TRUE(graphicObject.is()); - auto graphic = graphicObject.getGraphic(); + auto graphic = graphicObject.get(); ASSERT_TRUE(graphic); auto graphicRoot = graphic->getRoot(); @@ -394,7 +398,7 @@ TEST_F(EasingTest, Rotate) advanceTime(5000); ASSERT_TRUE(IsEqual(222, group1->getValue(kGraphicPropertyTranslateX))); - ASSERT_TRUE(IsEqual(Transform2D::translate(222, 118.5), group1->getValue(kGraphicPropertyTransform).getTransform2D())); + ASSERT_TRUE(IsEqual(Transform2D::translate(222, 118.5), group1->getValue(kGraphicPropertyTransform).get())); } diff --git a/unit/animation/unittest_easing_approximation.cpp b/unit/animation/unittest_easing_approximation.cpp index e4dfcb2..083606b 100644 --- a/unit/animation/unittest_easing_approximation.cpp +++ b/unit/animation/unittest_easing_approximation.cpp @@ -28,7 +28,7 @@ compareCurve(const TestCurve& testCurve, { for (double t = 0.0; t <= 1.0; t += 0.01) { for (int index = 0; index < testCurve.dof(); index++) { - if (::abs(testCurve.position(t, index) - approx->getPosition(t, index)) > epsilon) + if (std::abs(testCurve.position(t, index) - approx->getPosition(t, index)) > epsilon) return ::testing::AssertionFailure() << " position mismatch at percentage=" << t << " index=" << index << " expected=" << testCurve.position(t, index) diff --git a/unit/audio/testaudioplayer.cpp b/unit/audio/testaudioplayer.cpp index f7c78cb..8878728 100644 --- a/unit/audio/testaudioplayer.cpp +++ b/unit/audio/testaudioplayer.cpp @@ -16,6 +16,8 @@ #include "testaudioplayer.h" #include "testaudioplayerfactory.h" +#include "apl/utils/log.h" + namespace apl { const bool DEBUG_TEST_AUDIO_PLAYER = false; diff --git a/unit/audio/testaudioplayerfactory.h b/unit/audio/testaudioplayerfactory.h index dc6a6dc..33faa58 100644 --- a/unit/audio/testaudioplayerfactory.h +++ b/unit/audio/testaudioplayerfactory.h @@ -43,7 +43,7 @@ class TestAudioPlayerFactory : public AudioPlayerFactory, AudioPlayerPtr createPlayer(AudioPlayerCallback playerCallback, SpeechMarkCallback speechMarkCallback) override { - auto self = std::dynamic_pointer_cast(shared_from_this()); + auto self = std::static_pointer_cast(shared_from_this()); auto player = std::make_shared(std::move(playerCallback), std::move(speechMarkCallback), std::move(self)); diff --git a/unit/audio/unittest_command_page_audio.cpp b/unit/audio/unittest_command_page_audio.cpp index f749c2d..b4be86c 100644 --- a/unit/audio/unittest_command_page_audio.cpp +++ b/unit/audio/unittest_command_page_audio.cpp @@ -44,7 +44,7 @@ class CommandPageAudioTest : public AudioTest { << ", actual: " << actualId; } - auto actualBounds = child->getCalculated(kPropertyBounds).getRect(); + auto actualBounds = child->getCalculated(kPropertyBounds).get(); if (bounds != actualBounds) { return ::testing::AssertionFailure() << "child " << idx diff --git a/unit/audio/unittest_dynamictokenlist_audio.cpp b/unit/audio/unittest_dynamictokenlist_audio.cpp index be684a6..9cf2ebe 100644 --- a/unit/audio/unittest_dynamictokenlist_audio.cpp +++ b/unit/audio/unittest_dynamictokenlist_audio.cpp @@ -226,7 +226,7 @@ TEST_F(DynamicTokenListAudioTest, DeepProgressive) { config->dataSourceProvider("testList", source); loadDocument(BIT_BY_A_BIT_DEEP, BIT_BY_A_BIT_DATA); - auto sequence = std::static_pointer_cast(root->findComponentById("dynamicSequence")); + auto sequence = CoreComponent::cast(root->findComponentById("dynamicSequence")); ASSERT_TRUE(sequence); ASSERT_EQ(1, sequence->getChildCount()); diff --git a/unit/audio/unittest_sequencer_audio.cpp b/unit/audio/unittest_sequencer_audio.cpp index c9b10f7..4e609ef 100644 --- a/unit/audio/unittest_sequencer_audio.cpp +++ b/unit/audio/unittest_sequencer_audio.cpp @@ -164,8 +164,8 @@ TEST_F(SequencerAudioTest, SpeakItemAndScroll) ASSERT_TRUE(CheckPlayer("URL3", TestAudioPlayer::kReady)); ASSERT_FALSE(factory->hasEvent()); - auto bounds = component->getCalculated(kPropertyBounds).getRect(); - auto childBounds = component->getChildAt(0)->getCalculated(kPropertyBounds).getRect(); + auto bounds = component->getCalculated(kPropertyBounds).get(); + auto childBounds = component->getChildAt(0)->getCalculated(kPropertyBounds).get(); auto position = Point(0, (childBounds.getCenterY() - bounds.getCenterY()) / 2); ASSERT_EQ(position, component->scrollPosition()); diff --git a/unit/audio/unittest_speak_item_audio.cpp b/unit/audio/unittest_speak_item_audio.cpp index 3af8fff..fb4baaa 100644 --- a/unit/audio/unittest_speak_item_audio.cpp +++ b/unit/audio/unittest_speak_item_audio.cpp @@ -1019,7 +1019,7 @@ TEST_F(SpeakItemAudioTest, TransitionalRequests) ASSERT_TRUE(root->hasEvent()); auto event = root->popEvent(); ASSERT_EQ(kEventTypeRequestLineBounds, event.getType()); - auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).getRect(); + auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).get(); event.getActionRef().resolve({0, 0, textFieldBoundary.getWidth(), 10}); advanceTime(100); @@ -1133,7 +1133,7 @@ TEST_F(SpeakItemAudioTest, PreserveTesting) ASSERT_TRUE(root->hasEvent()); auto event = root->popEvent(); ASSERT_EQ(kEventTypeRequestLineBounds, event.getType()); - auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).getRect(); + auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).get(); event.getActionRef().resolve({0, 0, textFieldBoundary.getWidth(), 10}); advanceTime(100); @@ -1145,7 +1145,7 @@ TEST_F(SpeakItemAudioTest, PreserveTesting) ASSERT_TRUE(CheckPlayer("URL1", TestAudioPlayer::kPlay)); ASSERT_EQ(component->scrollPosition().getY(), textFieldBoundary.getY()); - auto text = std::dynamic_pointer_cast(root->findComponentById("text1")); + auto text = CoreComponent::cast(root->findComponentById("text1")); // Will also ask to scroll to the first line for play ASSERT_TRUE(verifyLineUpdate(root, text, 0, 0, 4)); @@ -1166,7 +1166,7 @@ TEST_F(SpeakItemAudioTest, PreserveTesting) loop->rehydrate(playerTimer); - text = std::dynamic_pointer_cast(root->findComponentById("text1")); + text = CoreComponent::cast(root->findComponentById("text1")); ASSERT_TRUE(text->getState().get(apl::kStateKaraoke)); // "give your office a holiday" @@ -1217,7 +1217,7 @@ TEST_F(SpeakItemAudioTest, PreserveTestingNoTarget) ASSERT_TRUE(root->hasEvent()); auto event = root->popEvent(); ASSERT_EQ(kEventTypeRequestLineBounds, event.getType()); - auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).getRect(); + auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).get(); event.getActionRef().resolve({0, 0, textFieldBoundary.getWidth(), 10}); advanceTime(100); @@ -1229,7 +1229,7 @@ TEST_F(SpeakItemAudioTest, PreserveTestingNoTarget) ASSERT_TRUE(CheckPlayer("URL1", TestAudioPlayer::kPlay)); ASSERT_EQ(component->scrollPosition().getY(), textFieldBoundary.getY()); - auto text = std::dynamic_pointer_cast(root->findComponentById("text1")); + auto text = CoreComponent::cast(root->findComponentById("text1")); // Will also ask to scroll to the first line for play ASSERT_TRUE(verifyLineUpdate(root, text, 0, 0, 4)); @@ -1438,7 +1438,7 @@ TEST_F(SpeakItemAudioTest, MarksAfterText) ASSERT_TRUE(root->hasEvent()); auto event = root->popEvent(); ASSERT_EQ(kEventTypeRequestLineBounds, event.getType()); - auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).getRect(); + auto textFieldBoundary = root->findComponentById("text1")->getCalculated(apl::kPropertyBounds).get(); event.getActionRef().resolve({0, 0, textFieldBoundary.getWidth(), 10}); advanceTime(100); diff --git a/unit/audio/unittest_speech_marks.cpp b/unit/audio/unittest_speech_marks.cpp index 10972f4..5c0a586 100644 --- a/unit/audio/unittest_speech_marks.cpp +++ b/unit/audio/unittest_speech_marks.cpp @@ -152,4 +152,34 @@ TEST_F(SpeechMarkTest, PollyTurtles) result1.insert(result1.end(), result2.begin(), result2.end()); ASSERT_EQ(result1, TURTLES_EXPECTED); +} + +static const char * POLLY_EXAMPLE_BAD = R"apl( +{"time":0,"type":"sentence","start":0,"end":23,"value":"Mary had a little lamb."} +{"time":6,"type":"word","start":0,"end":4,"value":"Mary"} +{"time":6,"type":"viseme","value":"p"} +{"time":73,"type":"viseme","value":"E"} +{"time":180,"type":"viseme","value":"r"} +{"time":292,"type":"viseme","value":"i" +{"time":373,"type":"word","start":5,"end":8,"value":"had"} +{"time":373,"type":"viseme","value":"k"} +{"time":460,"type":"viseme","value":"a"} +{"time":521,"type":"viseme","value":"t"} +{"time":604,"type":"word","start":9,"end":10,"value":"a"} +{"time":604,"type":"viseme","value":"@"} +{"time":643,"type":"word","start":11,"end":17,"value":"little"} +{"time":643,"type":"viseme","value":"t"} +{"time":739,"type":"viseme","value":"i"} +{"time":769,"type":"viseme","value":"t"} +{"time":799,"type":"viseme","value":"t"} +{"time":882,"type":"word","start":18,"end":22,"value":"lamb"} +{"time":882,"type":"viseme","value":"t"} +{"time":964,"type":"viseme","value":"a"} +{"time":1082,"type":"viseme","value":"p"} +)apl"; + +TEST_F(SpeechMarkTest, PollyExampleBad) +{ + auto result = parsePollySpeechMarks(POLLY_EXAMPLE_BAD, strlen(POLLY_EXAMPLE_BAD)); + ASSERT_EQ(result.size(), 6); } \ No newline at end of file diff --git a/unit/command/unittest_command_animateitem.cpp b/unit/command/unittest_command_animateitem.cpp index 9e480c8..ee8f43b 100644 --- a/unit/command/unittest_command_animateitem.cpp +++ b/unit/command/unittest_command_animateitem.cpp @@ -606,13 +606,13 @@ TEST_F(AnimateItemTest, OpacityAndTransform) ASSERT_TRUE(goButton); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); ASSERT_EQ(Object(0), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Transform2D::translateX(metrics.getWidth()), frame->getCalculated(kPropertyTransform).getTransform2D()); + ASSERT_EQ(Transform2D::translateX(metrics.getWidth()), frame->getCalculated(kPropertyTransform).get()); ASSERT_TRUE(CheckDirty(frame, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); for (int repeat = 0 ; repeat <= 3 ; repeat++) { @@ -628,7 +628,7 @@ TEST_F(AnimateItemTest, OpacityAndTransform) ASSERT_EQ(Object(expectedOpacity), frame->getCalculated(kPropertyOpacity)); ASSERT_TRUE(IsEqual(Transform2D::translateX(expectedX), - frame->getCalculated(kPropertyTransform).getTransform2D())); + frame->getCalculated(kPropertyTransform).get())); ASSERT_TRUE(CheckDirty(frame, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); } } @@ -647,13 +647,13 @@ TEST_F(AnimateItemTest, OpacityAndTransformTerminate) ASSERT_TRUE(goButton); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); ASSERT_EQ(Object(0), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Transform2D::translateX(metrics.getWidth()), frame->getCalculated(kPropertyTransform).getTransform2D()); + ASSERT_EQ(Transform2D::translateX(metrics.getWidth()), frame->getCalculated(kPropertyTransform).get()); ASSERT_TRUE(CheckDirty(frame, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); auto startTime = loop->currentTime(); @@ -663,7 +663,7 @@ TEST_F(AnimateItemTest, OpacityAndTransformTerminate) float expectedX = metrics.getWidth() * (1000 - i) * .001; ASSERT_EQ(Object(expectedOpacity), frame->getCalculated(kPropertyOpacity)); ASSERT_TRUE(IsEqual(Transform2D::translateX(expectedX), - frame->getCalculated(kPropertyTransform).getTransform2D())); + frame->getCalculated(kPropertyTransform).get())); ASSERT_TRUE(CheckDirty(frame, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); } @@ -672,7 +672,7 @@ TEST_F(AnimateItemTest, OpacityAndTransformTerminate) ASSERT_EQ(0, loop->size()); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); ASSERT_TRUE(CheckDirty(frame, kPropertyTransform, kPropertyOpacity, kPropertyVisualHash)); } @@ -746,7 +746,7 @@ TEST_F(AnimateItemTest, OpacityAndRichTransform) ASSERT_TRUE(goButton); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); @@ -766,7 +766,7 @@ TEST_F(AnimateItemTest, OpacityAndRichTransform) Transform2D::translate(-50, -50); ASSERT_EQ(Object(expectedOpacity), frame->getCalculated(kPropertyOpacity)); - ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).getTransform2D())) + ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).get())) << " time=" << i << " tx=" << expectedX << " scale=" << expectedScale << " rotate=" << expectedAngle; ASSERT_TRUE(CheckDirty(frame, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); } @@ -838,7 +838,7 @@ TEST_F(AnimateItemTest, ResourceTest) ASSERT_TRUE(goButton); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); @@ -851,7 +851,7 @@ TEST_F(AnimateItemTest, ResourceTest) Transform2D expectedTransform = Transform2D::translateX(expectedX); ASSERT_NEAR(expectedOpacity, frame->getCalculated(kPropertyOpacity).asNumber(), 0.0001); - ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).getTransform2D())); + ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).get())); ASSERT_TRUE(CheckDirty(frame, kPropertyTransform, kPropertyOpacity, kPropertyVisualHash)); } @@ -907,7 +907,7 @@ TEST_F(AnimateItemTest, MissingTransformFrom) ASSERT_TRUE(frame); ASSERT_TRUE(goButton); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); @@ -974,7 +974,7 @@ TEST_F(AnimateItemTest, MissingTransformRotate) ASSERT_TRUE(goButton); ASSERT_EQ(Object(1), frame->getCalculated(kPropertyOpacity)); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 100); root->clearPending(); @@ -987,7 +987,7 @@ TEST_F(AnimateItemTest, MissingTransformRotate) Transform2D expectedTransform = Transform2D::translateX(expectedX); // The Rotation transformation only showed up in the "from" list, so it is ignored - ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).getTransform2D())); + ASSERT_TRUE(IsEqual(expectedTransform, frame->getCalculated(kPropertyTransform).get())); ASSERT_TRUE(CheckDirty(frame, kPropertyTransform)); } diff --git a/unit/command/unittest_command_animateitem_values.cpp b/unit/command/unittest_command_animateitem_values.cpp index 5164ba8..a86990c 100644 --- a/unit/command/unittest_command_animateitem_values.cpp +++ b/unit/command/unittest_command_animateitem_values.cpp @@ -383,7 +383,7 @@ TEST_F(AnimateItemValueTest, AnimateVG) loadDocument(ANIMATE_VG); ASSERT_TRUE(component); - auto graphic = component->getCalculated(apl::kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(apl::kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto container = graphic->getRoot(); @@ -470,7 +470,7 @@ TEST_F(AnimateItemValueTest, BadVGParameters) loadDocument(BAD_VG_PARAMETERS); ASSERT_TRUE(component); - auto graphic = component->getCalculated(apl::kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(apl::kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto container = graphic->getRoot(); @@ -534,7 +534,7 @@ TEST_F(AnimateItemValueTest, NoVG) ASSERT_TRUE(component); auto graphic = component->getCalculated(apl::kPropertyGraphic); - ASSERT_FALSE(graphic.isGraphic()); + ASSERT_FALSE(graphic.is()); // Animate a property that doesn't exist executeCommand("ChangeValue", {{"TO", "red"}, {"PARAM", "COLOR"}}, false); diff --git a/unit/command/unittest_command_document.cpp b/unit/command/unittest_command_document.cpp index c3b116a..168c150 100644 --- a/unit/command/unittest_command_document.cpp +++ b/unit/command/unittest_command_document.cpp @@ -213,7 +213,7 @@ TEST_F(MountTest, AnimateMultiple) thing1->getCalculated(kPropertyColor)); ASSERT_TRUE(IsEqual(Transform2D::translateX(100 * (1.0 - delta)), - thing2->getCalculated(kPropertyTransform).getTransform2D())); + thing2->getCalculated(kPropertyTransform).get())); ASSERT_NEAR(delta,component->getCalculated(kPropertyOpacity).asNumber(),0.0001); } } diff --git a/unit/command/unittest_command_event_binding.cpp b/unit/command/unittest_command_event_binding.cpp index 3f93506..9176e25 100644 --- a/unit/command/unittest_command_event_binding.cpp +++ b/unit/command/unittest_command_event_binding.cpp @@ -261,9 +261,9 @@ TEST_F(CommandEventBinding, VideoComponentEventInterpolation) auto array = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(3, array.size()); - ASSERT_EQ("track2", array.at(0).getMediaSource().getUrl()); - ASSERT_EQ("track13", array.at(1).getMediaSource().getUrl()); - ASSERT_EQ("track3", array.at(2).getMediaSource().getUrl()); + ASSERT_EQ("track2", array.at(0).get().getUrl()); + ASSERT_EQ("track13", array.at(1).get().getUrl()); + ASSERT_EQ("track3", array.at(2).get().getUrl()); // Start playback component->updateMediaState(MediaState(0, @@ -288,5 +288,5 @@ TEST_F(CommandEventBinding, VideoComponentEventInterpolation) array = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(1, array.size()); - ASSERT_EQ("clip0-3", array.at(0).getMediaSource().getUrl()); + ASSERT_EQ("clip0-3", array.at(0).get().getUrl()); } diff --git a/unit/command/unittest_command_page.cpp b/unit/command/unittest_command_page.cpp index 32ed2f9..0305a06 100644 --- a/unit/command/unittest_command_page.cpp +++ b/unit/command/unittest_command_page.cpp @@ -55,7 +55,7 @@ class CommandPageTest : public CommandTest { << ", actual: " << actualId; } - auto actualBounds = child->getCalculated(kPropertyBounds).getRect(); + auto actualBounds = child->getCalculated(kPropertyBounds).get(); if (bounds != actualBounds) { return ::testing::AssertionFailure() << "child " << idx diff --git a/unit/command/unittest_command_sendevent.cpp b/unit/command/unittest_command_sendevent.cpp index ddef655..13409aa 100644 --- a/unit/command/unittest_command_sendevent.cpp +++ b/unit/command/unittest_command_sendevent.cpp @@ -361,3 +361,102 @@ TEST_F(CommandSendEventTest, SendEventWithDefaultFlags) ASSERT_EQ(Object(7), flags.get("three")); ASSERT_EQ(Object("I_AM_DEFAULT"), flags.get("four")); } + +static const char *SENDEVENT_WITH_NULL_COMPONENTS = R"apl({ + "type": "APL", + "version": "2022.2", + "commands": { + "SendEventWrap1": { + "parameters": [ + { + "name": "arguments", + "type": "array" + }, + { + "name": "components", + "type": "array" + } + ], + "commands": [ + { + "type": "SendEvent", + "arguments": [ + "${arguments}", + { + "utcTime": "${utcTime}" + } + ], + "components": "${components}" + } + ] + } + }, + "mainTemplate": { + "item": { + "type": "TouchWrapper", + "width": "100%", + "height": "100%", + "onPress": [ + { + "type": "SendEventWrap1", + "arguments": ["8C246"] + } + ] + } + } +})apl"; + +TEST_F(CommandSendEventTest, SendEventWithNullComponents) +{ + loadDocument(SENDEVENT_WITH_NULL_COMPONENTS); + ASSERT_TRUE(component); + + performClick(10, 10); + advanceTime(500); + + auto event = root->popEvent(); + ASSERT_EQ(kEventTypeSendEvent, event.getType()); + auto arguments = event.getValue(kEventPropertyArguments).getArray(); + ASSERT_EQ(Object("8C246"), arguments.at(0)); + + ASSERT_TRUE(event.getValue(kEventPropertyComponents).empty()); + ASSERT_TRUE(ConsoleMessage()); +} + +static const char *SENDEVENT_WITH_TYPE_MIX_COMPONENTS = R"apl({ + "type": "APL", + "version": "2022.2", + "mainTemplate": { + "item": { + "id": "root", + "type": "TouchWrapper", + "width": "100%", + "height": "100%", + "onPress": [ + { + "type": "SendEvent", + "components": ["root", null, 42, {"fuzzy":"duck"}] + } + ] + } + } +})apl"; + +TEST_F(CommandSendEventTest, SendEventWithTypeMixComponents) +{ + loadDocument(SENDEVENT_WITH_TYPE_MIX_COMPONENTS); + ASSERT_TRUE(component); + + performClick(10, 10); + advanceTime(500); + + auto event = root->popEvent(); + ASSERT_EQ(kEventTypeSendEvent, event.getType()); + auto components = event.getValue(kEventPropertyComponents).getMap(); + const auto& pair = components.begin(); + ASSERT_EQ(pair->first, "root"); + ASSERT_EQ(pair->second, component->getValue()); + + // 3 messages for 3 non-string component IDs. + ASSERT_TRUE(ConsoleMessage()); +} diff --git a/unit/command/unittest_command_setvalue.cpp b/unit/command/unittest_command_setvalue.cpp index b4e8f61..94bb961 100644 --- a/unit/command/unittest_command_setvalue.cpp +++ b/unit/command/unittest_command_setvalue.cpp @@ -292,13 +292,13 @@ TEST_F(CommandSetValueTest, Video) auto source = component->getCalculated(kPropertySource); ASSERT_TRUE(source.isArray()); - ASSERT_EQ("https://video.com/video.mp4", source.at(0).getMediaSource().getUrl()); + ASSERT_EQ("https://video.com/video.mp4", source.at(0).get().getUrl()); executeSetValue("video", "source", "https://video.com/new_video.mp4"); ASSERT_TRUE(root->isDirty()); root->clearDirty(); source = component->getCalculated(kPropertySource); ASSERT_TRUE(source.isArray()); - ASSERT_EQ("https://video.com/new_video.mp4", source.at(0).getMediaSource().getUrl()); + ASSERT_EQ("https://video.com/new_video.mp4", source.at(0).get().getUrl()); ASSERT_TRUE(CheckNoActions()); } diff --git a/unit/command/unittest_commands.cpp b/unit/command/unittest_commands.cpp index 2a9e52f..a2ccdba 100644 --- a/unit/command/unittest_commands.cpp +++ b/unit/command/unittest_commands.cpp @@ -976,8 +976,8 @@ static const char *SET_STATE_FOCUSED = R"({ TEST_F(CommandTest, SetStateFocused) { loadDocument(SET_STATE_FOCUSED); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -1164,25 +1164,25 @@ TEST_F(CommandTest, ExecuteFocus) auto event = root->popEvent(); ASSERT_EQ(kEventTypeFocus, event.getType()); ASSERT_EQ(touch1, event.getComponent()); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); // Try to set the focus on a non-existing component ASSERT_FALSE(ConsoleMessage()); executeCommand("SetFocus", {{"componentId", "touch7"}}, false); ASSERT_FALSE(root->hasEvent()); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); ASSERT_TRUE(ConsoleMessage()); // There should be a warning about a missing component // Leave out the component ID executeCommand("SetFocus", {}, false); ASSERT_FALSE(root->hasEvent()); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); ASSERT_TRUE(ConsoleMessage()); // Warn about missing componentId // Refocus the component that already has the focus executeCommand("SetFocus", {{"componentId", "touch1"}}, false); ASSERT_FALSE(root->hasEvent()); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); // Clear focus executeCommand("ClearFocus", {}, false); @@ -1192,8 +1192,8 @@ TEST_F(CommandTest, ExecuteFocus) ASSERT_EQ(nullptr, event.getComponent().get()); ASSERT_TRUE(event.getActionRef().empty()); root->clearPending(); - ASSERT_FALSE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); - ASSERT_FALSE(std::static_pointer_cast(touch2)->getState().get(kStateFocused)); + ASSERT_FALSE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); + ASSERT_FALSE(CoreComponent::cast(touch2)->getState().get(kStateFocused)); // Clear focus when no focus set executeCommand("ClearFocus", {}, false); @@ -1216,12 +1216,12 @@ TEST_F(CommandTest, ExecuteFocusDisabled) auto event = root->popEvent(); ASSERT_EQ(kEventTypeFocus, event.getType()); ASSERT_EQ(touch1, event.getComponent()); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); // Disable the component executeCommand("SetValue", {{"componentId", "touch1"}, {"property", "disabled"}, {"value", true}}, false); - ASSERT_TRUE(std::static_pointer_cast(touch1)->getState().get(kStateDisabled)); - ASSERT_FALSE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_TRUE(CoreComponent::cast(touch1)->getState().get(kStateDisabled)); + ASSERT_FALSE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); ASSERT_TRUE(root->hasEvent()); event = root->popEvent(); @@ -1232,7 +1232,7 @@ TEST_F(CommandTest, ExecuteFocusDisabled) // Try to execute set focus on the disabled component executeCommand("SetFocus", {{"componentId", "touch1"}}, false); ASSERT_FALSE(root->hasEvent()); - ASSERT_FALSE(std::static_pointer_cast(touch1)->getState().get(kStateFocused)); + ASSERT_FALSE(CoreComponent::cast(touch1)->getState().get(kStateFocused)); } static const char *FINISH_BACK = R"({ @@ -1504,9 +1504,9 @@ TEST_F(CommandTest, BindingUpdateTransform){ ASSERT_TRUE(text); ASSERT_EQ(kComponentTypeText, text->getType()); - ASSERT_TRUE(IsEqual(Transform2D().translateX(64), text->getCalculated(kPropertyTransform).getTransform2D())); + ASSERT_TRUE(IsEqual(Transform2D().translateX(64), text->getCalculated(kPropertyTransform).get())); executeCommand("SetValue", {{"componentId", "myContainer"}, {"property", "len"}, {"value", "${500}"}}, false); - ASSERT_TRUE(IsEqual(Transform2D().translateX(500), text->getCalculated(kPropertyTransform).getTransform2D())); + ASSERT_TRUE(IsEqual(Transform2D().translateX(500), text->getCalculated(kPropertyTransform).get())); } diff --git a/unit/command/unittest_setstate.cpp b/unit/command/unittest_setstate.cpp index 48908e5..aa5ff7c 100644 --- a/unit/command/unittest_setstate.cpp +++ b/unit/command/unittest_setstate.cpp @@ -112,13 +112,13 @@ TEST_F(SetStateTest, BasicStateChange) // Try to explicitly set state on the child fails because of the inheritParentState value ASSERT_FALSE(ConsoleMessage()); - std::static_pointer_cast(text)->setState(kStatePressed, true); + CoreComponent::cast(text)->setState(kStatePressed, true); ASSERT_TRUE(CheckDirty(root)); ASSERT_TRUE(CheckState(text)); ASSERT_TRUE(ConsoleMessage()); // Explicitly set the color and then try changing state - std::dynamic_pointer_cast(text)->setProperty(kPropertyColor, Color(0x112233ff)); + CoreComponent::cast(text)->setProperty(kPropertyColor, Color(0x112233ff)); component->setState(kStatePressed, true); ASSERT_TRUE(CheckDirty(text, kPropertyFontStyle, kPropertyColor, kPropertyColorKaraokeTarget, kPropertyColorNonKaraoke, kPropertyVisualHash)); @@ -139,7 +139,7 @@ TEST_F(SetStateTest, StateAndPropertyChange) ASSERT_TRUE(text); // Explicitly set the color and then try changing state - std::dynamic_pointer_cast(text)->setProperty(kPropertyColor, Color(0x112233ff)); + CoreComponent::cast(text)->setProperty(kPropertyColor, Color(0x112233ff)); ASSERT_TRUE(CheckDirty(text, kPropertyColor, kPropertyColorKaraokeTarget, kPropertyColorNonKaraoke, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, text)); @@ -162,7 +162,7 @@ TEST_F(SetStateTest, PropertyMatchesState) // Explicitly set the color to the existing color Object origColor = text->getCalculated(kPropertyColor); - std::dynamic_pointer_cast(text)->setProperty(kPropertyColor, origColor); + CoreComponent::cast(text)->setProperty(kPropertyColor, origColor); // Because the value didn't change, we should not get a dirty flag ASSERT_TRUE(CheckDirty(root)); diff --git a/unit/component/unittest_accessibility_actions.cpp b/unit/component/unittest_accessibility_actions.cpp index a85923d..019e3c8 100644 --- a/unit/component/unittest_accessibility_actions.cpp +++ b/unit/component/unittest_accessibility_actions.cpp @@ -53,7 +53,7 @@ TEST_F(AccessibilityActionTest, Basic) auto actions = component->getCalculated(kPropertyAccessibilityActions); ASSERT_TRUE(actions.isArray()); ASSERT_EQ(1, actions.size()); - auto action = actions.at(0).getAccessibilityAction(); + auto action = actions.at(0).get(); ASSERT_STREQ("MakeRed", action->getName().c_str()); ASSERT_STREQ("Make the background red", action->getLabel().c_str()); ASSERT_TRUE(action->enabled()); @@ -562,7 +562,7 @@ TEST_F(AccessibilityActionTest, Enabled) const auto& actions = component->getCalculated(kPropertyAccessibilityActions); ASSERT_TRUE(actions.isArray()); ASSERT_EQ(1, actions.size()); - ASSERT_FALSE(actions.at(0).getAccessibilityAction()->enabled()); + ASSERT_FALSE(actions.at(0).get()->enabled()); // Attempt to invoke the disabled gesture component->update(kUpdateAccessibilityAction, "test"); @@ -573,7 +573,7 @@ TEST_F(AccessibilityActionTest, Enabled) component->update(kUpdatePressed, 1); root->clearPending(); ASSERT_TRUE(IsEqual("1", text->getCalculated(kPropertyText).asString())); - ASSERT_TRUE(component->getCalculated(kPropertyAccessibilityActions).at(0).getAccessibilityAction()->enabled()); + ASSERT_TRUE(component->getCalculated(kPropertyAccessibilityActions).at(0).get()->enabled()); ASSERT_TRUE(CheckDirty(text, kPropertyText, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(component, kPropertyAccessibilityActions)); ASSERT_TRUE(CheckDirty(root, text, component)); @@ -582,7 +582,7 @@ TEST_F(AccessibilityActionTest, Enabled) component->update(kUpdateAccessibilityAction, "test"); root->clearPending(); ASSERT_TRUE(IsEqual("10", text->getCalculated(kPropertyText).asString())); - ASSERT_FALSE(component->getCalculated(kPropertyAccessibilityActions).at(0).getAccessibilityAction()->enabled()); + ASSERT_FALSE(component->getCalculated(kPropertyAccessibilityActions).at(0).get()->enabled()); ASSERT_TRUE(CheckDirty(text, kPropertyText, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(component, kPropertyAccessibilityActions)); ASSERT_TRUE(CheckDirty(root, text, component)); @@ -784,11 +784,11 @@ TEST_F(AccessibilityActionTest, ArgumentPassing) ASSERT_TRUE(actions.isArray()); ASSERT_EQ(2, actions.size()); - auto a0 = actions.at(0).getAccessibilityAction(); + auto a0 = actions.at(0).get(); ASSERT_STREQ("testAction", a0->getName().c_str()); ASSERT_STREQ("This is a test action", a0->getLabel().c_str()); - auto a1 = actions.at(1).getAccessibilityAction(); + auto a1 = actions.at(1).get(); ASSERT_STREQ("testAction2", a1->getName().c_str()); ASSERT_STREQ("This is another test action", a1->getLabel().c_str()); diff --git a/unit/component/unittest_bounds.cpp b/unit/component/unittest_bounds.cpp index 0c94963..b68363e 100644 --- a/unit/component/unittest_bounds.cpp +++ b/unit/component/unittest_bounds.cpp @@ -52,13 +52,13 @@ TEST_F(BoundsTest, ScrollView) { loadDocument(SCROLL_VIEW); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); auto frame = component->getChildAt(0); ASSERT_EQ(10, frame->getChildCount()); for (auto i = 0 ; i < frame->getChildCount() ; i++) { auto child = frame->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i, 200, 200), rect); @@ -67,7 +67,7 @@ TEST_F(BoundsTest, ScrollView) component->update(kUpdateScrollPosition, 100); for (auto i = 0 ; i < frame->getChildCount() ; i++) { auto child = frame->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i - 100, 200, 200), rect); @@ -76,7 +76,7 @@ TEST_F(BoundsTest, ScrollView) component->update(kUpdateScrollPosition, 500); for (auto i = 0 ; i < frame->getChildCount() ; i++) { auto child = frame->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i - 500, 200, 200), rect); @@ -107,12 +107,12 @@ TEST_F(BoundsTest, VerticalSequence) loadDocument(VERTICAL_SEQUENCE); advanceTime(10); - ASSERT_EQ(Rect(0,0,200,500), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,200,500), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(5, component->getChildCount()); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i, 200, 200), rect); @@ -121,7 +121,7 @@ TEST_F(BoundsTest, VerticalSequence) component->update(kUpdateScrollPosition, 100); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i - 100, 200, 200), rect); @@ -130,7 +130,7 @@ TEST_F(BoundsTest, VerticalSequence) component->update(kUpdateScrollPosition, 500); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200*i, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(0, 200*i - 500, 200, 200), rect); @@ -165,12 +165,12 @@ TEST_F(BoundsTest, HorizontalSequence) loadDocument(HORIZONTAL_SEQUENCE); advanceTime(10); - ASSERT_EQ(Rect(0,0,500,200), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,500,200), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(5, component->getChildCount()); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(200*i, 0, 200, 200), rect); @@ -179,7 +179,7 @@ TEST_F(BoundsTest, HorizontalSequence) component->update(kUpdateScrollPosition, 100); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(200*i - 100, 0, 200, 200), rect); @@ -188,7 +188,7 @@ TEST_F(BoundsTest, HorizontalSequence) component->update(kUpdateScrollPosition, 500); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(200*i - 500, 0, 200, 200), rect); @@ -205,12 +205,12 @@ TEST_F(BoundsTest, HorizontalSequenceRTL) component->setProperty(kPropertyLayoutDirectionAssigned, "RTL"); root->clearPending(); - ASSERT_EQ(Rect(0,0,500,200), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,500,200), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(5, component->getChildCount()); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), rect); @@ -219,7 +219,7 @@ TEST_F(BoundsTest, HorizontalSequenceRTL) component->update(kUpdateScrollPosition, -100); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(300 - 200*i + 100, 0, 200, 200), rect); @@ -228,7 +228,7 @@ TEST_F(BoundsTest, HorizontalSequenceRTL) component->update(kUpdateScrollPosition, -500); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(300 - 200*i, 0, 200, 200), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(300 - 200*i + 500, 0, 200, 200), rect); @@ -320,8 +320,8 @@ TEST_F(BoundsTest, ChildInParent) auto label = child->getChildAt(1); // Position w.r.t. the holding container - ASSERT_EQ(Rect(0, 0, 100, 100), number->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(100, 0, 924, 100), label->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), number->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(100, 0, 924, 100), label->getCalculated(kPropertyBounds).get()); // Global position ASSERT_EQ(Rect(0, 150 + i * 100, 100, 100), number->getGlobalBounds()); @@ -345,8 +345,8 @@ TEST_F(BoundsTest, ChildInParent) auto label = child->getChildAt(1); // Position w.r.t. the holding container doesn't change - ASSERT_EQ(Rect(0, 0, 100, 100), number->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(100, 0, 924, 100), label->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), number->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(100, 0, 924, 100), label->getCalculated(kPropertyBounds).get()); // Global position moves upwards by 25 ASSERT_EQ(Rect(0, 125 + i * 100, 100, 100), number->getGlobalBounds()); @@ -420,7 +420,7 @@ TEST_F(BoundsTest, NestedChild) auto text2 = ctr2->getCoreChildAt(0); ASSERT_EQ(kComponentTypeText, text2->getType()); - ASSERT_EQ(Rect(0, 0, 50, 50), text2->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 50, 50), text2->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Rect(0, 100, 50, 50), text2->getGlobalBounds()); } @@ -490,6 +490,6 @@ TEST_F(BoundsTest, AbsolutePositioning) auto text2 = ctr2->getCoreChildAt(0); ASSERT_EQ(kComponentTypeText, text2->getType()); - ASSERT_EQ(Rect(10, 40, 50, 50), text2->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(10, 40, 50, 50), text2->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Rect(810, 340, 50, 50), text2->getGlobalBounds()); } diff --git a/unit/component/unittest_component_events.cpp b/unit/component/unittest_component_events.cpp index 7f0e3d4..6751943 100644 --- a/unit/component/unittest_component_events.cpp +++ b/unit/component/unittest_component_events.cpp @@ -531,7 +531,7 @@ TEST_F(ComponentEventsTest, TouchWrapperSendEvent) ASSERT_EQ(kEventTypeSendEvent, event.getType()); auto arguments = event.getValue(kEventPropertyArguments); - ASSERT_EQ(apl::Object::kArrayType, arguments.getType()); + ASSERT_TRUE(arguments.isArray()); ASSERT_EQ("Press", arguments.at(0).asString()); ASSERT_EQ(false, arguments.at(1).asBoolean()); ASSERT_EQ("1", arguments.at(2).asString()); @@ -546,7 +546,7 @@ TEST_F(ComponentEventsTest, TouchWrapperSendEvent) ASSERT_EQ(false, source.get("value").asBoolean()); auto components = event.getValue(kEventPropertyComponents); - ASSERT_EQ(apl::Object::kMapType, components.getType()); + ASSERT_TRUE(components.isMap()); ASSERT_EQ("One", components.get("textComp").asString()); } @@ -609,7 +609,7 @@ TEST_F(ComponentEventsTest, PagerSendEvent) ASSERT_EQ(kEventTypeSendEvent, event.getType()); auto arguments = event.getValue(kEventPropertyArguments); - ASSERT_EQ(apl::Object::kArrayType, arguments.getType()); + ASSERT_TRUE(arguments.isArray()); ASSERT_EQ("Page", arguments.at(0).asString()); ASSERT_EQ("2", arguments.at(1).asString()); ASSERT_EQ("1", arguments.at(2).asString()); @@ -628,7 +628,7 @@ TEST_F(ComponentEventsTest, PagerSendEvent) auto text3 = context->findComponentById("text3"); auto components = event.getValue(kEventPropertyComponents); - ASSERT_EQ(apl::Object::kMapType, components.getType()); + ASSERT_TRUE(components.isMap()); ASSERT_EQ("One", components.get(text1->getId()).asString()); ASSERT_EQ("Two", components.get(text2->getId()).asString()); ASSERT_EQ("Three", components.get(text3->getId()).asString()); @@ -681,7 +681,7 @@ TEST_F(ComponentEventsTest, ScrollableSendCustomEvent) ASSERT_EQ(sEventTypeBimap.at("CustomEvent"), event.getType()); auto arguments = event.getValue(kEventPropertyArguments); - ASSERT_EQ(apl::Object::kArrayType, arguments.getType()); + ASSERT_TRUE(arguments.isArray()); ASSERT_EQ("Scroll", arguments.at(0).asString()); ASSERT_EQ("1.5", arguments.at(1).asString()); ASSERT_EQ("1", arguments.at(2).asString()); @@ -843,7 +843,7 @@ validateMediaEvent(RootContextPtr root, ASSERT_EQ(TestEventCommand::eventType(), event.getType()); auto arguments = event.getValue(kEventPropertyArguments); - ASSERT_EQ(apl::Object::kArrayType, arguments.getType()); + ASSERT_TRUE(arguments.isArray()); ASSERT_EQ(handler, arguments.at(0).asString()); ASSERT_TRUE(IsEqual(value, arguments.at(1))) << handler; ASSERT_EQ("1", arguments.at(2).asString()); @@ -873,7 +873,7 @@ validateMediaEvent(RootContextPtr root, auto text = root->context().findComponentById("textComp"); auto components = event.getValue(kEventPropertyComponents); - ASSERT_EQ(apl::Object::kMapType, components.getType()); + ASSERT_TRUE(components.isMap()); ASSERT_TRUE(IsEqual("One", components.get("textComp").asString())); } @@ -1025,7 +1025,7 @@ TEST_F(ComponentEventsTest, MediaOnTimeUpdate) ASSERT_EQ(sEventTypeBimap.at("CustomEvent"), event.getType()); auto arguments = event.getValue(kEventPropertyArguments); - ASSERT_EQ(apl::Object::kArrayType, arguments.getType()); + ASSERT_TRUE(arguments.isArray()); ASSERT_EQ(Object("TimeUpdate"), arguments.at(0)); // Handler ASSERT_EQ(Object(5), arguments.at(1)); // Value (current time) ASSERT_EQ(Object(1), arguments.at(2)); // Opacity diff --git a/unit/component/unittest_default_component_size.cpp b/unit/component/unittest_default_component_size.cpp index 434ccb2..ed7ad8d 100644 --- a/unit/component/unittest_default_component_size.cpp +++ b/unit/component/unittest_default_component_size.cpp @@ -728,8 +728,7 @@ static const char *DEFAULT_IS_VALID_CHAR_TEST = R"( TEST_F(DefaultComponentTest, DefaultIsChar) { loadDocument(DEFAULT_IS_VALID_CHAR_TEST); - Component* textComponent = dynamic_cast(root->topComponent().get()); - ASSERT_EQ(kComponentTypeText, textComponent->getType()); + ASSERT_EQ(kComponentTypeText, component->getType()); //if isCharacterValid is unsupported for the component, it should return false. - ASSERT_FALSE(textComponent->isCharacterValid(L'0')); + ASSERT_FALSE(component->isCharacterValid(L'0')); } diff --git a/unit/component/unittest_draw.cpp b/unit/component/unittest_draw.cpp index 6990dbb..8dd2045 100644 --- a/unit/component/unittest_draw.cpp +++ b/unit/component/unittest_draw.cpp @@ -32,8 +32,8 @@ const double EPSILON = 0.05; inline ::testing::AssertionResult CheckAABB(const Rect &expected, const ComponentPtr &component) { - auto t2d = component->getCalculated(kPropertyTransform).getTransform2D(); - auto bounds = component->getCalculated(kPropertyBounds).getRect(); + auto t2d = component->getCalculated(kPropertyTransform).get(); + auto bounds = component->getCalculated(kPropertyBounds).get(); auto aabb = t2d.calculateAxisAlignedBoundingBox(Rect{0, 0, bounds.getWidth(), bounds.getHeight()}); aabb.offset(bounds.getTopLeft()); diff --git a/unit/component/unittest_dynamic_component.cpp b/unit/component/unittest_dynamic_component.cpp index 5e8fe02..2e41553 100644 --- a/unit/component/unittest_dynamic_component.cpp +++ b/unit/component/unittest_dynamic_component.cpp @@ -488,7 +488,7 @@ TEST_F(DynamicComponentTestSimple, Focus) ASSERT_FALSE(event.getComponent()); // The detached component should be unfocused - ASSERT_FALSE(std::static_pointer_cast(child)->getState().get(kStateFocused)); + ASSERT_FALSE(CoreComponent::cast(child)->getState().get(kStateFocused)); // The children property will be dirty ASSERT_TRUE(CheckDirty(component, kPropertyNotifyChildrenChanged)); diff --git a/unit/component/unittest_dynamic_properties.cpp b/unit/component/unittest_dynamic_properties.cpp index b08de84..ee06162 100644 --- a/unit/component/unittest_dynamic_properties.cpp +++ b/unit/component/unittest_dynamic_properties.cpp @@ -96,9 +96,9 @@ TEST_F(DynamicPropertiesTest, HeightWidthStyled) loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); - auto frame3 = std::dynamic_pointer_cast(context->findComponentById("frame3")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); + auto frame3 = CoreComponent::cast(context->findComponentById("frame3")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); @@ -125,8 +125,8 @@ TEST_F(DynamicPropertiesTest, HeightWidthStyled) {kPropertyBounds, Rect(0, 0, 90, 90) }, })); - ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).get()); root->clearDirty(); } @@ -137,7 +137,7 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto container = std::dynamic_pointer_cast(context->findComponentById("c1")); + auto container = CoreComponent::cast(context->findComponentById("c1")); ASSERT_TRUE(container); ASSERT_EQ(kComponentTypeContainer, container->getType()); ASSERT_TRUE(CheckProperties(container, { @@ -146,7 +146,7 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) {kPropertyBounds, Rect(0, 0, 200, 550) }, })); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -155,7 +155,7 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) {kPropertyBounds, Rect(0, 0, 100, 100) }, })); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(frame2); ASSERT_EQ(kComponentTypeFrame, frame2->getType()); ASSERT_TRUE(CheckProperties(frame2, { @@ -164,7 +164,7 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) {kPropertyBounds, Rect(0, 100, 100, 100) }, })); - auto frame3 = std::dynamic_pointer_cast(context->findComponentById("frame3")); + auto frame3 = CoreComponent::cast(context->findComponentById("frame3")); ASSERT_TRUE(frame3); ASSERT_EQ(kComponentTypeFrame, frame3->getType()); ASSERT_TRUE(CheckProperties(frame3, { @@ -182,10 +182,10 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) ASSERT_TRUE(CheckDirty(root, component, frame1, frame2, frame3)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 100, 400), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 400, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 500, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 100, 400), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 400, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 500, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set width property of frame1, it will impact only frame1 frame1->setProperty(kPropertyWidth, 150); @@ -194,10 +194,10 @@ TEST_F(DynamicPropertiesTest, HeightWidthDynamic) ASSERT_TRUE(CheckDirty(root, component, frame1)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 150, 400), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 400, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 500, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 150, 400), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 400, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 500, 100, 100), frame3->getCalculated(kPropertyBounds).get()); } // Test for base component min/max height/width properties for dynamic @@ -206,12 +206,12 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto container = std::dynamic_pointer_cast(context->findComponentById("c1")); + auto container = CoreComponent::cast(context->findComponentById("c1")); ASSERT_TRUE(container); ASSERT_EQ(kComponentTypeContainer, container->getType()); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -222,7 +222,7 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) {kPropertyBounds, Rect(0, 0, 100, 100) }, })); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(frame2); ASSERT_EQ(kComponentTypeFrame, frame2->getType()); ASSERT_TRUE(CheckProperties(frame2, { @@ -233,7 +233,7 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) {kPropertyBounds, Rect(0, 100, 100, 100) }, })); - auto frame3 = std::dynamic_pointer_cast(context->findComponentById("frame3")); + auto frame3 = CoreComponent::cast(context->findComponentById("frame3")); ASSERT_TRUE(frame3); ASSERT_EQ(kComponentTypeFrame, frame3->getType()); ASSERT_TRUE(CheckProperties(frame3, { @@ -254,10 +254,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(90)), frame1->getCalculated(kPropertyMaxHeight)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 100, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 100, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set maxWidth property of frame1, it will not impact any component frame1->setProperty(kPropertyMaxWidth, 150); @@ -268,10 +268,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(150)), frame1->getCalculated(kPropertyMaxWidth)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 100, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 100, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set maxWidth property of frame1 to lower than width, it will impact only frame1 frame1->setProperty(kPropertyMaxWidth, 90); @@ -282,10 +282,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(90)), frame1->getCalculated(kPropertyMaxWidth)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 100, 100), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 190, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set minHeight property of frame2, it will impact frame3 also frame2->setProperty(kPropertyMinHeight, 125); @@ -297,10 +297,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(125)), frame2->getCalculated(kPropertyMinHeight)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 100, 125), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 100, 125), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set minWidth property of frame2, it will not impact any component frame2->setProperty(kPropertyMinWidth, 50); @@ -311,10 +311,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(50)), frame2->getCalculated(kPropertyMinWidth)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 100, 125), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 100, 125), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).get()); // Set minWidth property of frame2 to higher than width, it will impact only frame2 frame2->setProperty(kPropertyMinWidth, 125); @@ -325,10 +325,10 @@ TEST_F(DynamicPropertiesTest, MinMaxHeightWidth) ASSERT_EQ(Object(Dimension(125)), frame2->getCalculated(kPropertyMinWidth)); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 90, 125, 125), frame2->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 550), container->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 90, 90), frame1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 90, 125, 125), frame2->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 215, 100, 100), frame3->getCalculated(kPropertyBounds).get()); } // Test for base component shadow* properties for dynamic @@ -337,7 +337,7 @@ TEST_F(DynamicPropertiesTest, ShadowProperties) loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(frame2); ASSERT_EQ(kComponentTypeFrame, frame2->getType()); @@ -442,26 +442,26 @@ TEST_F(DynamicPropertiesTest, LayoutDirectionPropertyStyled) loadDocument(LAYOUTDIRCTION_SETVALUE); ASSERT_TRUE(component); // Given a container with layoutDirection as LTR - auto container = std::dynamic_pointer_cast(context->findComponentById("c1")); + auto container = CoreComponent::cast(context->findComponentById("c1")); ASSERT_TRUE(container); ASSERT_EQ(kComponentTypeContainer, container->getType()); ASSERT_EQ(Object(kLayoutDirectionLTR), container->getCalculated(kPropertyLayoutDirection)); // and the frame1 displays at top-left. - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(CheckProperties(frame1, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(0, 0, 200, 100) }, {kPropertyInnerBounds, Rect(0, 0, 200, 100) }, })); // and the frame2 displays at center. - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(CheckProperties(frame2, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(150, 100, 200, 100) }, {kPropertyInnerBounds, Rect(0, 0, 200, 100) }, })); // and the frame3 displays at top-left of frame2. - auto frame3 = std::dynamic_pointer_cast(context->findComponentById("frame3")); + auto frame3 = CoreComponent::cast(context->findComponentById("frame3")); ASSERT_TRUE(CheckProperties(frame3, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(0, 0, 100, 100) }, @@ -509,26 +509,26 @@ TEST_F(DynamicPropertiesTest, LayoutDirectionPropertyDynamic) loadDocument(LAYOUTDIRCTION_SETVALUE); ASSERT_TRUE(component); // Given a container with layoutDirection as LTR - auto container = std::dynamic_pointer_cast(context->findComponentById("c1")); + auto container = CoreComponent::cast(context->findComponentById("c1")); ASSERT_TRUE(container); ASSERT_EQ(kComponentTypeContainer, container->getType()); ASSERT_EQ(Object(kLayoutDirectionLTR), container->getCalculated(kPropertyLayoutDirection)); // and the frame1 displays at top-left. - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(CheckProperties(frame1, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(0, 0, 200, 100) }, {kPropertyInnerBounds, Rect(0, 0, 200, 100) }, })); // and the frame2 displays at center. - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(CheckProperties(frame2, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(150, 100, 200, 100) }, {kPropertyInnerBounds, Rect(0, 0, 200, 100) }, })); // and the frame3 displays at top-left of frame2. - auto frame3 = std::dynamic_pointer_cast(context->findComponentById("frame3")); + auto frame3 = CoreComponent::cast(context->findComponentById("frame3")); ASSERT_TRUE(CheckProperties(frame3, { {kPropertyLayoutDirection, Object(kLayoutDirectionLTR)}, {kPropertyBounds, Rect(0, 0, 100, 100) }, @@ -648,12 +648,12 @@ TEST_F(DynamicPropertiesTest, PaddingStyled) loadDocument(PADDING_SETVALUE); ASSERT_TRUE(component); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(CheckProperties(frame2, { {kPropertyPaddingBottom, Dimension(5) }, {kPropertyPaddingLeft, Dimension(5) }, @@ -694,7 +694,7 @@ TEST_F(DynamicPropertiesTest, PaddingDynamic) { loadDocument(PADDING_SETVALUE); ASSERT_TRUE(component); - auto container = std::dynamic_pointer_cast(context->findComponentById("c1")); + auto container = CoreComponent::cast(context->findComponentById("c1")); ASSERT_TRUE(container); ASSERT_EQ(kComponentTypeContainer, container->getType()); ASSERT_TRUE(CheckProperties(container, { @@ -706,7 +706,7 @@ TEST_F(DynamicPropertiesTest, PaddingDynamic) { {kPropertyBounds, Rect(0, 0, 500, 400) }, })); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -715,7 +715,7 @@ TEST_F(DynamicPropertiesTest, PaddingDynamic) { {kPropertyInnerBounds, Rect(10, 10, 180, 80) }, })); - auto frame2 = std::dynamic_pointer_cast(context->findComponentById("frame2")); + auto frame2 = CoreComponent::cast(context->findComponentById("frame2")); ASSERT_TRUE(frame2); ASSERT_EQ(kComponentTypeFrame, frame2->getType()); ASSERT_TRUE(CheckProperties(frame2, { @@ -798,7 +798,7 @@ TEST_F(DynamicPropertiesTest, BorderWidth) { loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -828,7 +828,7 @@ TEST_F(DynamicPropertiesTest, BorderRadius) { loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -855,7 +855,7 @@ TEST_F(DynamicPropertiesTest, BorderAnyRadius) { loadDocument(HEIGHT_WIDTH_SETVALUE); ASSERT_TRUE(component); - auto frame1 = std::dynamic_pointer_cast(context->findComponentById("frame1")); + auto frame1 = CoreComponent::cast(context->findComponentById("frame1")); ASSERT_TRUE(frame1); ASSERT_EQ(kComponentTypeFrame, frame1->getType()); ASSERT_TRUE(CheckProperties(frame1, { @@ -963,7 +963,7 @@ TEST_F(DynamicPropertiesTest, ImageProperties) { loadDocument(IMAGE_SETVALUE); ASSERT_TRUE(component); auto mediaSourceUrl = Object("https://images.amazon.com/image/foo.png"); - auto img1 = std::dynamic_pointer_cast(context->findComponentById("img1")); + auto img1 = CoreComponent::cast(context->findComponentById("img1")); ASSERT_TRUE(img1); ASSERT_EQ(kComponentTypeImage, img1->getType()); ASSERT_TRUE(CheckProperties(img1, { @@ -974,9 +974,9 @@ TEST_F(DynamicPropertiesTest, ImageProperties) { })); auto grad1 = img1->getCalculated(kPropertyOverlayGradient); - ASSERT_TRUE(grad1.isGradient()); - ASSERT_EQ(Object(Color(Color::BLUE)), grad1.getGradient().getProperty(kGradientPropertyColorRange).at(0)); - ASSERT_EQ(Object(Color(Color::RED)), grad1.getGradient().getProperty(kGradientPropertyColorRange).at(1)); + ASSERT_TRUE(grad1.is()); + ASSERT_EQ(Object(Color(Color::BLUE)), grad1.get().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_EQ(Object(Color(Color::RED)), grad1.get().getProperty(kGradientPropertyColorRange).at(1)); // Set aline property of img img1->setProperty(kPropertyAlign, "left"); @@ -1009,7 +1009,7 @@ TEST_F(DynamicPropertiesTest, ImageProperties) { ASSERT_EQ(kImageScaleBestFill, img1->getCalculated(kPropertyScale).getInteger()); // Set overlayGradient property of img - auto img2 = std::dynamic_pointer_cast(context->findComponentById("img2")); + auto img2 = CoreComponent::cast(context->findComponentById("img2")); auto grad2 = img2->getCalculated(kPropertyOverlayGradient); img1->setProperty(kPropertyOverlayGradient, Object(grad2)); @@ -1020,9 +1020,9 @@ TEST_F(DynamicPropertiesTest, ImageProperties) { root->clearDirty(); grad1 = img1->getCalculated(kPropertyOverlayGradient); - ASSERT_TRUE(grad1.isGradient()); - ASSERT_EQ(Object(Color(Color::GREEN)), grad1.getGradient().getProperty(kGradientPropertyColorRange).at(0)); - ASSERT_EQ(Object(Color(Color::GRAY)), grad1.getGradient().getProperty(kGradientPropertyColorRange).at(1)); + ASSERT_TRUE(grad1.is()); + ASSERT_EQ(Object(Color(Color::GREEN)), grad1.get().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_EQ(Object(Color(Color::GRAY)), grad1.get().getProperty(kGradientPropertyColorRange).at(1)); } static const char *VECTOR_GRAPHIC_SETVALUE = R"apl( @@ -1058,7 +1058,7 @@ TEST_F(DynamicPropertiesTest, VectorGraphicProperties) { loadDocument(VECTOR_GRAPHIC_SETVALUE); ASSERT_TRUE(component); - auto vg = std::dynamic_pointer_cast(context->findComponentById("vg")); + auto vg = CoreComponent::cast(context->findComponentById("vg")); ASSERT_TRUE(vg); ASSERT_EQ(kComponentTypeVectorGraphic, vg->getType()); ASSERT_TRUE(CheckProperties(vg, { @@ -1113,7 +1113,7 @@ TEST_F(DynamicPropertiesTest, TextProperties) { loadDocument(TEXT_SETVALUE); ASSERT_TRUE(component); - auto txt = std::dynamic_pointer_cast(context->findComponentById("txt")); + auto txt = CoreComponent::cast(context->findComponentById("txt")); ASSERT_TRUE(txt); ASSERT_EQ(kComponentTypeText, txt->getType()); ASSERT_TRUE(CheckProperties(txt, { @@ -1203,7 +1203,7 @@ TEST_F(DynamicPropertiesTest, EditTextFontProperties) { loadDocument(EDIT_TEXT_SETVALUE); ASSERT_TRUE(component); - auto txt = std::dynamic_pointer_cast(context->findComponentById("editText")); + auto txt = CoreComponent::cast(context->findComponentById("editText")); ASSERT_TRUE(txt); ASSERT_EQ(kComponentTypeEditText, txt->getType()); ASSERT_TRUE(CheckProperties(txt, { @@ -1266,7 +1266,7 @@ TEST_F(DynamicPropertiesTest, EditTextProperties) { loadDocument(EDIT_TEXT_SETVALUE); ASSERT_TRUE(component); - auto txt = std::dynamic_pointer_cast(context->findComponentById("editText")); + auto txt = CoreComponent::cast(context->findComponentById("editText")); ASSERT_TRUE(txt); ASSERT_EQ(kComponentTypeEditText, txt->getType()); ASSERT_TRUE(CheckProperties(txt, { @@ -1313,7 +1313,7 @@ TEST_F(DynamicPropertiesTest, EditTextHintProperties) { loadDocument(EDIT_TEXT_SETVALUE); ASSERT_TRUE(component); - auto txt = std::dynamic_pointer_cast(context->findComponentById("editText")); + auto txt = CoreComponent::cast(context->findComponentById("editText")); ASSERT_TRUE(txt); ASSERT_EQ(kComponentTypeEditText, txt->getType()); ASSERT_TRUE(CheckProperties(txt, { @@ -1416,19 +1416,19 @@ TEST_F(DynamicPropertiesTest, SequenceStyled) loadDocument(SEQUENCE_SETVALUE); ASSERT_TRUE(component); - auto child0 = std::dynamic_pointer_cast(component->getChildAt(0)); + auto child0 = CoreComponent::cast(component->getChildAt(0)); ASSERT_TRUE(CheckProperties(child0, { {kPropertySpacing, Dimension(10) }, // spacing will be ignored for the first child {kPropertyBounds, Rect(0, 0, 100, 20) }, })); - auto child1 = std::dynamic_pointer_cast(component->getChildAt(1)); + auto child1 = CoreComponent::cast(component->getChildAt(1)); ASSERT_TRUE(CheckProperties(child1, { {kPropertySpacing, Dimension(10) }, {kPropertyBounds, Rect(0, 30, 100, 20) }, })); - auto child2 = std::dynamic_pointer_cast(component->getChildAt(2)); + auto child2 = CoreComponent::cast(component->getChildAt(2)); ASSERT_TRUE(CheckProperties(child2, { {kPropertySpacing, Dimension(10) }, {kPropertyBounds, Rect(0, 60, 100, 20) }, @@ -1439,9 +1439,9 @@ TEST_F(DynamicPropertiesTest, SequenceStyled) ASSERT_TRUE(CheckDirty(root, component, child1, child2)); ASSERT_EQ(Object(Dimension(20)), child1 ->getCalculated(kPropertySpacing)); - ASSERT_EQ(Rect(0, 0, 100, 20), child0->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 40, 100, 20), child1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 70, 100, 20), child2->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 20), child0->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 40, 100, 20), child1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 70, 100, 20), child2->getCalculated(kPropertyBounds).get()); root->clearDirty(); } @@ -1455,19 +1455,19 @@ TEST_F(DynamicPropertiesTest, SequenceDynamic) { ASSERT_TRUE(CheckChildrenLaidOut(component, Range(0, 2), true)); - auto child0 = std::dynamic_pointer_cast(component->getChildAt(0)); + auto child0 = CoreComponent::cast(component->getChildAt(0)); ASSERT_TRUE(CheckProperties(child0, { {kPropertySpacing, Dimension(10) }, // spacing will be ignored for the first child {kPropertyBounds, Rect(0, 0, 100, 20) }, })); - auto child1 = std::dynamic_pointer_cast(component->getChildAt(1)); + auto child1 = CoreComponent::cast(component->getChildAt(1)); ASSERT_TRUE(CheckProperties(child1, { {kPropertySpacing, Dimension(10) }, {kPropertyBounds, Rect(0, 30, 100, 20) }, })); - auto child2 = std::dynamic_pointer_cast(component->getChildAt(2)); + auto child2 = CoreComponent::cast(component->getChildAt(2)); ASSERT_TRUE(CheckProperties(child2, { {kPropertySpacing, Dimension(10) }, {kPropertyBounds, Rect(0, 60, 100, 20) }, @@ -1482,7 +1482,7 @@ TEST_F(DynamicPropertiesTest, SequenceDynamic) { ASSERT_TRUE(CheckDirty(root, component, child1, child2)); root->clearDirty(); ASSERT_EQ(Object(Dimension(20)), child1 ->getCalculated(kPropertySpacing)); - ASSERT_EQ(Rect(0, 0, 100, 20), child0->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 40, 100, 20), child1->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 70, 100, 20), child2->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 20), child0->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 40, 100, 20), child1->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 70, 100, 20), child2->getCalculated(kPropertyBounds).get()); } \ No newline at end of file diff --git a/unit/component/unittest_edit_text_component.cpp b/unit/component/unittest_edit_text_component.cpp index d662f91..a09f777 100644 --- a/unit/component/unittest_edit_text_component.cpp +++ b/unit/component/unittest_edit_text_component.cpp @@ -164,7 +164,7 @@ static const char* LANG_TEXT_DEFAULT_DOC = R"({ TEST_F(EditTextComponentTest, ComponentTextLangDefaults) { loadDocument(LANG_TEXT_DEFAULT_DOC); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_EQ("en-US", et->getCalculated(kPropertyLang).asString()); et->setProperty(kPropertyLang, "ja-jp"); @@ -250,10 +250,10 @@ TEST_F(EditTextComponentTest, NonDefaults) { ASSERT_TRUE(IsEqual(kKeyboardTypeNumberPad, et->getCalculated(kPropertyKeyboardType))); ASSERT_TRUE(IsEqual(4, et->getCalculated(kPropertyMaxLength))); auto submit = et->getCalculated(kPropertyOnSubmit); - ASSERT_EQ(Object::kArrayType, submit.getType()); + ASSERT_TRUE(submit.isArray()); ASSERT_EQ(1, submit.getArray().size()); auto change = et->getCalculated(kPropertyOnTextChange); - ASSERT_EQ(Object::kArrayType, submit.getType()); + ASSERT_TRUE(submit.isArray()); ASSERT_EQ(1, change.getArray().size()); ASSERT_TRUE(IsEqual(true, et->getCalculated(kPropertySecureInput))); ASSERT_TRUE(IsEqual(true, et->getCalculated(kPropertySelectOnFocus))); @@ -281,24 +281,23 @@ static const char* VALID_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, ValidCharacterRanges) { loadDocument(VALID_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); - - ASSERT_TRUE(pEditText->isCharacterValid(L'0')); - ASSERT_TRUE(pEditText->isCharacterValid(L'9')); - ASSERT_TRUE(pEditText->isCharacterValid(L'A')); - ASSERT_TRUE(pEditText->isCharacterValid(L'Y')); - ASSERT_TRUE(pEditText->isCharacterValid(L'a')); - ASSERT_TRUE(pEditText->isCharacterValid(L'y')); - ASSERT_FALSE(pEditText->isCharacterValid(L'-')); - ASSERT_TRUE(pEditText->isCharacterValid(L'@')); - ASSERT_TRUE(pEditText->isCharacterValid(L':')); - ASSERT_TRUE(pEditText->isCharacterValid(L'?')); - ASSERT_FALSE(pEditText->isCharacterValid(L'z')); - ASSERT_FALSE(pEditText->isCharacterValid(L'Z')); - ASSERT_FALSE(pEditText->isCharacterValid(L'{')); - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); + + ASSERT_TRUE(component->isCharacterValid(L'0')); + ASSERT_TRUE(component->isCharacterValid(L'9')); + ASSERT_TRUE(component->isCharacterValid(L'A')); + ASSERT_TRUE(component->isCharacterValid(L'Y')); + ASSERT_TRUE(component->isCharacterValid(L'a')); + ASSERT_TRUE(component->isCharacterValid(L'y')); + ASSERT_FALSE(component->isCharacterValid(L'-')); + ASSERT_TRUE(component->isCharacterValid(L'@')); + ASSERT_TRUE(component->isCharacterValid(L':')); + ASSERT_TRUE(component->isCharacterValid(L'?')); + ASSERT_FALSE(component->isCharacterValid(L'z')); + ASSERT_FALSE(component->isCharacterValid(L'Z')); + ASSERT_FALSE(component->isCharacterValid(L'{')); + ASSERT_FALSE(component->isCharacterValid(L'\u2192')); } static const char* VALID_CHARACTER_RANGES_UNICODE_DOC = @@ -320,14 +319,13 @@ static const char* VALID_CHARACTER_RANGES_UNICODE_DOC = TEST_F(EditTextComponentTest, ValidCharacterRangesUnicode) { loadDocument(VALID_CHARACTER_RANGES_UNICODE_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); - - ASSERT_TRUE(pEditText->isCharacterValid(L'\u2192')); - ASSERT_TRUE(pEditText->isCharacterValid(L'\u2193')); - ASSERT_TRUE(pEditText->isCharacterValid(L'\u2195')); - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2196')); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); + + ASSERT_TRUE(component->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component->isCharacterValid(L'\u2193')); + ASSERT_TRUE(component->isCharacterValid(L'\u2195')); + ASSERT_FALSE(component->isCharacterValid(L'\u2196')); } static const char* EMPTY_CHARACTER_RANGES_DOC = R"({ @@ -344,15 +342,14 @@ static const char* EMPTY_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, EmptyCharacterRanges) { loadDocument(EMPTY_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); // everything should be valid - ASSERT_TRUE(pEditText->isCharacterValid(L'\u2192')); - ASSERT_TRUE(pEditText->isCharacterValid(L'-')); - ASSERT_TRUE(pEditText->isCharacterValid(L'A')); - ASSERT_TRUE(pEditText->isCharacterValid(L'0')); + ASSERT_TRUE(component->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component->isCharacterValid(L'-')); + ASSERT_TRUE(component->isCharacterValid(L'A')); + ASSERT_TRUE(component->isCharacterValid(L'0')); session->clear(); } @@ -374,16 +371,15 @@ static const char* INVALID_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, InvalidCharacterRanges) { loadDocument(INVALID_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); // everything should be valid - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2192')); - ASSERT_FALSE(pEditText->isCharacterValid(L'-')); - ASSERT_TRUE(pEditText->isCharacterValid(L'Q')); - ASSERT_FALSE(pEditText->isCharacterValid(L'A')); - ASSERT_FALSE(pEditText->isCharacterValid(L'0')); + ASSERT_FALSE(component->isCharacterValid(L'\u2192')); + ASSERT_FALSE(component->isCharacterValid(L'-')); + ASSERT_TRUE(component->isCharacterValid(L'Q')); + ASSERT_FALSE(component->isCharacterValid(L'A')); + ASSERT_FALSE(component->isCharacterValid(L'0')); session->clear(); } @@ -403,19 +399,18 @@ static const char* INVALID_DASH_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, InvalidDashCharacterRanges) { loadDocument(INVALID_DASH_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); // everything should be valid - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2192')); - ASSERT_TRUE(pEditText->isCharacterValid(L'-')); - ASSERT_TRUE(pEditText->isCharacterValid(L'A')); - ASSERT_FALSE(pEditText->isCharacterValid(L'Z')); - ASSERT_TRUE(pEditText->isCharacterValid(L'0')); - ASSERT_TRUE(pEditText->isCharacterValid(L'?')); // U+003F - ASSERT_TRUE(pEditText->isCharacterValid(L'@')); // U+0040 - ASSERT_FALSE(pEditText->isCharacterValid(L'}')); // U+007D + ASSERT_FALSE(component->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component->isCharacterValid(L'-')); + ASSERT_TRUE(component->isCharacterValid(L'A')); + ASSERT_FALSE(component->isCharacterValid(L'Z')); + ASSERT_TRUE(component->isCharacterValid(L'0')); + ASSERT_TRUE(component->isCharacterValid(L'?')); // U+003F + ASSERT_TRUE(component->isCharacterValid(L'@')); // U+0040 + ASSERT_FALSE(component->isCharacterValid(L'}')); // U+007D session->clear(); } @@ -434,19 +429,18 @@ static const char* AMOUNT_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, AmountCharacterRanges) { loadDocument(AMOUNT_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); - - ASSERT_TRUE(pEditText->isCharacterValid(L'0')); - ASSERT_TRUE(pEditText->isCharacterValid(L'5')); - ASSERT_TRUE(pEditText->isCharacterValid(L'7')); - ASSERT_TRUE(pEditText->isCharacterValid(L'9')); - ASSERT_TRUE(pEditText->isCharacterValid(L'.')); - ASSERT_FALSE(pEditText->isCharacterValid(L'A')); - ASSERT_FALSE(pEditText->isCharacterValid(L'@')); - ASSERT_FALSE(pEditText->isCharacterValid(L'-')); - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); + + ASSERT_TRUE(component->isCharacterValid(L'0')); + ASSERT_TRUE(component->isCharacterValid(L'5')); + ASSERT_TRUE(component->isCharacterValid(L'7')); + ASSERT_TRUE(component->isCharacterValid(L'9')); + ASSERT_TRUE(component->isCharacterValid(L'.')); + ASSERT_FALSE(component->isCharacterValid(L'A')); + ASSERT_FALSE(component->isCharacterValid(L'@')); + ASSERT_FALSE(component->isCharacterValid(L'-')); + ASSERT_FALSE(component->isCharacterValid(L'\u2192')); } static const char* EMAIL_CHARACTER_RANGES_DOC = R"({ @@ -463,27 +457,26 @@ static const char* EMAIL_CHARACTER_RANGES_DOC = R"({ TEST_F(EditTextComponentTest, EmailCharacterRanges) { loadDocument(EMAIL_CHARACTER_RANGES_DOC); - EditTextComponent* pEditText = dynamic_cast(root->topComponent().get()); - ASSERT_TRUE(pEditText); - ASSERT_EQ(kComponentTypeEditText, pEditText->getType()); - - ASSERT_TRUE(pEditText->isCharacterValid(L'-')); - ASSERT_TRUE(pEditText->isCharacterValid(L'+')); - ASSERT_TRUE(pEditText->isCharacterValid(L'a')); - ASSERT_TRUE(pEditText->isCharacterValid(L'p')); - ASSERT_TRUE(pEditText->isCharacterValid(L'z')); - ASSERT_TRUE(pEditText->isCharacterValid(L'A')); - ASSERT_TRUE(pEditText->isCharacterValid(L'P')); - ASSERT_TRUE(pEditText->isCharacterValid(L'Z')); - ASSERT_TRUE(pEditText->isCharacterValid(L'0')); - ASSERT_TRUE(pEditText->isCharacterValid(L'5')); - ASSERT_TRUE(pEditText->isCharacterValid(L'7')); - ASSERT_TRUE(pEditText->isCharacterValid(L'9')); - ASSERT_TRUE(pEditText->isCharacterValid(L'_')); - ASSERT_TRUE(pEditText->isCharacterValid(L'@')); - ASSERT_TRUE(pEditText->isCharacterValid(L'.')); - ASSERT_FALSE(pEditText->isCharacterValid(L':')); - ASSERT_FALSE(pEditText->isCharacterValid(L'\u2192')); + ASSERT_TRUE(component); + ASSERT_EQ(kComponentTypeEditText, component->getType()); + + ASSERT_TRUE(component->isCharacterValid(L'-')); + ASSERT_TRUE(component->isCharacterValid(L'+')); + ASSERT_TRUE(component->isCharacterValid(L'a')); + ASSERT_TRUE(component->isCharacterValid(L'p')); + ASSERT_TRUE(component->isCharacterValid(L'z')); + ASSERT_TRUE(component->isCharacterValid(L'A')); + ASSERT_TRUE(component->isCharacterValid(L'P')); + ASSERT_TRUE(component->isCharacterValid(L'Z')); + ASSERT_TRUE(component->isCharacterValid(L'0')); + ASSERT_TRUE(component->isCharacterValid(L'5')); + ASSERT_TRUE(component->isCharacterValid(L'7')); + ASSERT_TRUE(component->isCharacterValid(L'9')); + ASSERT_TRUE(component->isCharacterValid(L'_')); + ASSERT_TRUE(component->isCharacterValid(L'@')); + ASSERT_TRUE(component->isCharacterValid(L'.')); + ASSERT_FALSE(component->isCharacterValid(L':')); + ASSERT_FALSE(component->isCharacterValid(L'\u2192')); } static const char* INVALID_DIMENSIONS_DOC = R"({ @@ -635,8 +628,8 @@ TEST_F(EditTextComponentTest, Handlers) { CheckDirty(result, kPropertyText); ASSERT_TRUE(IsEqual(Color(Color::BLUE), et->getCalculated(kPropertyColor))); auto resultTxt = result->getCalculated(kPropertyText); - ASSERT_TRUE(resultTxt.isStyledText()); - ASSERT_TRUE(IsEqual("Submit:hello", resultTxt.getStyledText().getRawText())); + ASSERT_TRUE(resultTxt.is()); + ASSERT_TRUE(IsEqual("Submit:hello", resultTxt.get().getRawText())); root->clearDirty(); et->update(kUpdateTextChange, "goodbye"); @@ -648,8 +641,8 @@ TEST_F(EditTextComponentTest, Handlers) { ASSERT_TRUE(IsEqual("goodbye", et->getCalculated(kPropertyText))); ASSERT_TRUE(IsEqual(Color(Color::RED), et->getCalculated(kPropertyColor))); resultTxt = result->getCalculated(kPropertyText); - ASSERT_TRUE(resultTxt.isStyledText()); - ASSERT_TRUE(IsEqual("TextChange:goodbye", resultTxt.getStyledText().getRawText())); + ASSERT_TRUE(resultTxt.is()); + ASSERT_TRUE(IsEqual("TextChange:goodbye", resultTxt.get().getRawText())); root->clearDirty(); } @@ -786,9 +779,9 @@ TEST_F(EditTextComponentTest, EditTextMeasurement) { // Check the layout auto top = root->topComponent().get(); - ASSERT_EQ(Rect(0, 0, 400, 400), top->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 400, 400), top->getCalculated(kPropertyBounds).get()); auto editText = top->getChildAt(0); - ASSERT_EQ(Rect(2, 2, 60, 120), editText->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(2, 2, 60, 120), editText->getCalculated(kPropertyBounds).get()); } /** diff --git a/unit/component/unittest_flexbox.cpp b/unit/component/unittest_flexbox.cpp index 17acc19..58a2703 100644 --- a/unit/component/unittest_flexbox.cpp +++ b/unit/component/unittest_flexbox.cpp @@ -93,9 +93,9 @@ TEST_F(FlexboxTest, SimpleAuto) loadDocument(SIMPLE_AUTO); auto bounds = component->getCalculated(kPropertyBounds); - ASSERT_TRUE(bounds.isRect()); + ASSERT_TRUE(bounds.is()); - ASSERT_EQ(Rect(0,0,1024,800), bounds.getRect()); + ASSERT_EQ(Rect(0,0,1024,800), bounds.get()); } static const char * SIMPLE_FIXED = @@ -116,12 +116,12 @@ TEST_F(FlexboxTest, SimpleFixed) loadDocument(SIMPLE_FIXED); auto bounds = component->getCalculated(kPropertyBounds); - ASSERT_TRUE(bounds.isRect()); - ASSERT_EQ(Rect(0, 0, 200, 300), bounds.getRect()); + ASSERT_TRUE(bounds.is()); + ASSERT_EQ(Rect(0, 0, 200, 300), bounds.get()); auto inner = component->getCalculated(kPropertyInnerBounds); - ASSERT_TRUE(inner.isRect()); - ASSERT_EQ(Rect(0, 0, 200, 300), inner.getRect()); + ASSERT_TRUE(inner.is()); + ASSERT_EQ(Rect(0, 0, 200, 300), inner.get()); } static const char *TOO_LARGE = @@ -141,7 +141,7 @@ static const char *TOO_LARGE = TEST_F(FlexboxTest, TooLarge) { loadDocument(TOO_LARGE); - ASSERT_EQ(Rect(0,0,2000,2000), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,2000,2000), component->getCalculated(kPropertyBounds).get()); } static const char *THREE_CHILDREN_TALL = @@ -176,21 +176,21 @@ static const char *THREE_CHILDREN_TALL = TEST_F(FlexboxTest, ThreeChildrenTall) { loadDocument(THREE_CHILDREN_TALL); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(10, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(10, 230, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 230, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(10, 430, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 430, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); } static const char *THREE_CHILDREN_WIDE = @@ -226,21 +226,21 @@ static const char *THREE_CHILDREN_WIDE = TEST_F(FlexboxTest, ThreeChildrenWide) { loadDocument(THREE_CHILDREN_WIDE); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(10, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(110, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(110, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(210, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(210, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); } static const char *OVERLY_TALL_CHILDREN = @@ -267,17 +267,17 @@ static const char *OVERLY_TALL_CHILDREN = TEST_F(FlexboxTest, OverlyTallChildren) { loadDocument(OVERLY_TALL_CHILDREN); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 400), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 400, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 400, 100, 400), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(0, 800, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 800, 100, 400), child->getCalculated(kPropertyBounds).get()); } static const char *SHRINKING_CHILDREN = @@ -306,20 +306,20 @@ static const char *SHRINKING_CHILDREN = TEST_F(FlexboxTest, ShrinkingChildren) { loadDocument(SHRINKING_CHILDREN); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100, 320), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 320), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 320, 100, 240), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 320, 100, 240), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(0, 560, 100, 160), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 560, 100, 160), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); - ASSERT_EQ(Rect(0, 720, 100, 80), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 720, 100, 80), child->getCalculated(kPropertyBounds).get()); } static const char *GROWING_CHILDREN = @@ -348,20 +348,20 @@ static const char *GROWING_CHILDREN = TEST_F(FlexboxTest, GrowingChildren) { loadDocument(GROWING_CHILDREN); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100, 140), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 140), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 140, 100, 180), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 140, 100, 180), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(0, 320, 100, 220), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 320, 100, 220), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); - ASSERT_EQ(Rect(0, 540, 100, 260), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 540, 100, 260), child->getCalculated(kPropertyBounds).get()); } static const char *ABSOLUTE_POSITION = @@ -386,11 +386,11 @@ static const char *ABSOLUTE_POSITION = TEST_F(FlexboxTest, AbsolutePosition) { loadDocument(ABSOLUTE_POSITION); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(1, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(5, 10, 999, 775), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(5, 10, 999, 775), child->getCalculated(kPropertyBounds).get()); } const static char * BORDER_TEST = @@ -418,16 +418,16 @@ const static char * BORDER_TEST = TEST_F(FlexboxTest, BorderTest) { loadDocument(BORDER_TEST); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).get()); auto frame = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 1024, 800), frame->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), frame->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(Dimension(10)), frame->getCalculated(kPropertyBorderWidth)); - ASSERT_EQ(Rect(10, 10, 1004, 780), frame->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 10, 1004, 780), frame->getCalculated(kPropertyInnerBounds).get()); // The child of the frame respects the border auto child = frame->getChildAt(0); - ASSERT_EQ(Rect(10, 10, 1004, 780), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(10, 10, 1004, 780), child->getCalculated(kPropertyBounds).get()); } static const char *BORDER_TEST_WITH_PADDING = @@ -459,17 +459,17 @@ static const char *BORDER_TEST_WITH_PADDING = TEST_F(FlexboxTest, BorderTestWithPadding) { loadDocument(BORDER_TEST_WITH_PADDING); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).get()); auto frame = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 1024, 800), frame->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), frame->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(Dimension(10)), frame->getCalculated(kPropertyBorderWidth)); ASSERT_TRUE(IsEqual(Rect(30, 40, 944, 700), frame->getCalculated(kPropertyInnerBounds))); - ASSERT_EQ(Rect(30, 40, 944, 700), frame->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(30, 40, 944, 700), frame->getCalculated(kPropertyInnerBounds).get()); // The child of the frame respects the border auto child = frame->getChildAt(0); - ASSERT_EQ(Rect(30, 40, 944, 700), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(30, 40, 944, 700), child->getCalculated(kPropertyBounds).get()); } static const char *JUSTIFY_END = @@ -496,14 +496,14 @@ static const char *JUSTIFY_END = TEST_F(FlexboxTest, JustifyEnd) { loadDocument(JUSTIFY_END); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 600, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 600, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 700, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 700, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *JUSTIFY_CENTER = @@ -530,14 +530,14 @@ static const char *JUSTIFY_CENTER = TEST_F(FlexboxTest, JustifyCenter) { loadDocument(JUSTIFY_CENTER); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 400, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 400, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *JUSTIFY_SPACE_BETWEEN = @@ -564,14 +564,14 @@ static const char *JUSTIFY_SPACE_BETWEEN = TEST_F(FlexboxTest, JustifySpaceBetween) { loadDocument(JUSTIFY_SPACE_BETWEEN); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 700, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 700, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *JUSTIFY_SPACE_AROUND = @@ -598,14 +598,14 @@ static const char *JUSTIFY_SPACE_AROUND = TEST_F(FlexboxTest, JustifySpaceAround) { loadDocument(JUSTIFY_SPACE_AROUND); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 150, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 150, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 550, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 550, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *ALIGN_ITEMS_START = @@ -635,20 +635,20 @@ static const char *ALIGN_ITEMS_START = TEST_F(FlexboxTest, AlignItemsStart) { loadDocument(ALIGN_ITEMS_START); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is "auto", which will be left-aligned - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Second child is "start - ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // Third child is "end" - ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // Fourth child is "center" - ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *ALIGN_ITEMS_CENTER = @@ -678,20 +678,20 @@ static const char *ALIGN_ITEMS_CENTER = TEST_F(FlexboxTest, AlignItemsCenter) { loadDocument(ALIGN_ITEMS_CENTER); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is "auto", which will be centered - ASSERT_EQ(Rect(462, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Second child is "start - ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // Third child is "end" - ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // Fourth child is "center" - ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *ALIGN_ITEMS_END = @@ -721,20 +721,20 @@ static const char *ALIGN_ITEMS_END = TEST_F(FlexboxTest, AlignItemsEnd) { loadDocument(ALIGN_ITEMS_END); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is "auto", which will be right-aligned - ASSERT_EQ(Rect(924, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Second child is "start - ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // Third child is "end" - ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 200, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // Fourth child is "center" - ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *SPACING_VERTICAL = @@ -762,17 +762,17 @@ static const char *SPACING_VERTICAL = TEST_F(FlexboxTest, SpacingVertical) { loadDocument(SPACING_VERTICAL); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); // No spacing for first child - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Add spacing for second child of 50 - ASSERT_EQ(Rect(0, 150, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 150, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // The last child gets another 100 - ASSERT_EQ(Rect(0, 350, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 350, 100, 100), child->getCalculated(kPropertyBounds).get()); } @@ -802,17 +802,17 @@ static const char *SPACING_HORIZONTAL = TEST_F(FlexboxTest, SpacingHorizontal) { loadDocument(SPACING_HORIZONTAL); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); // No spacing for first child - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Add spacing for second child of 50 - ASSERT_EQ(Rect(150, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(150, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // The last child gets another 100 - ASSERT_EQ(Rect(350, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(350, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); } @@ -844,30 +844,30 @@ TEST_F(FlexboxTest, TextCheck) { config->measure(std::make_shared()); loadDocument(TEXT_MEASUREMENT); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); // Test for TextComponent auto childTextComponent = component->getChildAt(0); // No spacing for first child - ASSERT_EQ(Rect(0, 0, 150, 20), childTextComponent->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 150, 20), childTextComponent->getCalculated(kPropertyBounds).get()); clearDirty(); // Now let's change the text - this should trigger a re-layout - std::dynamic_pointer_cast(childTextComponent)->setProperty(kPropertyText, "Short"); + CoreComponent::cast(childTextComponent)->setProperty(kPropertyText, "Short"); ASSERT_TRUE(root->isDirty()); root->clearDirty(); - ASSERT_EQ(Rect(0, 0, 50, 10), childTextComponent->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 50, 10), childTextComponent->getCalculated(kPropertyBounds).get()); //Test for EditTextComponent auto childEditTextComponent = component->getChildAt(1); // No spacing for first child - ASSERT_EQ(Rect(0, 10, 400, 10), childEditTextComponent->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 10, 400, 10), childEditTextComponent->getCalculated(kPropertyBounds).get()); clearDirty(); // Now let's change the text - this should not trigger a re-layout for edit text - std::dynamic_pointer_cast(childEditTextComponent)->setProperty(kPropertyText, "Short"); + CoreComponent::cast(childEditTextComponent)->setProperty(kPropertyText, "Short"); ASSERT_TRUE(root->isDirty()); root->clearDirty(); - ASSERT_EQ(Rect(0, 10, 400, 10), childEditTextComponent->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 10, 400, 10), childEditTextComponent->getCalculated(kPropertyBounds).get()); } static const char *FONT_STYLE_CHECK = @@ -909,11 +909,11 @@ TEST_F(FlexboxTest, FontStyleCheck) config->measure(std::make_shared()); loadDocument(FONT_STYLE_CHECK); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(1, component->getChildCount()); auto child = component->getChildAt(0); // No spacing for first child - ASSERT_EQ(Rect(0, 0, 150, 20), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 150, 20), child->getCalculated(kPropertyBounds).get()); clearDirty(); // Now toggle the style - this will force a re-layout @@ -921,11 +921,11 @@ TEST_F(FlexboxTest, FontStyleCheck) clearDirty(); // The bold font is twice as wide as the normal font. - ASSERT_EQ(Rect(0, 0, 300, 20), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 300, 20), child->getCalculated(kPropertyBounds).get()); root->handlePointerEvent(PointerEvent(kPointerUp, Point(1,1))); clearDirty(); - ASSERT_EQ(Rect(0, 0, 150, 20), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 150, 20), child->getCalculated(kPropertyBounds).get()); } const static char *BASELINE_TEST = @@ -957,17 +957,17 @@ TEST_F(FlexboxTest, BaselineTest) config->measure(std::make_shared()); loadDocument(BASELINE_TEST); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); // First child is one line - ASSERT_EQ(Rect(0, 20, 110, 10), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 20, 110, 10), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // First child is one line - ASSERT_EQ(Rect(110, 10, 110, 20), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(110, 10, 110, 20), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // First child is one line - ASSERT_EQ(Rect(220, 0, 110, 30), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(220, 0, 110, 30), child->getCalculated(kPropertyBounds).get()); } const static char *BASELINE_EDITTEXT_TEST = R"( @@ -1001,20 +1001,20 @@ TEST_F(FlexboxTest, BaselineEditTextTest) config->measure(std::make_shared()); loadDocument(BASELINE_EDITTEXT_TEST); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is one line - ASSERT_EQ(Rect(0, 0, 50, 10), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 50, 10), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // First child is one line - ASSERT_EQ(Rect(50, 0, 190, 10), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(50, 0, 190, 10), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // First child is one line - ASSERT_EQ(Rect(240, 0, 400, 10), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(240, 0, 400, 10), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // First child is one line - ASSERT_EQ(Rect(640, 0, 560, 10), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(640, 0, 560, 10), child->getCalculated(kPropertyBounds).get()); } const static char *SCROLL_VIEW_TEST = @@ -1038,12 +1038,12 @@ const static char *SCROLL_VIEW_TEST = TEST_F(FlexboxTest, ScrollViewTest) { loadDocument(SCROLL_VIEW_TEST); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(1, component->getChildCount()); ASSERT_EQ(kComponentTypeScrollView, component->getType()); auto frame = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 1024, 4000), frame->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 4000), frame->getCalculated(kPropertyBounds).get()); } const static char *SEQUENCE_TEST = @@ -1075,13 +1075,13 @@ TEST_F(FlexboxTest, SequenceTest) { loadDocument(SEQUENCE_TEST); advanceTime(10); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); ASSERT_EQ(kComponentTypeSequence, component->getType()); for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(0, 400*i, 1024, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 400*i, 1024, 400), child->getCalculated(kPropertyBounds).get()); } } @@ -1114,14 +1114,14 @@ TEST_F(FlexboxTest, HorizontalSequenceTest) { loadDocument(HORIZONTAL_SEQUENCE_TEST); advanceTime(10); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(4, component->getChildCount()); ASSERT_EQ(kComponentTypeSequence, component->getType()); ASSERT_EQ(kScrollDirectionHorizontal, component->getCalculated(kPropertyScrollDirection).asInt()); for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(400*i, 0, 400, 800), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(400*i, 0, 400, 800), child->getCalculated(kPropertyBounds).get()); } } @@ -1166,7 +1166,7 @@ TEST_F(FlexboxTest, SequenceWithSpacingTest) config->set(RootProperty::kSequenceChildCache, 2); loadDocument(SPACED_SEQUENCE); advanceTime(10); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(8, component->getChildCount()); ASSERT_EQ(kComponentTypeSequence, component->getType()); @@ -1174,7 +1174,7 @@ TEST_F(FlexboxTest, SequenceWithSpacingTest) for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); ASSERT_EQ(std::to_string(i+1), child->getChildAt(0)->getCalculated(kPropertyText).asString()); - ASSERT_EQ(Rect(0, y, 1024, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, y, 1024, 200), child->getCalculated(kPropertyBounds).get()); y += 200 + (i + 2) * 10; } } @@ -1184,7 +1184,7 @@ TEST_F(FlexboxTest, SequenceWithSpacingTestEnsureJump) config->set(RootProperty::kSequenceChildCache, 2); loadDocument(SPACED_SEQUENCE); advanceTime(10); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(8, component->getChildCount()); ASSERT_EQ(kComponentTypeSequence, component->getType()); @@ -1192,7 +1192,7 @@ TEST_F(FlexboxTest, SequenceWithSpacingTestEnsureJump) for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); ASSERT_EQ(std::to_string(i+1), child->getChildAt(0)->getCalculated(kPropertyText).asString()); - ASSERT_EQ(Rect(0, y, 1024, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, y, 1024, 200), child->getCalculated(kPropertyBounds).get()); y += 200 + (i + 2) * 10; } } @@ -1223,14 +1223,14 @@ const static char *PAGER_TEST = TEST_F(FlexboxTest, PagerTest) { loadDocument(PAGER_TEST); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(3, component->getChildCount()); ASSERT_EQ(kComponentTypePager, component->getType()); advanceTime(10); for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(0, 0, 1024, 800), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), child->getCalculated(kPropertyBounds).get()); } } @@ -1278,46 +1278,46 @@ TEST_F(FlexboxTest, AlignmentTest) { metrics.dpi(320.0); loadDocument(ALIGNMENT_TEST); - ASSERT_EQ(Rect(0,0,512,400), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,512,400), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(5, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100.5, 100.5), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100.5, 100.5), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 100.5, 100.5, 100.5), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 100.5, 100.5, 100.5), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(0, 201, 50, 50), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 201, 50, 50), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); - ASSERT_EQ(Rect(0, 251, 128, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 251, 128, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(4); - ASSERT_EQ(Rect(0, 351, 128, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 351, 128, 100), child->getCalculated(kPropertyBounds).get()); } TEST_F(FlexboxTest, AlignmentTestReverse) { metrics.dpi(80.0); loadDocument(ALIGNMENT_TEST); - ASSERT_EQ(Rect(0,0,2048,1600), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,2048,1600), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(5, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(0, 200, 200, 200), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200, 200, 200), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); - ASSERT_EQ(Rect(0, 400, 512, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 400, 512, 400), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(4); - ASSERT_EQ(Rect(0, 800, 512, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 800, 512, 400), child->getCalculated(kPropertyBounds).get()); } /* diff --git a/unit/component/unittest_frame_component.cpp b/unit/component/unittest_frame_component.cpp index 27d1ea9..64cd36a 100644 --- a/unit/component/unittest_frame_component.cpp +++ b/unit/component/unittest_frame_component.cpp @@ -52,7 +52,7 @@ TEST_F(FrameComponentTest, ComponentDefaults) { ASSERT_TRUE(IsEqual(Object::NULL_OBJECT(), frame->getCalculated(kPropertyBorderTopLeftRadius))); ASSERT_TRUE(IsEqual(Object::NULL_OBJECT(), frame->getCalculated(kPropertyBorderTopRightRadius))); // kpropertyBorderRadii is calculated from all kpropertyBorderXXXRadius values - ASSERT_TRUE(IsEqual(Object::EMPTY_RADII(), frame->getCalculated(kPropertyBorderRadii))); + ASSERT_TRUE(IsEqual(Radii(), frame->getCalculated(kPropertyBorderRadii))); ASSERT_TRUE(IsEqual(Dimension(0), frame->getCalculated(kPropertyBorderWidth))); ASSERT_TRUE(frame->getCalculated(kPropertyBorderStrokeWidth).isNull()); @@ -291,7 +291,7 @@ TEST_F(FrameComponentTest, SimpleFrame) // Frame properties ASSERT_EQ(0x00000000, component->getCalculated(kPropertyBackgroundColor).getColor()); - ASSERT_EQ(Object::EMPTY_RADII(), component->getCalculated(kPropertyBorderRadii)); + ASSERT_EQ(Object(Radii()), component->getCalculated(kPropertyBorderRadii)); ASSERT_EQ(0x00000000, component->getCalculated(kPropertyBorderColor).getColor()); ASSERT_EQ(Object(Dimension(0)), component->getCalculated(kPropertyBorderRadius)); ASSERT_EQ(Object(Dimension(0)), component->getCalculated(kPropertyBorderWidth)); @@ -326,7 +326,7 @@ TEST_F(FrameComponentTest, Borders) ASSERT_EQ(Object::NULL_OBJECT(), map.get(kPropertyBorderBottomRightRadius)); // The output values match the border radius - ASSERT_EQ(Radii(10), map.get(kPropertyBorderRadii).getRadii()); + ASSERT_EQ(Radii(10), map.get(kPropertyBorderRadii).get()); } static const char *BORDER_TEST_2 = R"({ @@ -565,12 +565,12 @@ TEST_F(FrameComponentTest, StyleFrameInnerBounds) auto width = metrics.getWidth(); auto height = metrics.getHeight(); - ASSERT_EQ(Rect(0, 0, width, height), component->getCalculated(kPropertyInnerBounds).getRect()); - ASSERT_EQ(Rect(100, 100, width - 200, height - 200), image->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0, 0, width, height), component->getCalculated(kPropertyInnerBounds).get()); + ASSERT_EQ(Rect(100, 100, width - 200, height - 200), image->getCalculated(kPropertyInnerBounds).get()); component->setState(kStatePressed, true); root->clearPending(); - ASSERT_EQ(Rect(100, 100, width - 200, height - 200), component->getCalculated(kPropertyInnerBounds).getRect()); - ASSERT_EQ(Rect(100, 100, width - 400, height - 400), image->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(100, 100, width - 200, height - 200), component->getCalculated(kPropertyInnerBounds).get()); + ASSERT_EQ(Rect(100, 100, width - 400, height - 400), image->getCalculated(kPropertyInnerBounds).get()); } diff --git a/unit/component/unittest_grid_sequence_component.cpp b/unit/component/unittest_grid_sequence_component.cpp index 04cd92c..4872246 100644 --- a/unit/component/unittest_grid_sequence_component.cpp +++ b/unit/component/unittest_grid_sequence_component.cpp @@ -43,7 +43,7 @@ class GridSequenceComponentTest : public DocumentWrapper { ::testing::AssertionResult validateBounds(const ComponentPtr& component, const Rect& rect) { - auto componentBounds = component->getCalculated(kPropertyBounds).getRect(); + auto componentBounds = component->getCalculated(kPropertyBounds).get(); if (componentBounds != rect) { return ::testing::AssertionFailure() @@ -78,8 +78,8 @@ validateCellBounds(const CoreComponentPtr& grid, auto scrollDirection = grid->getCalculated(kPropertyScrollDirection); bool isLTR = grid->getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; auto startPoint = isLTR - ? grid->getCalculated(kPropertyInnerBounds).getRect().getTopLeft() - : grid->getCalculated(kPropertyInnerBounds).getRect().getTopRight(); + ? grid->getCalculated(kPropertyInnerBounds).get().getTopLeft() + : grid->getCalculated(kPropertyInnerBounds).get().getTopRight(); std::vector labels; @@ -1378,7 +1378,7 @@ TEST_F(GridSequenceComponentTest, CheckEmptyEnsuredChildren) // Now that mEnsuredChildren is empty we call MultiChildScrollableComponent::processLayoutChanges root->clearPending(); - auto grid = std::dynamic_pointer_cast(root->findComponentById("grid")); + auto grid = GridSequenceComponent::cast(root->findComponentById("grid")); ASSERT_EQ(grid->getDisplayedChildCount(), 9); } @@ -1670,7 +1670,7 @@ TEST_F(GridSequenceComponentTest, AutoSequence) { ASSERT_EQ(kComponentTypeGridSequence, grid->getType()); ASSERT_EQ(4, grid->getCalculated(kPropertyItemsPerCourse).asInt()); - auto bounds = grid->getCalculated(kPropertyBounds).getRect(); + auto bounds = grid->getCalculated(kPropertyBounds).get(); ASSERT_EQ(Rect(0, 0, 400, 100), bounds); ASSERT_TRUE(validateCellBounds( grid, @@ -1707,7 +1707,7 @@ TEST_F(GridSequenceComponentTest, ChildHeightWidthVertical) { loadDocument(VERTICAL_GRID_SETVALUE); ASSERT_TRUE(component); - auto gridSeq = std::dynamic_pointer_cast(component); + auto gridSeq = CoreComponent::cast(component); ASSERT_EQ(kComponentTypeGridSequence, gridSeq->getType()); ASSERT_EQ(kScrollDirectionVertical, gridSeq->getCalculated(kPropertyScrollDirection).asInt()); @@ -1851,7 +1851,7 @@ TEST_F(GridSequenceComponentTest, HeightWidthVertical) { loadDocument(VERTICAL_GRID_SETVALUE); ASSERT_TRUE(component); - auto gridSeq = std::dynamic_pointer_cast(component); + auto gridSeq = CoreComponent::cast(component); ASSERT_EQ(kComponentTypeGridSequence, gridSeq->getType()); ASSERT_EQ(kScrollDirectionVertical, gridSeq->getCalculated(kPropertyScrollDirection).asInt()); @@ -1954,7 +1954,7 @@ TEST_F(GridSequenceComponentTest, ChildHeightWidthHorizontal) { loadDocument(HORIZONTAL_GRID_SETVALUE); ASSERT_TRUE(component); - auto gridSeq = std::dynamic_pointer_cast(component); + auto gridSeq = CoreComponent::cast(component); ASSERT_EQ(kComponentTypeGridSequence, gridSeq->getType()); ASSERT_EQ(kScrollDirectionHorizontal, gridSeq->getCalculated(kPropertyScrollDirection).asInt()); @@ -2099,7 +2099,7 @@ TEST_F(GridSequenceComponentTest, HeightWidthHorizontal) { loadDocument(HORIZONTAL_GRID_SETVALUE); ASSERT_TRUE(component); - auto gridSeq = std::dynamic_pointer_cast(component); + auto gridSeq = CoreComponent::cast(component); ASSERT_EQ(kComponentTypeGridSequence, gridSeq->getType()); ASSERT_EQ(kScrollDirectionHorizontal, gridSeq->getCalculated(kPropertyScrollDirection).asInt()); @@ -2236,7 +2236,7 @@ TEST_F(GridSequenceComponentTest, TestSnappingWithMultipleComponentsPerLine) { // Give time for the component to snap advanceTime(1000); - auto grid = std::dynamic_pointer_cast(context->findComponentById("gridSequence")); + auto grid = CoreComponent::cast(context->findComponentById("gridSequence")); // Verify we snap to the top of the component ASSERT_EQ(0, grid->getCalculated(kPropertyScrollPosition).asNumber()); diff --git a/unit/component/unittest_layout_direction.cpp b/unit/component/unittest_layout_direction.cpp index 4b03129..2bc2324 100644 --- a/unit/component/unittest_layout_direction.cpp +++ b/unit/component/unittest_layout_direction.cpp @@ -160,22 +160,22 @@ static const char *RTL_THREE_CHILDREN_WIDE = R"( TEST_F(LayoutDirectionText, RTLThreeChildrenWide) { loadDocument(RTL_THREE_CHILDREN_WIDE); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); - ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(10, 30, 994, 730), component->getCalculated(kPropertyInnerBounds).get()); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(904, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(904, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(804, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(804, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(704, 30, 100, 200), child->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(704, 30, 100, 200), child->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(1, 3, 97, 193), child->getCalculated(kPropertyInnerBounds).get()); } /* @@ -210,18 +210,18 @@ static const char *RTL_OVERLY_TALL_CHILDREN = R"( TEST_F(LayoutDirectionText, RTLOverlyTallChildren) { loadDocument(RTL_OVERLY_TALL_CHILDREN); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); ASSERT_EQ(3, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(924, 0, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 0, 100, 400), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(924, 400, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 400, 100, 400), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); - ASSERT_EQ(Rect(924, 800, 100, 400), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 800, 100, 400), child->getCalculated(kPropertyBounds).get()); } /* @@ -637,12 +637,12 @@ TEST_F(LayoutDirectionText, RTLAbsolutePosition) // If top is set, bottom is ignored. // Also in RTL, if right is set, left is ignored. loadDocument(RTL_ABSOLUTE_POSITION); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); ASSERT_EQ(1, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(904, 10, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(904, 10, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *RTL_RELATIVE_POSITION = R"( @@ -673,12 +673,12 @@ TEST_F(LayoutDirectionText, RTLRelativePosition) // If top is set, bottom is ignored. // Also in RTL, if right is set, left is ignored. loadDocument(RTL_RELATIVE_POSITION); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); ASSERT_EQ(1, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(904, 10, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(904, 10, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *RTL_ALIGN_ITEMS_START = R"( @@ -710,21 +710,21 @@ static const char *RTL_ALIGN_ITEMS_START = R"( TEST_F(LayoutDirectionText, RTLAlignItemsStart) { loadDocument(RTL_ALIGN_ITEMS_START); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is "auto", which will be right-aligned - ASSERT_EQ(Rect(924, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Second child is "start", which will be right-aligned - ASSERT_EQ(Rect(924, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // Third child is "end", which will be left-aligned - ASSERT_EQ(Rect(0, 200, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // Fourth child is "center", which will be centered - ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *RTL_ALIGN_ITEMS_END = R"( @@ -756,21 +756,21 @@ static const char *RTL_ALIGN_ITEMS_END = R"( TEST_F(LayoutDirectionText, RTLAlignItemsEnd) { loadDocument(RTL_ALIGN_ITEMS_END); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(Object(kLayoutDirectionRTL), component->getCalculated(kPropertyLayoutDirection)); ASSERT_EQ(4, component->getChildCount()); auto child = component->getChildAt(0); // First child is "auto", which will be left-aligned - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); // Second child is "start", which will be right-aligned - ASSERT_EQ(Rect(924, 100, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(924, 100, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(2); // Third child is "end", which will be left-aligned - ASSERT_EQ(Rect(0, 200, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 200, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(3); // Fourth child is "center", which will be centered - ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(462, 300, 100, 100), child->getCalculated(kPropertyBounds).get()); } /* @@ -803,14 +803,14 @@ static const char *RTL_JUSTIFY_END = R"( TEST_F(LayoutDirectionText, RTLJustifyEnd) { loadDocument(RTL_JUSTIFY_END); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(2, component->getChildCount()); auto child = component->getChildAt(0); - ASSERT_EQ(Rect(100, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(100, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); child = component->getChildAt(1); - ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), child->getCalculated(kPropertyBounds).get()); } static const char *DOC_LAYOUTDIRECTION_PROPERTY_DEFAULT = R"( diff --git a/unit/component/unittest_scroll.cpp b/unit/component/unittest_scroll.cpp index 9ef6442..494351a 100644 --- a/unit/component/unittest_scroll.cpp +++ b/unit/component/unittest_scroll.cpp @@ -2441,7 +2441,7 @@ TEST_F(ScrollTest, DeepNestedStickyComponents) auto stickyCompInner1 = context->findComponentById("innerSticky1"); auto stickyCompInner2 = context->findComponentById("innerSticky2"); auto stickyCompInner3 = - std::dynamic_pointer_cast(context->findComponentById("innerSticky3")); + CoreComponent::cast(context->findComponentById("innerSticky3")); auto stickyCompInner4 = context->findComponentById("innerSticky4"); ASSERT_TRUE(scroll); ASSERT_TRUE(stickyComp); @@ -2660,7 +2660,7 @@ TEST_F(ScrollTest, StickyTestSkipRightOffset) { loadDocument(TEST_BASIC_LEFT_RIGHT_OFFSET_STICKY); auto scroll = context->findComponentById("scrollone"); - auto stickyComp = std::dynamic_pointer_cast(context->findComponentById("rightsticky")); + auto stickyComp = CoreComponent::cast(context->findComponentById("rightsticky")); ASSERT_TRUE(scroll); ASSERT_TRUE(stickyComp); @@ -3156,7 +3156,7 @@ TEST_F(ScrollTest, SetUnsetStickyChildTest) JsonData dataTop(NON_STICKY_CHILD_TOP_WITH_OFFSET); auto childTop = context->inflate(dataTop.get()); - auto coreChildTop = std::dynamic_pointer_cast(childTop); + auto coreChildTop = CoreComponent::cast(childTop); ASSERT_TRUE(coreChildTop); auto scroll = context->findComponentById("scrollone"); @@ -3206,7 +3206,7 @@ TEST_F(ScrollTest, SetUnsetStickyChildTest) //Check it also works with a second child JsonData dataBottom(NON_STICKY_CHILD_BOTTOM_WITHOUT_OFFSET); auto childBottom = context->inflate(dataBottom.get()); - auto coreChildBottom = std::dynamic_pointer_cast(childBottom); + auto coreChildBottom = CoreComponent::cast(childBottom); ASSERT_TRUE(childBottom); stickyCont->insertChild(childBottom, 2); @@ -3527,7 +3527,7 @@ TEST_F(ScrollTest, NestedScrollablesSameAndDifferntTypeWithStickies) auto stickyTop = context->findComponentById("topSticky"); auto deepestSticky = - std::dynamic_pointer_cast(context->findComponentById("deepestSticky")); + CoreComponent::cast(context->findComponentById("deepestSticky")); assert(stickyTop); assert(deepestSticky); diff --git a/unit/component/unittest_serialize.cpp b/unit/component/unittest_serialize.cpp index f36a5ea..e31e378 100644 --- a/unit/component/unittest_serialize.cpp +++ b/unit/component/unittest_serialize.cpp @@ -37,7 +37,7 @@ class SerializeTest : public DocumentWrapper { ASSERT_EQ(component->getUniqueId(), json["id"].GetString()); ASSERT_EQ(component->getType(), json["type"].GetInt()); ASSERT_EQ(component->getCalculated(kPropertyAccessibilityLabel), json["accessibilityLabel"].GetString()); - auto bounds = component->getCalculated(kPropertyBounds).getRect(); + auto bounds = component->getCalculated(kPropertyBounds).get(); ASSERT_EQ(bounds.getX(), json["_bounds"][0].GetFloat()); ASSERT_EQ(bounds.getY(), json["_bounds"][1].GetFloat()); ASSERT_EQ(bounds.getWidth(), json["_bounds"][2].GetFloat()); @@ -45,13 +45,13 @@ class SerializeTest : public DocumentWrapper { ASSERT_EQ(component->getCalculated(kPropertyChecked), json["checked"].GetBool()); ASSERT_EQ(component->getCalculated(kPropertyDisabled), json["disabled"].GetBool()); ASSERT_EQ(component->getCalculated(kPropertyDisplay), json["display"].GetDouble()); - auto innerBounds = component->getCalculated(kPropertyInnerBounds).getRect(); + auto innerBounds = component->getCalculated(kPropertyInnerBounds).get(); ASSERT_EQ(innerBounds.getX(), json["_innerBounds"][0].GetFloat()); ASSERT_EQ(innerBounds.getY(), json["_innerBounds"][1].GetFloat()); ASSERT_EQ(innerBounds.getWidth(), json["_innerBounds"][2].GetFloat()); ASSERT_EQ(innerBounds.getHeight(), json["_innerBounds"][3].GetFloat()); ASSERT_EQ(component->getCalculated(kPropertyOpacity), json["opacity"].GetDouble()); - auto transform = component->getCalculated(kPropertyTransform).getTransform2D(); + auto transform = component->getCalculated(kPropertyTransform).get(); ASSERT_EQ(transform.get().at(0), json["_transform"][0].GetFloat()); ASSERT_EQ(transform.get().at(1), json["_transform"][1].GetFloat()); ASSERT_EQ(transform.get().at(2), json["_transform"][2].GetFloat()); @@ -216,11 +216,11 @@ TEST_F(SerializeTest, Components) checkCommonProperties(image, imageJson); ASSERT_EQ(image->getCalculated(kPropertyAlign), imageJson["align"].GetDouble()); ASSERT_EQ(image->getCalculated(kPropertyBorderRadius).getAbsoluteDimension(), imageJson["borderRadius"].GetDouble()); - auto filter = image->getCalculated(kPropertyFilters).getArray().at(0).getFilter(); + auto filter = image->getCalculated(kPropertyFilters).getArray().at(0).get(); ASSERT_EQ(filter.getType(), imageJson["filters"][0]["type"]); ASSERT_EQ(filter.getValue(FilterProperty::kFilterPropertyRadius).getAbsoluteDimension(), imageJson["filters"][0]["radius"].GetDouble()); ASSERT_EQ(image->getCalculated(kPropertyOverlayColor).getColor(), Color(session, imageJson["overlayColor"].GetString())); - auto gradient = image->getCalculated(kPropertyOverlayGradient).getGradient(); + auto gradient = image->getCalculated(kPropertyOverlayGradient).get(); ASSERT_EQ(gradient.getType(), imageJson["overlayGradient"]["type"].GetDouble()); ASSERT_EQ(gradient.getProperty(kGradientPropertyAngle), imageJson["overlayGradient"]["angle"].GetDouble()); ASSERT_EQ(gradient.getProperty(kGradientPropertyColorRange).size(), imageJson["overlayGradient"]["colorRange"].Size()); @@ -241,7 +241,7 @@ TEST_F(SerializeTest, Components) ASSERT_EQ(text->getCalculated(kPropertyLetterSpacing).getAbsoluteDimension(), textJson["letterSpacing"].GetDouble()); ASSERT_EQ(text->getCalculated(kPropertyLineHeight), textJson["lineHeight"].GetDouble()); ASSERT_EQ(text->getCalculated(kPropertyMaxLines), textJson["maxLines"].GetDouble()); - auto styledText = text->getCalculated(kPropertyText).getStyledText(); + auto styledText = text->getCalculated(kPropertyText).get(); ASSERT_EQ(styledText.getText(), textJson["text"]["text"].GetString()); auto styledTextIt = StyledText::Iterator(styledText); styledTextIt.next(); @@ -261,14 +261,14 @@ TEST_F(SerializeTest, Components) auto& frameJson = json["children"][3]; checkCommonProperties(frame, frameJson); ASSERT_EQ(frame->getCalculated(kPropertyBackgroundColor).getColor(), Color(session, frameJson["backgroundColor"].GetString())); - auto radii = frame->getCalculated(kPropertyBorderRadii).getRadii(); + const auto& radii = frame->getCalculated(kPropertyBorderRadii).get(); ASSERT_EQ(radii.get().at(0), frameJson["_borderRadii"][0].GetFloat()); ASSERT_EQ(radii.get().at(1), frameJson["_borderRadii"][1].GetFloat()); ASSERT_EQ(radii.get().at(2), frameJson["_borderRadii"][2].GetFloat()); ASSERT_EQ(radii.get().at(3), frameJson["_borderRadii"][3].GetFloat()); ASSERT_EQ(frame->getCalculated(kPropertyBorderColor).getColor(), Color(session, frameJson["borderColor"].GetString())); ASSERT_EQ(frame->getCalculated(kPropertyBorderWidth).getAbsoluteDimension(), frameJson["borderWidth"].GetDouble()); - auto action = frame->getCalculated(kPropertyAccessibilityActions).at(0).getAccessibilityAction(); + auto action = frame->getCalculated(kPropertyAccessibilityActions).at(0).get(); ASSERT_EQ(action->getName(), frameJson["action"][0]["name"].GetString()); ASSERT_EQ(action->getLabel(), frameJson["action"][0]["label"].GetString()); ASSERT_EQ(action->enabled(), frameJson["action"][0]["enabled"].GetBool()); @@ -314,7 +314,7 @@ TEST_F(SerializeTest, Components) auto videoSource = video->getCalculated(kPropertySource).getArray(); ASSERT_EQ(3, videoSource.size()); ASSERT_EQ(videoSource.size(), videoJson["source"].Size()); - auto source3 = videoSource.at(2).getMediaSource(); + auto source3 = videoSource.at(2).get(); ASSERT_EQ(source3.getUrl(), videoJson["source"][2]["url"].GetString()); ASSERT_EQ(source3.getDescription(), videoJson["source"][2]["description"].GetString()); ASSERT_EQ(source3.getDuration(), videoJson["source"][2]["duration"].GetInt()); @@ -329,7 +329,7 @@ TEST_F(SerializeTest, Dirty) loadDocument(SERIALIZE_COMPONENTS); ASSERT_EQ(kComponentTypeContainer, component->getType()); - auto text = std::static_pointer_cast(context->findComponentById("text")); + auto text = CoreComponent::cast(context->findComponentById("text")); ASSERT_TRUE(text); text->setProperty(kPropertyText, "Not very styled text."); @@ -348,7 +348,7 @@ TEST_F(SerializeTest, Event) loadDocument(SERIALIZE_COMPONENTS); ASSERT_EQ(kComponentTypeContainer, component->getType()); - auto text = std::static_pointer_cast(context->findComponentById("text")); + auto text = CoreComponent::cast(context->findComponentById("text")); ASSERT_TRUE(text); auto touch = context->findComponentById("touch"); @@ -768,7 +768,7 @@ TEST_F(SerializeTest, AVG) checkCommonProperties(component, json); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto& graphicJson = json["graphic"]; ASSERT_EQ(graphicJson["isValid"].GetBool(), true); @@ -816,7 +816,7 @@ TEST_F(SerializeTest, AVG) ASSERT_TRUE(pathJson["props"]["_strokeTransform"].IsArray()); ASSERT_EQ(pathJson["props"]["strokeWidth"].GetDouble(), path->getValue(kGraphicPropertyStrokeWidth).getDouble()); - auto gradient = path->getValue(kGraphicPropertyStroke).getGradient(); + auto gradient = path->getValue(kGraphicPropertyStroke).get(); auto& gradientJson = pathJson["props"]["stroke"]; ASSERT_EQ(gradientJson["type"].GetDouble(), gradient.getType()); @@ -829,7 +829,7 @@ TEST_F(SerializeTest, AVG) ASSERT_EQ(gradientJson["y2"].GetDouble(), gradient.getProperty(kGradientPropertyY2).getDouble()); - auto pattern = path->getValue(kGraphicPropertyFill).getGraphicPattern(); + auto pattern = path->getValue(kGraphicPropertyFill).get(); auto& patternJson = pathJson["props"]["fill"]; ASSERT_EQ(patternJson["id"].GetString(), pattern->getUniqueId()); @@ -1064,11 +1064,11 @@ TEST_F(SerializeTest, SerializeHeaders) { checkCommonProperties(video, videoJson); auto videoSource = video->getCalculated(kPropertySource).getArray(); ASSERT_EQ(1, videoSource.size()); - ASSERT_TRUE(videoSource.at(0).isMediaSource()); - ASSERT_EQ(videoSource.at(0).getMediaSource().getUrl(), videoJson["source"][0]["url"].GetString()); - ASSERT_EQ(videoSource.at(0).getMediaSource().getDescription(), videoJson["source"][0]["description"].GetString()); + ASSERT_TRUE(videoSource.at(0).is()); + ASSERT_EQ(videoSource.at(0).get().getUrl(), videoJson["source"][0]["url"].GetString()); + ASSERT_EQ(videoSource.at(0).get().getDescription(), videoJson["source"][0]["description"].GetString()); auto videoSerializedHeaders = videoJson["source"][0]["headers"].GetArray(); - auto videoHeaders = videoSource.at(0).getMediaSource().getHeaders(); + auto videoHeaders = videoSource.at(0).get().getHeaders(); ASSERT_EQ(videoSerializedHeaders.Size(), videoHeaders.size()); for(auto i = 0; i < videoHeaders.size(); ++i) { ASSERT_EQ(videoHeaders[i], videoSerializedHeaders[i].GetString()); @@ -1080,10 +1080,10 @@ TEST_F(SerializeTest, SerializeHeaders) { checkCommonProperties(image, imageJson); auto imageSource = image->getCalculated(kPropertySource).getArray(); ASSERT_EQ(1, imageSource.size()); - ASSERT_TRUE(imageSource.at(0).isURLRequest()); - ASSERT_EQ(imageSource.at(0).getURLRequest().getUrl(), imageJson["source"][0]["url"].GetString()); + ASSERT_TRUE(imageSource.at(0).is()); + ASSERT_EQ(imageSource.at(0).get().getUrl(), imageJson["source"][0]["url"].GetString()); auto serializedHeaders = imageJson["source"][0]["headers"].GetArray(); - auto imageHeaders = imageSource.at(0).getURLRequest().getHeaders(); + auto imageHeaders = imageSource.at(0).get().getHeaders(); ASSERT_EQ(serializedHeaders.Size(), imageHeaders.size()); for(auto i = 0; i < imageHeaders.size(); ++i) { ASSERT_EQ(imageHeaders[i], serializedHeaders[i].GetString()); diff --git a/unit/component/unittest_text_component.cpp b/unit/component/unittest_text_component.cpp index 1feddd0..34f64b1 100644 --- a/unit/component/unittest_text_component.cpp +++ b/unit/component/unittest_text_component.cpp @@ -101,7 +101,7 @@ static const char* LANG_TEXT_DEFAULT_DOC = R"({ TEST_F(TextComponentTest, ComponentTextLangDefaults) { loadDocument(LANG_TEXT_DEFAULT_DOC); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_EQ("en-US", et->getCalculated(kPropertyLang).asString()); et->setProperty(kPropertyLang, "ja-jp"); @@ -152,7 +152,7 @@ static const char* TEXT_ALIGN_DEFAULT = R"({ TEST_F(TextComponentTest, TextAlignParseCheck) { loadDocument(TEXT_ALIGN_DEFAULT); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_EQ(kTextAlignAuto, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlign).asInt()); ASSERT_EQ(kTextAlignAuto, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlignAssigned).asInt()); @@ -212,7 +212,7 @@ static const char* TEXT_ALIGN_DEFAULT_RTL = R"({ TEST_F(TextComponentTest, TextAlignParseCheckRTL) { loadDocument(TEXT_ALIGN_DEFAULT_RTL); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_EQ(kTextAlignRight, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlign).asInt()); ASSERT_EQ(kTextAlignStart, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlignAssigned).asInt()); @@ -234,7 +234,7 @@ TEST_F(TextComponentTest, TextAlignParseCheckRTL) { TEST_F(TextComponentTest, TextAlignDynCheckRTL) { loadDocument(TEXT_ALIGN_DEFAULT_RTL); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_EQ(kTextAlignRight, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlign).asInt()); ASSERT_EQ(kTextAlignStart, et->getCoreChildAt(0)->getCalculated(kPropertyTextAlignAssigned).asInt()); @@ -257,7 +257,7 @@ TEST_F(TextComponentTest, TextAlignDynCheckRTL) { TEST_F(TextComponentTest, TextAlignDirtyFlag) { loadDocument(TEXT_ALIGN_DEFAULT_RTL); - auto et = std::dynamic_pointer_cast(root->topComponent()); + auto et = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(CheckDirty(root)); diff --git a/unit/component/unittest_visual_hash.cpp b/unit/component/unittest_visual_hash.cpp index be8dcbf..b604bc5 100644 --- a/unit/component/unittest_visual_hash.cpp +++ b/unit/component/unittest_visual_hash.cpp @@ -379,7 +379,7 @@ TEST_F(VisualHashTest, HashRemainsStableWhenLayoutAlignmentNeedsFixing) loadDocument(RTL_FIX_ALIGNMENT); ASSERT_EQ("Text", component->name()); - auto textComponent = std::dynamic_pointer_cast(component); + auto textComponent = TextComponent::cast(component); auto originalVisualHash = textComponent->getCalculated(kPropertyVisualHash); textComponent->setProperty(kPropertyText, "Different text"); @@ -419,7 +419,7 @@ TEST_F(VisualHashTest, HashRecalculatedBeforeLayoutInTimeForTextMeasurement) loadDocument(REMEASURE_TEXT); ASSERT_EQ(1, component->getChildCount()); - auto textComponent = std::dynamic_pointer_cast(component->getChildAt(0)); + auto textComponent = TextComponent::cast(component->getChildAt(0)); ASSERT_EQ("Text", textComponent->name()); ASSERT_EQ(1, spyTextMeasure->visualHashes.size()); diff --git a/unit/content/unittest_apl.cpp b/unit/content/unittest_apl.cpp index bf39c71..2bbe02f 100644 --- a/unit/content/unittest_apl.cpp +++ b/unit/content/unittest_apl.cpp @@ -148,13 +148,13 @@ TEST(APLTest, Basic) // Check the layout auto top = root->topComponent(); // The touchwrapper - ASSERT_EQ(Rect(0, 0, 400, 400), top->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 400, 400), top->getCalculated(kPropertyBounds).get()); auto frame = top->getChildAt(0); ASSERT_EQ(Object(Color()), frame->getCalculated(kPropertyBorderColor)); auto text = frame->getChildAt(0); - ASSERT_EQ(Rect(2, 2, 120, 60), text->getCalculated(kPropertyBounds).getRect()); // Frame has a 2 dp border + ASSERT_EQ(Rect(2, 2, 120, 60), text->getCalculated(kPropertyBounds).get()); // Frame has a 2 dp border ASSERT_EQ(StyledText::create(root->context(), "Your text inserted here"), - text->getCalculated(kPropertyText).getStyledText()); + text->getCalculated(kPropertyText).get()); ASSERT_EQ(Object(Color(root->getSession(), "#ff1020")), text->getCalculated(kPropertyColor)); // Simulate a user touching on the screen diff --git a/unit/content/unittest_document.cpp b/unit/content/unittest_document.cpp index 9b7eae9..e7a9559 100644 --- a/unit/content/unittest_document.cpp +++ b/unit/content/unittest_document.cpp @@ -234,8 +234,8 @@ TEST(DocumentTest, UserDefinedSettings) ASSERT_EQ(500, settings->getValue("userSettingNumber").getInteger()); ASSERT_TRUE(settings->getValue("userSettingBool").getBoolean()); ASSERT_EQ(Object(Dimension(100)), settings->getValue("userSettingDimension").asDimension(*context)); - ASSERT_EQ(Object::kArrayType, settings->getValue("userSettingArray").getType()); - ASSERT_EQ(Object::kMapType, settings->getValue("userSettingMap").getType()); + ASSERT_TRUE(settings->getValue("userSettingArray").isArray()); + ASSERT_TRUE(settings->getValue("userSettingMap").isMap()); } diff --git a/unit/content/unittest_document_background.cpp b/unit/content/unittest_document_background.cpp index 49bad5e..42e2334 100644 --- a/unit/content/unittest_document_background.cpp +++ b/unit/content/unittest_document_background.cpp @@ -54,7 +54,7 @@ TEST_F(DocumentBackgroundTest, NoBackground) { auto background = load(NO_BACKGROUND); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::TRANSPARENT), background)); } @@ -73,7 +73,7 @@ TEST_F(DocumentBackgroundTest, ColorBackground) { auto background = load(COLOR_BACKGROUND); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::BLUE), background)); } @@ -103,9 +103,9 @@ TEST_F(DocumentBackgroundTest, GradientBackground) { auto background = load(GRADIENT_BACKGROUND); - ASSERT_TRUE(background.isGradient()); + ASSERT_TRUE(background.is()); - auto gradient = background.getGradient(); + auto gradient = background.get(); ASSERT_EQ(Gradient::GradientType::LINEAR, gradient.getType()); ASSERT_EQ(90, gradient.getProperty(kGradientPropertyAngle).getInteger()); ASSERT_EQ(std::vector({Color(0x006400ff), Color(0xffffffff)}), gradient.getProperty(kGradientPropertyColorRange).getArray()); @@ -129,7 +129,7 @@ TEST_F(DocumentBackgroundTest, BadBackgroundMap) { auto background = load(BAD_BACKGROUND_MAP); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::TRANSPARENT), background)); } @@ -148,7 +148,7 @@ TEST_F(DocumentBackgroundTest, BadBackgroundColor) { auto background = load(BAD_BACKGROUND_COLOR); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::TRANSPARENT), background)); } @@ -168,13 +168,13 @@ TEST_F(DocumentBackgroundTest, DataBindingTest) // Small screens get a red background metrics.size(100, 100); auto background = load(DATA_BINDING_TEST); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::RED), background)); // Large screens get a blue background metrics.size(1000,1000); background = load(DATA_BINDING_TEST); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(Color::BLUE), background)); } @@ -196,12 +196,12 @@ TEST_F(DocumentBackgroundTest, DataBoundTheme) { metrics.theme("dark"); auto background = load(DATA_BOUND_THEME); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(0x102040ff), background)); metrics.theme("light"); background = load(DATA_BOUND_THEME); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(0xe0e0c0ff), background)); } @@ -224,11 +224,11 @@ TEST_F(DocumentBackgroundTest, DataBoundThemeOverride) { metrics.theme("dark"); auto background = load(DATA_BOUND_THEME_OVERRIDE); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(0xe0e0c0ff), background)); metrics.theme("light"); background = load(DATA_BOUND_THEME_OVERRIDE); - ASSERT_TRUE(background.isColor()); + ASSERT_TRUE(background.is()); ASSERT_TRUE(IsEqual(Color(0xe0e0c0ff), background)); } diff --git a/unit/datagrammar/unittest_decompile.cpp b/unit/datagrammar/unittest_decompile.cpp index a2258eb..84df28a 100644 --- a/unit/datagrammar/unittest_decompile.cpp +++ b/unit/datagrammar/unittest_decompile.cpp @@ -88,7 +88,7 @@ TEST_F(DecompileTest, Basic) for (const auto& m : DECOMPILE_TEST_CASES) { auto v = getDataBinding(*context, m.expression); ASSERT_TRUE(v.isEvaluable()); - auto bc = v.getByteCode(); + auto bc = v.get(); ASSERT_TRUE(CheckByteCode(m.instructions, bc)) << "Test case '" << m.expression << "'"; } } \ No newline at end of file diff --git a/unit/datagrammar/unittest_grammar.cpp b/unit/datagrammar/unittest_grammar.cpp index c912437..35edb31 100644 --- a/unit/datagrammar/unittest_grammar.cpp +++ b/unit/datagrammar/unittest_grammar.cpp @@ -1062,7 +1062,7 @@ TEST_F(GrammarTest, RangeAsArray) // Use getArray on a RangeGenerator auto range = evaluate(*c, "${Array.range(10)}"); - ASSERT_EQ(Object::kArrayType, range.getType()); + ASSERT_TRUE(range.isArray()); ASSERT_EQ(10, range.size()); ASSERT_EQ(10, range.getArray().size()); const auto expected = ObjectArray{0,1,2,3,4,5,6,7,8,9}; @@ -1070,7 +1070,7 @@ TEST_F(GrammarTest, RangeAsArray) // Try a zero-size array range = evaluate(*c, "${Array.range(-2)}"); - ASSERT_EQ(Object::kArrayType, range.getType()); + ASSERT_TRUE(range.isArray()); ASSERT_EQ(0, range.size()); ASSERT_EQ(0, range.getArray().size()); ASSERT_EQ(ObjectArray{}, range.getArray()); @@ -1128,7 +1128,7 @@ TEST_F(GrammarTest, SliceAsArray) // Use getArray on a SliceGenerator auto range = evaluate(*c, "${Array.slice(a1, 4)}"); - ASSERT_EQ(Object::kArrayType, range.getType()); + ASSERT_TRUE(range.isArray()); ASSERT_EQ(2, range.size()); ASSERT_EQ(2, range.getArray().size()); const auto expected = ObjectArray{105,106}; @@ -1136,7 +1136,7 @@ TEST_F(GrammarTest, SliceAsArray) // Try a zero-size array range = evaluate(*c, "${Array.slice(a1,10)}"); - ASSERT_EQ(Object::kArrayType, range.getType()); + ASSERT_TRUE(range.isArray()); ASSERT_EQ(0, range.size()); ASSERT_EQ(0, range.getArray().size()); ASSERT_EQ(ObjectArray{}, range.getArray()); diff --git a/unit/datagrammar/unittest_optimize.cpp b/unit/datagrammar/unittest_optimize.cpp index c45d0e1..ed56560 100644 --- a/unit/datagrammar/unittest_optimize.cpp +++ b/unit/datagrammar/unittest_optimize.cpp @@ -66,12 +66,12 @@ TEST_F(OptimizeTest, Basic) auto result = parseDataBinding(*context, m.first); ASSERT_TRUE(result.isEvaluable()); ASSERT_TRUE(IsEqual(m.second, result.eval())) << m.first; - ASSERT_FALSE(result.getByteCode()->isOptimized()); + ASSERT_FALSE(result.get()->isOptimized()); SymbolReferenceMap symbols; result.symbols(symbols); ASSERT_TRUE(IsEqual(m.second, result.eval())) << m.first; - ASSERT_TRUE(result.getByteCode()->isOptimized()); + ASSERT_TRUE(result.get()->isOptimized()); } } @@ -98,7 +98,7 @@ TEST_F(OptimizeTest, DeadCodeRemoval) { context->putUserWriteable("a", 23); auto result = parseDataBinding(*context, "${a?(1!=2? 10:3):4}"); - ASSERT_TRUE(result.isByteCode()); + ASSERT_TRUE(result.is()); ASSERT_TRUE(IsEqual(10, result.eval())); context->userUpdateAndRecalculate("a", 0, false); diff --git a/unit/datagrammar/unittest_parse.cpp b/unit/datagrammar/unittest_parse.cpp index a18b1e4..052363a 100644 --- a/unit/datagrammar/unittest_parse.cpp +++ b/unit/datagrammar/unittest_parse.cpp @@ -57,7 +57,7 @@ TEST_F(ParseTest, Simple) context->putConstant("@red", Color(Color::RED)); foo = parseDataBinding(*context, "${@red}"); ASSERT_FALSE(foo.isEvaluable()); - ASSERT_TRUE(foo.isColor()); + ASSERT_TRUE(foo.is()); ASSERT_TRUE(IsEqual(Color(Color::RED), foo)); context->putUserWriteable("b", 82); diff --git a/unit/datasource/unittest_dynamicindexlist.cpp b/unit/datasource/unittest_dynamicindexlist.cpp index 69da57a..d6685ae 100644 --- a/unit/datasource/unittest_dynamicindexlist.cpp +++ b/unit/datasource/unittest_dynamicindexlist.cpp @@ -3984,13 +3984,13 @@ TEST_F(DynamicIndexListTest, SequenceRecreate) auto sequence = component->getCoreChildAt(0)->getCoreChildAt(0); ASSERT_EQ(5, sequence->getChildCount()); - ASSERT_EQ(Rect(0, 0, 300, 300), component->getCoreChildAt(0)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 300, 150), sequence->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 300, 10), sequence->getCoreChildAt(0)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 10, 300, 10), sequence->getCoreChildAt(1)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 20, 300, 10), sequence->getCoreChildAt(2)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 30, 300, 10), sequence->getCoreChildAt(3)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 40, 300, 10), sequence->getCoreChildAt(4)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 300, 300), component->getCoreChildAt(0)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 300, 150), sequence->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 300, 10), sequence->getCoreChildAt(0)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 10, 300, 10), sequence->getCoreChildAt(1)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 20, 300, 10), sequence->getCoreChildAt(2)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 30, 300, 10), sequence->getCoreChildAt(3)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 40, 300, 10), sequence->getCoreChildAt(4)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(ds->processUpdate(REPLACE_SEQUENCE_CRUD)); root->clearPending(); @@ -3998,13 +3998,13 @@ TEST_F(DynamicIndexListTest, SequenceRecreate) sequence = component->getCoreChildAt(0)->getCoreChildAt(0); ASSERT_EQ(5, sequence->getChildCount()); - ASSERT_EQ(Rect(0, 0, 300, 300).toDebugString(), component->getCoreChildAt(0)->getCalculated(kPropertyBounds).getRect().toDebugString()); - ASSERT_EQ(Rect(0, 0, 300, 150), sequence->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 0, 300, 10), sequence->getCoreChildAt(0)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 10, 300, 10), sequence->getCoreChildAt(1)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 20, 300, 10), sequence->getCoreChildAt(2)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 30, 300, 10), sequence->getCoreChildAt(3)->getCalculated(kPropertyBounds).getRect()); - ASSERT_EQ(Rect(0, 40, 300, 10), sequence->getCoreChildAt(4)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 300, 300).toDebugString(), component->getCoreChildAt(0)->getCalculated(kPropertyBounds).get().toDebugString()); + ASSERT_EQ(Rect(0, 0, 300, 150), sequence->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 0, 300, 10), sequence->getCoreChildAt(0)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 10, 300, 10), sequence->getCoreChildAt(1)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 20, 300, 10), sequence->getCoreChildAt(2)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 30, 300, 10), sequence->getCoreChildAt(3)->getCalculated(kPropertyBounds).get()); + ASSERT_EQ(Rect(0, 40, 300, 10), sequence->getCoreChildAt(4)->getCalculated(kPropertyBounds).get()); } static const char *FILLED_DATA = R"({ diff --git a/unit/datasource/unittest_dynamictokenlist.cpp b/unit/datasource/unittest_dynamictokenlist.cpp index 404aae5..72a8214 100644 --- a/unit/datasource/unittest_dynamictokenlist.cpp +++ b/unit/datasource/unittest_dynamictokenlist.cpp @@ -1677,7 +1677,7 @@ TEST_F(DynamicTokenListTest, DeepProgressive) { config->dataSourceProvider("testList", source); loadDocument(BIT_BY_A_BIT_DEEP, BIT_BY_A_BIT_DATA); - auto sequence = std::static_pointer_cast(root->findComponentById("dynamicSequence")); + auto sequence = CoreComponent::cast(root->findComponentById("dynamicSequence")); ASSERT_TRUE(sequence); ASSERT_EQ(1, sequence->getChildCount()); @@ -1932,12 +1932,12 @@ TEST_F(DynamicTokenListTest, DoublePager) { config->dataSourceProvider("testList", source); loadDocument(DOUBLE_PAGER_GALORE, DOUBLE_PAGER_GALORE_DATA); - auto topPager = std::static_pointer_cast(root->findComponentById("topPager")); + auto topPager = CoreComponent::cast(root->findComponentById("topPager")); ASSERT_TRUE(topPager); ASSERT_EQ(1, topPager->getChildCount()); ASSERT_EQ(Object(Rect(0,-50,100,100)), topPager->getCalculated(kPropertyBounds)); - auto bottomPager = std::static_pointer_cast(root->findComponentById("bottomPager")); + auto bottomPager = CoreComponent::cast(root->findComponentById("bottomPager")); ASSERT_TRUE(bottomPager); ASSERT_EQ(1, bottomPager->getChildCount()); ASSERT_EQ(Object(Rect(0,50,750,850)), bottomPager->getCalculated(kPropertyBounds)); diff --git a/unit/debugtools.cpp b/unit/debugtools.cpp index c43f3aa..a6730f6 100644 --- a/unit/debugtools.cpp +++ b/unit/debugtools.cpp @@ -112,7 +112,7 @@ void dumpHierarchy(const ComponentPtr& component, std::initializer_list args) { HierarchyVisitor hv(args); - std::dynamic_pointer_cast(component)->accept(hv); + CoreComponent::cast(component)->accept(hv); } @@ -186,7 +186,7 @@ dumpYogaInternal(YGNodeRef node, int indent=0) { void dumpYoga(const ComponentPtr& component) { - auto node = std::dynamic_pointer_cast(component)->getNode(); + auto node = CoreComponent::cast(component)->getNode(); dumpYogaInternal(node); } @@ -214,7 +214,7 @@ dumpLayoutInternal(const CoreComponentPtr& component, int indent, int childIndex void dumpLayout(const ComponentPtr& component) { - dumpLayoutInternal(std::dynamic_pointer_cast(component), 0, 0); + dumpLayoutInternal(CoreComponent::cast(component), 0, 0); } } // namespace apl diff --git a/unit/engine/unittest_builder.cpp b/unit/engine/unittest_builder.cpp index 8982cf4..993aa8b 100644 --- a/unit/engine/unittest_builder.cpp +++ b/unit/engine/unittest_builder.cpp @@ -162,7 +162,7 @@ TEST_F(BuilderTest, SimpleImage) ASSERT_EQ(Object(Dimension(0)), component->getCalculated(kPropertyShadowHorizontalOffset)); ASSERT_EQ(Object(Dimension(0)), component->getCalculated(kPropertyShadowRadius)); ASSERT_EQ(Object(Dimension(0)), component->getCalculated(kPropertyShadowVerticalOffset)); - ASSERT_EQ(Object::IDENTITY_2D(), component->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), component->getCalculated(kPropertyTransform)); ASSERT_EQ(Object::NULL_OBJECT(), component->getCalculated(kPropertyTransformAssigned)); ASSERT_EQ(Object(Dimension(100)), component->getCalculated(kPropertyWidth)); ASSERT_EQ(Object::TRUE_OBJECT(), component->getCalculated(kPropertyLaidOut)); @@ -264,8 +264,8 @@ TEST_F(BuilderTest, FullImage) // Transforms are tricky auto transform = component->getCalculated(kPropertyTransformAssigned); - ASSERT_TRUE(transform.isTransform()); - ASSERT_EQ(Point(20,4), transform.getTransformation()->get(10,10) * Point(10,4)); + ASSERT_TRUE(transform.is()); + ASSERT_EQ(Point(20,4), transform.get()->get(10,10) * Point(10,4)); ASSERT_EQ(Object(Transform2D::translateX(10)), component->getCalculated(kPropertyTransform)); // Image-specific properties @@ -276,14 +276,14 @@ TEST_F(BuilderTest, FullImage) ASSERT_EQ("http://foo.com/bar.png", component->getCalculated(kPropertySource).getString()); auto grad = component->getCalculated(kPropertyOverlayGradient); - ASSERT_TRUE(grad.isGradient()); - ASSERT_EQ(Gradient::LINEAR, grad.getGradient().getType()); - ASSERT_EQ(Object(Color(0x0000ffff)), grad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(grad.is()); + ASSERT_EQ(Gradient::LINEAR, grad.get().getType()); + ASSERT_EQ(Object(Color(0x0000ffff)), grad.get().getProperty(kGradientPropertyColorRange).at(0)); auto filters = component->getCalculated(kPropertyFilters); ASSERT_EQ(1, filters.size()); - ASSERT_EQ(kFilterTypeBlur, filters.at(0).getFilter().getType()); - ASSERT_EQ(Object(Dimension(22)), filters.at(0).getFilter().getValue(kFilterPropertyRadius)); + ASSERT_EQ(kFilterTypeBlur, filters.at(0).get().getType()); + ASSERT_EQ(Object(Dimension(22)), filters.at(0).get().getValue(kFilterPropertyRadius)); ASSERT_TRUE(CheckState(component, kStateChecked, kStateDisabled)); } @@ -322,9 +322,9 @@ TEST_F(BuilderTest, GradientInResource) { loadDocument(GRADIENT_IN_RESOURCE, DATA); auto grad = component->getCalculated(kPropertyOverlayGradient); - ASSERT_TRUE(grad.isGradient()); - ASSERT_EQ(Gradient::LINEAR, grad.getGradient().getType()); - ASSERT_EQ(Object(Color(0x0000ffff)), grad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(grad.is()); + ASSERT_EQ(Gradient::LINEAR, grad.get().getType()); + ASSERT_EQ(Object(Color(0x0000ffff)), grad.get().getProperty(kGradientPropertyColorRange).at(0)); } static const char *SIMPLE_TEXT = R"( @@ -367,7 +367,7 @@ TEST_F(BuilderTest, SimpleText) ASSERT_EQ(Object::NULL_OBJECT(), component->getCalculated(kPropertyPaddingTop)); ASSERT_EQ(Object(ObjectArray{}), component->getCalculated(kPropertyPadding)); ASSERT_EQ(kRoleNone, component->getCalculated(kPropertyRole).getInteger()); - ASSERT_EQ(Object::IDENTITY_2D(), component->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), component->getCalculated(kPropertyTransform)); ASSERT_EQ(Object::NULL_OBJECT(), component->getCalculated(kPropertyTransformAssigned)); ASSERT_EQ(Object(Dimension()), component->getCalculated(kPropertyWidth)); ASSERT_EQ(Object::TRUE_OBJECT(), component->getCalculated(kPropertyLaidOut)); @@ -525,15 +525,15 @@ TEST_F(BuilderTest, SimpleContainer) // The child has relative positioning ASSERT_EQ(kFlexboxAlignAuto, text["alignSelf"].getInteger()); - ASSERT_EQ(Object::AUTO_OBJECT(), text["bottom"]); + ASSERT_EQ(Object(Dimension()), text["bottom"]); ASSERT_EQ(0, text["grow"].getDouble()); - ASSERT_EQ(Object::AUTO_OBJECT(), text["left"]); + ASSERT_EQ(Object(Dimension()), text["left"]); ASSERT_EQ(kNumberingNormal, text["numbering"].getInteger()); ASSERT_EQ(kPositionRelative, text["position"].getInteger()); - ASSERT_EQ(Object::AUTO_OBJECT(), text["right"]); + ASSERT_EQ(Object(Dimension()), text["right"]); ASSERT_EQ(0, text["shrink"].getDouble()); ASSERT_EQ(Object(Dimension(0)), text["spacing"]); - ASSERT_EQ(Object::AUTO_OBJECT(), text["top"]); + ASSERT_EQ(Object(Dimension()), text["top"]); } static const char *FULL_CONTAINER = R"( @@ -650,15 +650,15 @@ TEST_F(BuilderTest, FullContainer) // Third item (Relative positioning) child = component->getChildAt(2)->getCalculated(); ASSERT_EQ(kFlexboxAlignBaseline, child["alignSelf"].getInteger()); - ASSERT_EQ(Object::AUTO_OBJECT(), child["bottom"]); + ASSERT_EQ(Object(Dimension()), child["bottom"]); ASSERT_EQ(1, child["grow"].getDouble()); ASSERT_EQ(Object(Dimension(10)), child["left"]); ASSERT_EQ(kNumberingSkip, child["numbering"].getInteger()); ASSERT_EQ(kPositionRelative, child["position"].getInteger()); - ASSERT_EQ(Object::AUTO_OBJECT(), child["right"]); + ASSERT_EQ(Object(Dimension()), child["right"]); ASSERT_EQ(2, child["shrink"].getDouble()); ASSERT_EQ(Object(Dimension(20)), child["spacing"]); - ASSERT_EQ(Object::AUTO_OBJECT(), child["top"]); + ASSERT_EQ(Object(Dimension()), child["top"]); // Fourth item ASSERT_EQ("Last", component->getChildAt(3)->getCalculated(kPropertyText).asString()); @@ -1601,7 +1601,7 @@ TEST_F(BuilderTest, FullVideo) ASSERT_EQ(true, map.get(kPropertyMuted).getBoolean()); ASSERT_EQ(3, map.get(kPropertySource).size()); - auto source1 = map.get(kPropertySource).at(0).getMediaSource(); + auto source1 = map.get(kPropertySource).at(0).get(); ASSERT_EQ("", source1.getDescription()); ASSERT_EQ(0, source1.getDuration()); ASSERT_EQ("URL1", source1.getUrl()); @@ -1609,7 +1609,7 @@ TEST_F(BuilderTest, FullVideo) ASSERT_TRUE(source1.getEntities().empty()); ASSERT_EQ(0, source1.getOffset()); - auto source2 = map.get(kPropertySource).at(1).getMediaSource(); + auto source2 = map.get(kPropertySource).at(1).get(); ASSERT_EQ("", source2.getDescription()); ASSERT_EQ(0, source2.getDuration()); ASSERT_EQ("URL2", source2.getUrl()); @@ -1617,7 +1617,7 @@ TEST_F(BuilderTest, FullVideo) ASSERT_TRUE(source2.getEntities().empty()); ASSERT_EQ(0, source2.getOffset()); - auto source3 = map.get(kPropertySource).at(2).getMediaSource(); + auto source3 = map.get(kPropertySource).at(2).get(); ASSERT_EQ("Sample video.", source3.getDescription()); ASSERT_EQ(1000, source3.getDuration()); ASSERT_EQ("URL3", source3.getUrl()); @@ -1689,7 +1689,7 @@ TEST_F(BuilderTest, MediaSource) sources = video1->getCalculated(kPropertySource); ASSERT_TRUE(sources.isArray()); ASSERT_EQ(1, sources.size()); - auto source = sources.at(0).getMediaSource(); + auto source = sources.at(0).get(); ASSERT_EQ("", source.getDescription()); ASSERT_EQ(0, source.getDuration()); ASSERT_EQ("URL1", source.getUrl()); @@ -1700,7 +1700,7 @@ TEST_F(BuilderTest, MediaSource) sources = video2->getCalculated(kPropertySource); ASSERT_TRUE(sources.isArray()); ASSERT_EQ(1, sources.size()); - source = sources.at(0).getMediaSource(); + source = sources.at(0).get(); ASSERT_EQ("Sample video.", source.getDescription()); ASSERT_EQ(1000, source.getDuration()); ASSERT_EQ("URL1", source.getUrl()); @@ -1711,13 +1711,13 @@ TEST_F(BuilderTest, MediaSource) sources = video3->getCalculated(kPropertySource); ASSERT_TRUE(sources.isArray()); ASSERT_EQ(2, sources.size()); - source = sources.at(0).getMediaSource(); + source = sources.at(0).get(); ASSERT_EQ("", source.getDescription()); ASSERT_EQ(0, source.getDuration()); ASSERT_EQ("URL1", source.getUrl()); ASSERT_EQ(0, source.getRepeatCount()); ASSERT_EQ(0, source.getOffset()); - source = sources.at(1).getMediaSource(); + source = sources.at(1).get(); ASSERT_EQ("", source.getDescription()); ASSERT_EQ(0, source.getDuration()); ASSERT_EQ("URL2", source.getUrl()); @@ -1791,7 +1791,7 @@ TEST_F(BuilderTest, MediaSource2) auto sources = video->getCalculated(kPropertySource); ASSERT_TRUE(sources.isArray()) << msg; ASSERT_EQ(1, sources.size()) << msg; - auto source = sources.at(0).getMediaSource(); + auto source = sources.at(0).get(); ASSERT_EQ("URL1", source.getUrl()) << msg; } } @@ -2085,12 +2085,12 @@ TEST_F(BuilderTest, TransformOnPress) auto frame = component->getChildAt(0); - ASSERT_EQ(Object::IDENTITY_2D(), frame->getCalculated(kPropertyTransform)); + ASSERT_EQ(Object(Transform2D()), frame->getCalculated(kPropertyTransform)); performClick(1, 1); root->clearPending(); - auto t = frame->getCalculated(kPropertyTransform).getTransform2D(); + auto t = frame->getCalculated(kPropertyTransform).get(); // (0,0) -> (-10, -50) -> (20, -50) -> (40, -100) -> (50, -50) ASSERT_EQ(Point(50,-50), t * Point()); } @@ -2152,7 +2152,7 @@ TEST_F(BuilderTest, TransformWithResources) loadDocument(TRANSFORM_WITH_RESOURCES); auto frame = component->getChildAt(0); - auto t = frame->getCalculated(kPropertyTransform).getTransform2D(); + auto t = frame->getCalculated(kPropertyTransform).get(); // Center Ty=+400 Rot=-90 De-Center // (0,0) -> (-10,-50) -> (-10, 350) -> (350,10) -> (360, 60) @@ -2162,7 +2162,7 @@ TEST_F(BuilderTest, TransformWithResources) performClick(1, 1); root->clearPending(); - t = frame->getCalculated(kPropertyTransform).getTransform2D(); + t = frame->getCalculated(kPropertyTransform).get(); // Center Tx=+5 Scale=0.5 De-center // (0,0) -> (-10, -50) -> (-5, -50) -> (-2.5, -25) -> (7.5, 25) ASSERT_EQ(Point(7.5,25), t * Point()); @@ -2197,8 +2197,8 @@ static const char *DISPLAY_TEST = R"( TEST_F(BuilderTest, DisplayTest) { loadDocument(DISPLAY_TEST); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -2843,10 +2843,10 @@ TEST_F(BuilderTest, DynamicStartEndPaddingLTR) { loadDocument(START_END_NO_PADDING_OVERRIDE); - auto frame = std::dynamic_pointer_cast(component->findComponentById("paddedFrame")); - auto frame2 = std::dynamic_pointer_cast(component->findComponentById("paddedFrame2")); - auto child = std::dynamic_pointer_cast(component->findComponentById("paddedFrameChild")); - auto child2 = std::dynamic_pointer_cast(component->findComponentById("paddedFrameChild2")); + auto frame = CoreComponent::cast(component->findComponentById("paddedFrame")); + auto frame2 = CoreComponent::cast(component->findComponentById("paddedFrame2")); + auto child = CoreComponent::cast(component->findComponentById("paddedFrameChild")); + auto child2 = CoreComponent::cast(component->findComponentById("paddedFrameChild2")); //Check setting End and the right doesn't apply the right padding { @@ -2985,7 +2985,7 @@ TEST_F(BuilderTest, ComplexDynamicStartEndPaddingRTL) { loadDocument(START_END_PADDING_OVERRIDE); - auto cont = std::dynamic_pointer_cast(component->findComponentById("cont")); + auto cont = CoreComponent::cast(component->findComponentById("cont")); cont->setProperty(kPropertyLayoutDirectionAssigned, "RTL"); root->clearPending(); @@ -3048,7 +3048,7 @@ TEST_F(BuilderTest, PositionTypeRelativeToAbsolute) { loadDocument(POSITION_TYPE_TEST); - auto cont = std::dynamic_pointer_cast(component->findComponentById("frameComp1")); + auto cont = CoreComponent::cast(component->findComponentById("frameComp1")); cont->setProperty(kPropertyRight, 0); EXPECT_TRUE(expectBounds(cont, 0, 0, 100, 100)); @@ -3098,8 +3098,8 @@ TEST_F(BuilderTest, PositionTypeRelativeToAbsoluteStartEndInsets) { loadDocument(POSITION_TYPE_TEST); - auto cont = std::dynamic_pointer_cast(component->findComponentById("frameComp1")); - auto containerComp = std::dynamic_pointer_cast(component->findComponentById("containerComp")); + auto cont = CoreComponent::cast(component->findComponentById("frameComp1")); + auto containerComp = CoreComponent::cast(component->findComponentById("containerComp")); cont->setProperty(kPropertyStart, 10); cont->setProperty(kPropertyRight, 20); diff --git a/unit/engine/unittest_builder_config_change.cpp b/unit/engine/unittest_builder_config_change.cpp index af6ce08..7d3e010 100644 --- a/unit/engine/unittest_builder_config_change.cpp +++ b/unit/engine/unittest_builder_config_change.cpp @@ -226,7 +226,7 @@ TEST_F(BuilderConfigChange, Basic) configChangeReinflate(ConfigurationChange(500, 1000)); - component = std::static_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component); ASSERT_TRUE(IsEqual(Color(Color::RED), component->getCalculated(kPropertyBackgroundColor))); ASSERT_TRUE(IsEqual("reinflation", evaluate(*context, "${environment.reason}"))); @@ -644,7 +644,7 @@ TEST_F(BuilderConfigChange, DefaultResizeBehavior) // Change the size. There is no onConfigChange handler, so the document should resize automatically configChange(ConfigurationChange(600,200)); root->clearPending(); - ASSERT_TRUE(IsEqual(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).get())); ASSERT_TRUE(CheckDirty(component, kPropertyBounds, kPropertyInnerBounds, kPropertyNotifyChildrenChanged, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); @@ -676,12 +676,12 @@ TEST_F(BuilderConfigChange, OnConfigChangeNoRelayout) metrics.size(200,200); loadDocument(ON_CONFIG_CHANGE_NO_RELAYOUT); ASSERT_TRUE(component); - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); // Change the size. configChange(ConfigurationChange(300,100)); root->clearPending(); - ASSERT_EQ(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component, kPropertyBounds, kPropertyInnerBounds, kPropertyNotifyChildrenChanged, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); @@ -712,7 +712,7 @@ TEST_F(BuilderConfigChange, ReinflateActionRefIsTerminated) metrics.size(200,200); loadDocument(ON_CONFIG_CHANGE_BASIC_RELAYOUT); ASSERT_TRUE(component); - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); // Change the size. configChange(ConfigurationChange(300,100)); @@ -723,7 +723,7 @@ TEST_F(BuilderConfigChange, ReinflateActionRefIsTerminated) ASSERT_TRUE(event.getActionRef().isPending()); // There is a pending action reference // No reinflation has occurred yet - we haven't resolved the action reference - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component)); ASSERT_TRUE(CheckDirty(root)); @@ -731,8 +731,8 @@ TEST_F(BuilderConfigChange, ReinflateActionRefIsTerminated) root->reinflate(); context = root->contextPtr(); ASSERT_TRUE(context); - component = std::dynamic_pointer_cast(root->topComponent()); - ASSERT_EQ(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).getRect()); + component = CoreComponent::cast(root->topComponent()); + ASSERT_EQ(Rect({0,0,300,100}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component)); ASSERT_TRUE(CheckDirty(root)); ASSERT_TRUE(event.getActionRef().isTerminated()); @@ -762,7 +762,7 @@ TEST_F(BuilderConfigChange, ResizeQueue) metrics.size(200,200); loadDocument(RESIZE_QUEUE); ASSERT_TRUE(component); - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); // Change the size. configChange(ConfigurationChange(300,100)); @@ -773,7 +773,7 @@ TEST_F(BuilderConfigChange, ResizeQueue) ASSERT_TRUE(event.getActionRef().isPending()); // There is a pending action reference // No reinflation has occurred yet - we haven't resolved the first action reference - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component)); ASSERT_TRUE(CheckDirty(root)); @@ -790,14 +790,14 @@ TEST_F(BuilderConfigChange, ResizeQueue) root->clearPending(); // No reinflation has occurred yet - we haven't resolved the new "live" action reference - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component)); ASSERT_TRUE(CheckDirty(root)); // Resolve the second action ref - this will unblock the resize. event2.getActionRef().resolve(); root->clearPending(); - ASSERT_EQ(Rect({0,0,400,500}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,400,500}), component->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(CheckDirty(component, kPropertyBounds, kPropertyInnerBounds, kPropertyNotifyChildrenChanged, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); @@ -858,7 +858,7 @@ TEST_F(BuilderConfigChange, ReinflateWithHandleTick) metrics.size(200,200); loadDocument(HANDLE_TICK_REINFLATE); ASSERT_TRUE(component); - ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,200,200}), component->getCalculated(kPropertyBounds).get()); auto text = component->findComponentById("textField"); ASSERT_TRUE(text); @@ -870,7 +870,7 @@ TEST_F(BuilderConfigChange, ReinflateWithHandleTick) // Change the size. configChangeReinflate(ConfigurationChange(300,300)); ASSERT_TRUE(component); - ASSERT_EQ(Rect({0,0,300,300}), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect({0,0,300,300}), component->getCalculated(kPropertyBounds).get()); text = component->findComponentById("textField"); ASSERT_TRUE(text); @@ -913,10 +913,10 @@ TEST_F(BuilderConfigChange, ScaledWidthHeight) metrics.size(1000,600).dpi(320); loadDocument(CHECK_SCALED_WIDTH_HEIGHT); ASSERT_TRUE(component); - ASSERT_TRUE(IsEqual(Rect({0,0,500,300}), component->getCalculated(kPropertyBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect({0,0,500,300}), component->getCalculated(kPropertyBounds).get())); // Change the size of the view host configChange(ConfigurationChange(600,1000)); ASSERT_TRUE(CheckSendEvent(root, 300, 500)); - ASSERT_TRUE(IsEqual(Rect({0,0,300,500}), component->getCalculated(kPropertyBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect({0,0,300,500}), component->getCalculated(kPropertyBounds).get())); } \ No newline at end of file diff --git a/unit/engine/unittest_builder_pager.cpp b/unit/engine/unittest_builder_pager.cpp index 67921cf..8a1d80a 100644 --- a/unit/engine/unittest_builder_pager.cpp +++ b/unit/engine/unittest_builder_pager.cpp @@ -31,7 +31,7 @@ class BuilderTestPager : public DocumentWrapper { << ", actual: " << actualId; } - auto actualBounds = child->getCalculated(kPropertyBounds).getRect(); + auto actualBounds = child->getCalculated(kPropertyBounds).get(); if (bounds != actualBounds) { return ::testing::AssertionFailure() << "child " << idx diff --git a/unit/engine/unittest_builder_preserve.cpp b/unit/engine/unittest_builder_preserve.cpp index dd51aa4..084cb18 100644 --- a/unit/engine/unittest_builder_preserve.cpp +++ b/unit/engine/unittest_builder_preserve.cpp @@ -288,12 +288,12 @@ TEST_F(BuilderPreserveTest, SetDynamicProperties) {"TEXT", "color", Color(Color::LIME), nullptr}, {"TEXT", "text", "This is another test", [](const Object& target, const Object& x) { - return x.isStyledText() && x.getStyledText().asString() == target.asString(); + return x.is() && x.get().asString() == target.asString(); }}, {"VIDEO", "source", "http://www.videostuff.fake/dog.mp3", [](const Object& target, const Object& x) { - return x.isArray() && x.size() == 1 && x.at(0).isMediaSource() && - x.at(0).getMediaSource().getUrl() == target.asString(); + return x.isArray() && x.size() == 1 && x.at(0).is() && + x.at(0).get().getUrl() == target.asString(); }}, }; @@ -972,8 +972,8 @@ TEST_F(BuilderPreserveTest, VideoComponentSource) auto sources = component->getCalculated(kPropertySource); ASSERT_TRUE(sources.isArray()); ASSERT_EQ(2, sources.size()); - ASSERT_TRUE(IsEqual("FOO1", sources.at(0).getMediaSource().getUrl())); - ASSERT_TRUE(IsEqual("FOO2", sources.at(1).getMediaSource().getUrl())); + ASSERT_TRUE(IsEqual("FOO1", sources.at(0).get().getUrl())); + ASSERT_TRUE(IsEqual("FOO2", sources.at(1).get().getUrl())); } static const char *PRESERVE_BOUND_VALUES = R"apl( diff --git a/unit/engine/unittest_builder_preserve_scroll.cpp b/unit/engine/unittest_builder_preserve_scroll.cpp index d71f26e..ed28147 100644 --- a/unit/engine/unittest_builder_preserve_scroll.cpp +++ b/unit/engine/unittest_builder_preserve_scroll.cpp @@ -29,7 +29,7 @@ class BuilderPreserveScrollTest : public DocumentWrapper { Object getProp(const std::string& name, PropertyKey key) { auto component = root->findComponentById(name); auto keyName = sComponentPropertyBimap.at(key); - return std::dynamic_pointer_cast(component)->getProperty(keyName); + return CoreComponent::cast(component)->getProperty(keyName); } // Set the scroll position of a named component @@ -854,7 +854,7 @@ TEST_F(BuilderPreserveScrollTest, HorizontalWithPadding) metrics.size(300,300); loadDocument(HORIZONTAL_WITH_PADDING); ASSERT_TRUE(component); - auto c = std::dynamic_pointer_cast(component); + auto c = CoreComponent::cast(component); // Access the "getProperty" method to check the positions we are reading // The width of the sequence inner bounds is 150, so 7.5 Text blocks should fit, putting @@ -917,7 +917,7 @@ TEST_F(BuilderPreserveScrollTest, VerticalWithPadding) metrics.size(300,300); loadDocument(VERTICAL_WITH_PADDING); ASSERT_TRUE(component); - auto c = std::dynamic_pointer_cast(component); + auto c = CoreComponent::cast(component); // Access the "getProperty" method to check the positions we are reading // The width of the sequence inner bounds is 150, so 7.5 Text blocks should fit, putting @@ -988,7 +988,7 @@ TEST_F(BuilderPreserveScrollTest, VerticalWithPaddingAndSpacing) metrics.size(300,300); loadDocument(VERTICAL_WITH_PADDING_AND_SPACING); ASSERT_TRUE(component); - auto c = std::dynamic_pointer_cast(component); + auto c = CoreComponent::cast(component); // Access the "getProperty" method to check the positions we are reading ASSERT_TRUE(IsEqual(ObjectArray{0, 0}, c->getProperty("firstIndex"))); @@ -1095,7 +1095,7 @@ TEST_F(BuilderPreserveScrollTest, SwitchSequenceType) component->update(kUpdateScrollPosition, 25); // Move 25dp over ASSERT_EQ(25, component->getCalculated(kPropertyScrollPosition).asNumber()); - auto c = std::dynamic_pointer_cast(component); + auto c = CoreComponent::cast(component); ASSERT_TRUE(IsEqual(ObjectArray{"TEXT-C", +0.25}, c->getProperty("centerId"))); // When we switch to vertical two-column format, will try to put TEXT-B in the center, which is not possible @@ -1105,7 +1105,7 @@ TEST_F(BuilderPreserveScrollTest, SwitchSequenceType) // Move 25 dp. TEXT-E should now be the "centered" component component->update(kUpdateScrollPosition, 25); - c = std::dynamic_pointer_cast(component); + c = CoreComponent::cast(component); ASSERT_TRUE(IsEqual(ObjectArray{"TEXT-E", +0.25}, c->getProperty("centerId"))); // Switch back to horizontal diff --git a/unit/engine/unittest_builder_sequence.cpp b/unit/engine/unittest_builder_sequence.cpp index 4411c14..c5d0d66 100644 --- a/unit/engine/unittest_builder_sequence.cpp +++ b/unit/engine/unittest_builder_sequence.cpp @@ -125,13 +125,13 @@ TEST_F(BuilderTestSequence, SimpleHorizontalSequenceRTL) { loadDocument(SIMPLE_HORIZONTAL_SEQUENCE_RTL); advanceTime(10); - ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0,0,1024,800), component->getCalculated(kPropertyBounds).get()); ASSERT_EQ(kComponentTypeSequence, component->getType()); ASSERT_EQ(kScrollDirectionHorizontal, component->getCalculated(kPropertyScrollDirection).asInt()); for (int i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(624-400*i, 0, 400, 800), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(624-400*i, 0, 400, 800), child->getCalculated(kPropertyBounds).get()); } // Children @@ -391,7 +391,7 @@ TEST_F(BuilderTestSequence, LayoutCacheHorizontalRTL) ASSERT_TRUE(CheckChildrenLaidOut(component, Range(0, 5), true)); for (auto i = 0 ; i < component->getChildCount() ; i++) { auto child = component->getChildAt(i); - ASSERT_EQ(Rect(100 - 100*i, 0, 100, 800), child->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(100 - 100*i, 0, 100, 800), child->getCalculated(kPropertyBounds).get()); Rect rect; ASSERT_TRUE(child->getBoundsInParent(nullptr, rect)); ASSERT_EQ(Rect(100 - 100*i + 100, 0, 100, 800), rect); diff --git a/unit/engine/unittest_context.cpp b/unit/engine/unittest_context.cpp index a7cb3fe..3c85681 100644 --- a/unit/engine/unittest_context.cpp +++ b/unit/engine/unittest_context.cpp @@ -15,6 +15,8 @@ #include "../testeventloop.h" +#include "apl/primitives/functions.h" + using namespace apl; class ContextTest : public MemoryWrapper { @@ -76,7 +78,7 @@ TEST_F(ContextTest, Basic) EXPECT_EQ("green", viewport.get("theme").asString()); EXPECT_EQ(Object("tv"), viewport.get("mode")); - EXPECT_TRUE(c->opt("Math").get("asin").isFunction()); + EXPECT_TRUE(c->opt("Math").get("asin").is()); EXPECT_EQ(256, c->vhToDp(25)); EXPECT_EQ(128, c->vwToDp(12.5)); diff --git a/unit/engine/unittest_dependant.cpp b/unit/engine/unittest_dependant.cpp index 02912f1..6aea07e 100644 --- a/unit/engine/unittest_dependant.cpp +++ b/unit/engine/unittest_dependant.cpp @@ -542,7 +542,7 @@ TEST_F(DependantTest, Nested) loadDocument(NESTED); ASSERT_TRUE(component); - auto wrapper = std::dynamic_pointer_cast(component->findComponentById("TouchId")); + auto wrapper = TouchWrapperComponent::cast(component->findComponentById("TouchId")); ASSERT_TRUE(wrapper); auto text = component->findComponentById("TextId"); @@ -996,7 +996,7 @@ TEST_F(DependantTest, AVGDependency) loadDocument(document.c_str()); ASSERT_TRUE(component); - auto graphic = component->getCoreChildAt(0)->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCoreChildAt(0)->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_FALSE(root->hasEvent()); diff --git a/unit/engine/unittest_hover.cpp b/unit/engine/unittest_hover.cpp index fa7cbb0..8e7aff3 100644 --- a/unit/engine/unittest_hover.cpp +++ b/unit/engine/unittest_hover.cpp @@ -157,13 +157,13 @@ static const std::string DOCUMENT = class HoverTest : public DocumentWrapper { public: inline CoreComponentPtr getTouchWrapper(const RootContextPtr& root) const { - return std::static_pointer_cast(root->topComponent()); + return CoreComponent::cast(root->topComponent()); } inline CoreComponentPtr getFrame(const ComponentPtr& touchWrapper) const { - return std::static_pointer_cast(touchWrapper->getChildAt(0)); + return CoreComponent::cast(touchWrapper->getChildAt(0)); } inline CoreComponentPtr getText(const ComponentPtr& cmp) const { - return std::static_pointer_cast(cmp->getChildAt(0)); + return CoreComponent::cast(cmp->getChildAt(0)); } void init(const char *json) { @@ -249,8 +249,8 @@ class HoverTest : public DocumentWrapper { void printBounds(const std::string& name, const ComponentPtr& component) { std::cerr << "[ ] " << name << ": " << component.get() << std::endl; - std::cerr << "[ ]\tbounds " << component->getCalculated(kPropertyBounds).getRect().toString() << std::endl; - std::cerr << "[ ]\tinner bounds " << component->getCalculated(kPropertyInnerBounds).getRect().toString() << std::endl; + std::cerr << "[ ]\tbounds " << component->getCalculated(kPropertyBounds).get().toString() << std::endl; + std::cerr << "[ ]\tinner bounds " << component->getCalculated(kPropertyInnerBounds).get().toString() << std::endl; std::cerr << "[ ]\tglobal bounds " << component->getGlobalBounds().toString() << std::endl; } @@ -868,7 +868,7 @@ static const char *SEQUENCE_HORIZONTAL = TEST_F(HoverTest, SequenceHorizontal) { loadDocument(SEQUENCE_HORIZONTAL); - auto sequence = std::dynamic_pointer_cast(context->findComponentById("mySequence")); + auto sequence = SequenceComponent::cast(context->findComponentById("mySequence")); completeScroll(component, 1); ASSERT_EQ(sequence->scrollPosition(), Point(100.0, 0.0)); @@ -928,7 +928,7 @@ static const char *SEQUENCE_VERTICAL_PADDING = TEST_F(HoverTest, SequenceVerticalPadding) { loadDocument(SEQUENCE_VERTICAL_PADDING); - auto sequence = std::dynamic_pointer_cast(context->findComponentById("mySequence")); + auto sequence = SequenceComponent::cast(context->findComponentById("mySequence")); completeScroll(component, 1); ASSERT_EQ(sequence->scrollPosition(), Point(0.0, 200.0)); @@ -998,7 +998,7 @@ static const char *SEQUENCE_VERTICAL = TEST_F(HoverTest, SequenceVertical) { loadDocument(SEQUENCE_VERTICAL); - auto sequence = std::dynamic_pointer_cast(context->findComponentById("mySequence")); + auto sequence = SequenceComponent::cast(context->findComponentById("mySequence")); completeScroll(component, 1); ASSERT_EQ(sequence->scrollPosition(), Point(0.0, 300.0)); @@ -1070,7 +1070,7 @@ static const char *SEQUENCE_VERTICAL_PADDING_TEXT = TEST_F(HoverTest, SequenceVerticalPaddingText) { loadDocument(SEQUENCE_VERTICAL_PADDING_TEXT); - auto sequence = std::dynamic_pointer_cast(context->findComponentById("mySequence")); + auto sequence = SequenceComponent::cast(context->findComponentById("mySequence")); completeScroll(component, 1); ASSERT_EQ(sequence->scrollPosition(), Point(0.0, 200.0)); @@ -1214,7 +1214,7 @@ TEST_F(HoverTest, OnCursor_DisableState_Change) ASSERT_TRUE(component); ASSERT_EQ(2, component->getChildCount()); - auto text1 = std::static_pointer_cast(component->getChildAt(0)); + auto text1 = CoreComponent::cast(component->getChildAt(0)); auto& fm = root->context().hoverManager(); // Hover over the component @@ -1270,7 +1270,7 @@ TEST_F(HoverTest, CursorMove_DisabledComponent) ASSERT_TRUE(component); ASSERT_EQ(2, component->getChildCount()); - auto text1 = std::static_pointer_cast(component->getChildAt(0)); + auto text1 = CoreComponent::cast(component->getChildAt(0)); auto& fm = root->context().hoverManager(); // disable the component diff --git a/unit/engine/unittest_keyboard_manager.cpp b/unit/engine/unittest_keyboard_manager.cpp index b6d0a4d..1450a27 100644 --- a/unit/engine/unittest_keyboard_manager.cpp +++ b/unit/engine/unittest_keyboard_manager.cpp @@ -111,7 +111,7 @@ TEST_F(KeyboardManagerTest, ComponentWithFocus) { ASSERT_TRUE(root->isDirty()); // verify target component changed - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::BLUE), target->getCalculated(kPropertyBackgroundColor))); } @@ -144,7 +144,7 @@ TEST_F(KeyboardManagerTest, WhenIsTrue) { setFocus(component); // verify initial state of the command target component - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::RED), target->getCalculated(kPropertyBackgroundColor))); @@ -167,7 +167,7 @@ TEST_F(KeyboardManagerTest, WhenIsFalse) { setFocus(component); // verify initial state of the command target component - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::RED), target->getCalculated(kPropertyBackgroundColor))); @@ -245,7 +245,7 @@ TEST_F(KeyboardManagerTest, DocumentWhenIsTrue) { ASSERT_TRUE(root); // verify initial state of the command target component - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::RED), target->getCalculated(kPropertyBackgroundColor))); @@ -267,7 +267,7 @@ TEST_F(KeyboardManagerTest, DocumentWhenIsFalse) { ASSERT_TRUE(root); // verify initial state of the command target component - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::RED), target->getCalculated(kPropertyBackgroundColor))); @@ -393,8 +393,8 @@ TEST_F(KeyboardManagerTest, PropagateToParent) { loadDocument(PROPAGATE_KEY_HANDLER_DOC); ASSERT_TRUE(component); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -404,7 +404,7 @@ TEST_F(KeyboardManagerTest, PropagateToParent) { // verify key update propagated to top Component ASSERT_TRUE(root->isDirty()); - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::BLUE), target->getCalculated(kPropertyBackgroundColor))); } @@ -414,8 +414,8 @@ TEST_F(KeyboardManagerTest, PropagateBlock) { loadDocument(PROPAGATE_KEY_HANDLER_DOC); ASSERT_TRUE(component); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -425,7 +425,7 @@ TEST_F(KeyboardManagerTest, PropagateBlock) { ASSERT_FALSE(root->isDirty()); // verify the key was consumed, and no change in the target component - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_FALSE(root->isDirty()); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::RED), target->getCalculated(kPropertyBackgroundColor))); @@ -437,8 +437,8 @@ TEST_F(KeyboardManagerTest, PropagateToDocument) { loadDocument(PROPAGATE_KEY_HANDLER_DOC); ASSERT_TRUE(component); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -448,7 +448,7 @@ TEST_F(KeyboardManagerTest, PropagateToDocument) { // verify key update propagated to Document ASSERT_TRUE(root->isDirty()); - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); ASSERT_TRUE(IsEqual(Color(Color::GREEN), target->getCalculated(kPropertyBackgroundColor))); @@ -463,11 +463,11 @@ TEST_F(KeyboardManagerTest, Consumed) { loadDocument(PROPAGATE_KEY_HANDLER_DOC); ASSERT_TRUE(component); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); - auto target = std::dynamic_pointer_cast(root->context().findComponentById("testFrame")); + auto target = CoreComponent::cast(root->context().findComponentById("testFrame")); ASSERT_TRUE(target); // send an "No Key" keydown to touch wrapper with handler, expect not consumed @@ -1070,17 +1070,17 @@ TEST_F(KeyboardManagerTest, ArrowKeysForAvg) loadDocument(ARROW_KEYS_CONTROLLING_AVG); ASSERT_TRUE(component); - auto vg = std::static_pointer_cast(component->findComponentById("vg")); + auto vg = CoreComponent::cast(component->findComponentById("vg")); ASSERT_EQ(kComponentTypeVectorGraphic, vg->getType()); auto event = root->popEvent(); ASSERT_EQ(kEventTypeFocus, event.getType()); ASSERT_EQ(vg, event.getComponent()); - auto group = vg->getCalculated(kPropertyGraphic).getGraphic()->getRoot()->getChildAt(1); + auto group = vg->getCalculated(kPropertyGraphic).get()->getRoot()->getChildAt(1); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); - auto transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + auto transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(150, 150), transform); @@ -1091,7 +1091,7 @@ TEST_F(KeyboardManagerTest, ArrowKeysForAvg) ASSERT_TRUE(root->handleKeyboard(kKeyDown, A_KEY)); ASSERT_TRUE(root->handleKeyboard(kKeyDown, S_KEY)); - transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(200, 100), transform); ASSERT_TRUE(root->handleKeyboard(kKeyDown, Keyboard::ARROW_RIGHT_KEY())); @@ -1101,16 +1101,16 @@ TEST_F(KeyboardManagerTest, ArrowKeysForAvg) ASSERT_TRUE(root->handleKeyboard(kKeyDown, Keyboard::ARROW_LEFT_KEY())); ASSERT_TRUE(root->handleKeyboard(kKeyDown, Keyboard::ARROW_DOWN_KEY())); - transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(250, 50), transform); ASSERT_TRUE(root->handleKeyboard(kKeyDown, Keyboard::ARROW_RIGHT_KEY())); - transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(300, 50), transform); // Actually passed to focus nav ASSERT_TRUE(root->handleKeyboard(kKeyDown, Keyboard::ARROW_RIGHT_KEY())); - transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(300, 50), transform); ASSERT_TRUE(root->hasEvent()); event = root->popEvent(); diff --git a/unit/engine/unittest_layouts.cpp b/unit/engine/unittest_layouts.cpp index 3b02e40..bf26cb0 100644 --- a/unit/engine/unittest_layouts.cpp +++ b/unit/engine/unittest_layouts.cpp @@ -472,7 +472,7 @@ TEST_F(LayoutTest, TypedLayoutParameterDefault) ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("150dp", text->getCalculated(kPropertyText).asString()); - ASSERT_EQ(Rect(0, 0, 300, 300), text->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 300, 300), text->getCalculated(kPropertyBounds).get()); } const char *DIMENSION_PARAMETER = @@ -524,5 +524,5 @@ TEST_F(LayoutTest, TypedLayoutParameter) ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("100dp", text->getCalculated(kPropertyText).asString()); - ASSERT_EQ(Rect(0, 0, 200, 200), text->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 200, 200), text->getCalculated(kPropertyBounds).get()); } diff --git a/unit/engine/unittest_resources.cpp b/unit/engine/unittest_resources.cpp index 686a56c..9a98fc2 100644 --- a/unit/engine/unittest_resources.cpp +++ b/unit/engine/unittest_resources.cpp @@ -358,9 +358,9 @@ TEST_F(ResourceTest, LinearGradient) ASSERT_EQ(1, root->info().resources().size()); auto object = context->opt("@myLinear"); - ASSERT_TRUE(object.isGradient()); + ASSERT_TRUE(object.is()); - auto& grad = object.getGradient(); + auto& grad = object.get(); ASSERT_EQ(Gradient::LINEAR, grad.getType()); ASSERT_EQ(Object(0), grad.getProperty(kGradientPropertyAngle)); @@ -410,9 +410,9 @@ TEST_F(ResourceTest, RadialGradient) ASSERT_EQ(1, root->info().resources().size()); auto object = context->opt("@myRadial"); - ASSERT_TRUE(object.isGradient()); + ASSERT_TRUE(object.is()); - auto& grad = object.getGradient(); + auto& grad = object.get(); ASSERT_EQ(Gradient::RADIAL, grad.getType()); auto colorRange = grad.getProperty(kGradientPropertyColorRange); @@ -472,9 +472,9 @@ TEST_F(ResourceTest, RichLinearGradient) loadDocument(RICH_LINEAR); auto object = context->opt("@myLinear"); - ASSERT_TRUE(object.isGradient()); + ASSERT_TRUE(object.is()); - auto& grad = object.getGradient(); + auto& grad = object.get(); ASSERT_EQ(Gradient::LINEAR, grad.getType()); ASSERT_EQ(Object(45), grad.getProperty(kGradientPropertyAngle)); @@ -609,9 +609,9 @@ TEST_F(ResourceTest, GradientAngle) for (auto& t : GRADIENT_ANGLE_TESTS) { LOG(LogLevel::kWarn) << t.at(0).toDebugString(); auto object = context->opt(t.at(0).asString()); - ASSERT_TRUE(object.isGradient()); + ASSERT_TRUE(object.is()); - auto& grad = object.getGradient(); + auto& grad = object.get(); ASSERT_EQ(Gradient::LINEAR, grad.getType()); auto colorRange = grad.getProperty(kGradientPropertyColorRange); @@ -663,9 +663,9 @@ TEST_F(ResourceTest, GradientRadialFull) loadDocument(GRADIENT_RADIAL_FULL); auto object = context->opt("@rad"); - ASSERT_TRUE(object.isGradient()); + ASSERT_TRUE(object.is()); - auto& grad = object.getGradient(); + auto& grad = object.get(); ASSERT_EQ(Gradient::RADIAL, grad.getType()); auto colorRange = grad.getProperty(kGradientPropertyColorRange); @@ -705,9 +705,9 @@ TEST_F(ResourceTest, Easing) { loadDocument(EASING); auto object = context->opt("@jagged"); - ASSERT_TRUE(object.isEasing()); + ASSERT_TRUE(object.is()); - auto easing = object.getEasing(); + auto easing = object.get(); ASSERT_EQ(0.75, easing->calc(0.25)); ASSERT_TRUE(IsEqual("0.75", component->getCalculated(kPropertyText).asString())); diff --git a/unit/engine/unittest_styles.cpp b/unit/engine/unittest_styles.cpp index b2a3056..16142c7 100644 --- a/unit/engine/unittest_styles.cpp +++ b/unit/engine/unittest_styles.cpp @@ -156,7 +156,7 @@ TEST_F(StylesTest, Basic) ASSERT_EQ(2, base->size()); ASSERT_TRUE(base->find("fontFamily") != base->end()); ASSERT_EQ(Object("Amazon Ember"), base->at("fontFamily")); - ASSERT_TRUE(base->at("color").isColor()); + ASSERT_TRUE(base->at("color").is()); ASSERT_EQ(0xf0f1efff, base->at("color").getColor()); // Sanity check that path values match rapidjson Pointer architecture diff --git a/unit/extension/unittest_extension_client.cpp b/unit/extension/unittest_extension_client.cpp index bcbedfb..ae0230b 100644 --- a/unit/extension/unittest_extension_client.cpp +++ b/unit/extension/unittest_extension_client.cpp @@ -44,7 +44,7 @@ class ExtensionClientTest : public DocumentWrapper { if (root) { context = root->contextPtr(); ASSERT_TRUE(context); - component = std::dynamic_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); } } @@ -715,8 +715,8 @@ TEST_F(ExtensionClientTest, ExtensionParseEventDataBindings) { ASSERT_EQ(2, liveDataMap.size()); auto arr = liveDataMap.at("entityList"); auto map = liveDataMap.at("deviceState"); - ASSERT_EQ(Object::ObjectType::kArrayType, arr->getType()); - ASSERT_EQ(Object::ObjectType::kMapType, map->getType()); + ASSERT_EQ(LiveObject::ObjectType::kArrayType, arr->getType()); + ASSERT_EQ(LiveObject::ObjectType::kMapType, map->getType()); } @@ -2314,8 +2314,8 @@ TEST_F(ExtensionClientTest, TypeWithoutPropertis) { auto liveDataMap = configPtr->getLiveObjectMap(); ASSERT_EQ(1, liveDataMap.size()); auto& map = liveDataMap.at("MyWeather"); - ASSERT_EQ(Object::ObjectType::kMapType, map->getType()); - auto liveMap = std::dynamic_pointer_cast(map); + ASSERT_EQ(LiveObject::ObjectType::kMapType, map->getType()); + auto liveMap = std::static_pointer_cast(map); ASSERT_EQ(0, liveMap->getMap().size()); // Inflate the doc @@ -2392,7 +2392,7 @@ TEST_F(ExtensionClientTest, ComponentRequestWithSuccessResponse) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extensionComponent = std::dynamic_pointer_cast(component); + auto extensionComponent = ExtensionComponent::cast(component); auto componentRequest = client->createComponentChange(doc.GetAllocator(), *extensionComponent); ASSERT_TRUE(std::string("1.0").compare(componentRequest["version"].GetString()) <= 0); @@ -2438,7 +2438,7 @@ TEST_F(ExtensionClientTest, ComponentRequestOnWrongClient) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extensionComponent = std::dynamic_pointer_cast(component); + auto extensionComponent = ExtensionComponent::cast(component); auto componentRequest = client2->createComponentChange(doc.GetAllocator(), *extensionComponent); ASSERT_TRUE(ConsoleMessage()); ASSERT_EQ(rapidjson::Value(rapidjson::kNullType), componentRequest); @@ -2453,7 +2453,7 @@ TEST_F(ExtensionClientTest, ComponentRequestWithSuccessResponseButInvalidID) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extensionComponent = std::dynamic_pointer_cast(component); + auto extensionComponent = ExtensionComponent::cast(component); auto componentRequest = client->createComponentChange(doc.GetAllocator(), *extensionComponent); ASSERT_TRUE(std::string("1.0").compare(componentRequest["version"].GetString()) <= 0); @@ -2480,7 +2480,7 @@ TEST_F(ExtensionClientTest, ComponentRequestWithFailedResponse) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extensionComponent = std::dynamic_pointer_cast(component); + auto extensionComponent = ExtensionComponent::cast(component); auto componentRequest = client->createComponentChange(doc.GetAllocator(), *extensionComponent); ASSERT_TRUE(std::string("1.0").compare(componentRequest["version"].GetString()) <= 0); @@ -2509,7 +2509,7 @@ TEST_F(ExtensionClientTest, ComponentRelease) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extensionComponent = std::dynamic_pointer_cast(component); + auto extensionComponent = ExtensionComponent::cast(component); extensionComponent->updateResourceState(kResourceReleased); auto componentRelease = client->createComponentChange(doc.GetAllocator(), *extensionComponent); @@ -2719,7 +2719,7 @@ TEST_F(ExtensionClientTest, ExtensionComponentCommandAndEvent) { auto extensionComponent = component->findComponentById("DrawArea"); ASSERT_EQ(extensionComponent->getType(), kComponentTypeExtension); - auto extnComp = std::dynamic_pointer_cast(extensionComponent); + auto extnComp = ExtensionComponent::cast(extensionComponent); ASSERT_NE(extnComp, nullptr); // Runtime needs to redirect this events to the server. @@ -2747,7 +2747,7 @@ TEST_F(ExtensionClientTest, ExtensionComponentProperty) { auto extensionComponent = component->findComponentById("DrawArea"); ASSERT_EQ(extensionComponent->getType(), kComponentTypeExtension); - auto extnComp = std::dynamic_pointer_cast(extensionComponent); + auto extnComp = ExtensionComponent::cast(extensionComponent); ASSERT_NE(extnComp, nullptr); // The pending message should trigger a componentUpdate @@ -2802,13 +2802,13 @@ TEST_F(ExtensionClientTest, ExtensionComponentKPropOutProperty) { initializeContext(); auto alexaButton = component->findComponentById("AlexaButton"); - auto extensionComponent = std::dynamic_pointer_cast(component->findComponentById("DrawArea")); + auto extensionComponent = CoreComponent::cast(component->findComponentById("DrawArea")); ASSERT_EQ(extensionComponent->getType(), kComponentTypeExtension); extensionComponent->setProperty(kPropertyDisplay, "none"); extensionComponent->updateResourceState(kResourceReady); - auto extnComp = dynamic_cast(extensionComponent.get()); + auto extnComp = ExtensionComponent::cast(extensionComponent); ASSERT_NE(extnComp, nullptr); // A dirty property in the extension component should trigger a componentUpdate @@ -2861,7 +2861,7 @@ TEST_F(ExtensionClientTest, ExtensionComponentInvalidEventHandlerInvoke) { ASSERT_EQ(component->getType(), kComponentTypeExtension); - auto extnComp = std::dynamic_pointer_cast(component); + auto extnComp = ExtensionComponent::cast(component); ASSERT_NE(extnComp, nullptr); std::string extensionEvent = EXT_COMPONENT_EVENT_HEADER; @@ -2882,7 +2882,7 @@ TEST_F(ExtensionClientTest, ExtensionComponentInvalidComponentInvoke) { auto extensionComponent = component->findComponentById("DrawArea"); ASSERT_EQ(extensionComponent->getType(), kComponentTypeExtension); - auto extnComp = std::dynamic_pointer_cast(extensionComponent); + auto extnComp = ExtensionComponent::cast(extensionComponent); ASSERT_NE(extnComp, nullptr); std::string extensionEvent = EXT_COMPONENT_EVENT_HEADER; @@ -2903,7 +2903,7 @@ TEST_F(ExtensionClientTest, ExtensionClientDisconnection) { auto extensionComponent = component->findComponentById("DrawArea"); ASSERT_EQ(extensionComponent->getType(), kComponentTypeExtension); - auto extnComp = std::dynamic_pointer_cast(extensionComponent); + auto extnComp = ExtensionComponent::cast(extensionComponent); ASSERT_NE(extnComp, nullptr); ASSERT_TRUE(client->handleDisconnection(root, 500, "Service not available")); diff --git a/unit/extension/unittest_extension_component.cpp b/unit/extension/unittest_extension_component.cpp index b61cb27..f8bbe8f 100644 --- a/unit/extension/unittest_extension_component.cpp +++ b/unit/extension/unittest_extension_component.cpp @@ -231,7 +231,7 @@ TEST_F(ExtensionComponentTest, NamedExtensionComponent) { ASSERT_TRUE(component); ASSERT_EQ(kComponentTypeExtension, component->getType()); - auto extensionComponent = dynamic_cast(component.get()); + auto extensionComponent = ExtensionComponent::cast(component); ASSERT_STREQ(extensionComponent->getUri().c_str(), "aplext:hello:10"); ASSERT_STREQ(extensionComponent->name().c_str(), "Display"); auto resId = extensionComponent->getResourceID(); @@ -276,7 +276,7 @@ TEST_F(ExtensionComponentTest, ComponentProperties) { ASSERT_TRUE(component); ASSERT_EQ(kComponentTypeExtension, component->getType()); - auto extensionComponent = dynamic_cast(component.get()); + auto extensionComponent = ExtensionComponent::cast(component); ASSERT_STREQ(extensionComponent->getUri().c_str(), "aplext:hello:10"); ASSERT_STREQ(extensionComponent->name().c_str(), "Display"); @@ -460,7 +460,7 @@ TEST_F(ExtensionComponentTest, ExtensionError) { ASSERT_FALSE(ConsoleMessage()); ASSERT_TRUE(IsEqual(kResourcePending, component->getCalculated(kPropertyResourceState))); - auto extensionComponent = dynamic_cast(component.get()); + auto extensionComponent = ExtensionComponent::cast(component); extensionComponent->extensionComponentFail(42, "extension failure"); ASSERT_FALSE(ConsoleMessage()); ASSERT_TRUE(CheckDirty(component, kPropertyResourceState)); diff --git a/unit/extension/unittest_extension_mediator.cpp b/unit/extension/unittest_extension_mediator.cpp index bb71599..b6da172 100644 --- a/unit/extension/unittest_extension_mediator.cpp +++ b/unit/extension/unittest_extension_mediator.cpp @@ -425,7 +425,7 @@ class LifecycleTestExtension : public alexaext::ExtensionBase, prefixByActivity.emplace(activity, prefix); } - rapidjson::Document response = RegistrationSuccess("1.0") + return RegistrationSuccess("1.0") .uri(uri) .token(useAutoToken ? "" : TOKEN) .schema("1.0", [uri, prefix](ExtensionSchema schema) { @@ -443,19 +443,9 @@ class LifecycleTestExtension : public alexaext::ExtensionBase, }) .liveDataArray(prefix + "liveArray", [](LiveDataSchema &liveDataSchema) { liveDataSchema.dataType("liveArraySchema"); - }); + }) + .component("Component"); }); - - // The schema API doesn't support component definitions yet, so we amend the response - // directly here instead - rapidjson::Value component; - component.SetObject(); - component.AddMember("name", "Component", response.GetAllocator()); - rapidjson::Value components; - components.SetArray(); - components.PushBack(component, response.GetAllocator()); - response["schema"].AddMember("components", components, response.GetAllocator()); - return response; } void onSessionStarted(const SessionDescriptor& session) override { @@ -967,8 +957,8 @@ TEST_F(ExtensionMediatorTest, ExtensionParseEventDataBindings) { ASSERT_EQ(2, liveDataMap.size()); auto arr = liveDataMap.at("entityList"); auto map = liveDataMap.at("deviceState"); - ASSERT_EQ(Object::ObjectType::kArrayType, arr->getType()); - ASSERT_EQ(Object::ObjectType::kMapType, map->getType()); + ASSERT_EQ(LiveObject::ObjectType::kArrayType, arr->getType()); + ASSERT_EQ(LiveObject::ObjectType::kMapType, map->getType()); } @@ -1444,8 +1434,8 @@ TEST_F(ExtensionMediatorTest, AudioPlayerIntegration) { ASSERT_TRUE(touch); // Basic data is loaded - ASSERT_TRUE(IsEqual("PLAYING", activityText->getCalculated(kPropertyText).getStyledText().getText())); - ASSERT_TRUE(IsEqual("123", activityOffset->getCalculated(kPropertyText).getStyledText().getText())); + ASSERT_TRUE(IsEqual("PLAYING", activityText->getCalculated(kPropertyText).get().getText())); + ASSERT_TRUE(IsEqual("123", activityOffset->getCalculated(kPropertyText).get().getText())); } class SimpleExtensionTestAdapter : public ExtensionBase { @@ -3679,4 +3669,232 @@ TEST_F(ExtensionMediatorTest, LifecycleAPIsRespectExtensionToken) { ASSERT_TRUE(CheckSendEvent(root, "ExtensionReadyReceived")); } +class ComponentExtension : public alexaext::ExtensionBase { +public: + static const char* URI; + + explicit ComponentExtension() : ExtensionBase(URI), + mActivityDescriptor(URI, nullptr, "") {}; + + rapidjson::Document createRegistration(const ActivityDescriptor& activity, + const rapidjson::Value& registrationRequest) override { + mActivityDescriptor = activity; + const auto& uri = activity.getURI(); + return RegistrationSuccess("1.0") + .uri(uri) + .token("") + .schema("1.0", [uri](ExtensionSchema schema) { + schema + .uri(uri) + .component("Simple") + .component("ResourceType", [](ComponentSchema componentSchema) { + componentSchema + .resourceType("SURFACE") + .context("video"); + }) + .component("Properties", [](ComponentSchema componentSchema) { + componentSchema + .property("propA", "bool") + .property("propB", [](TypePropertySchema propertySchema) { + propertySchema + .type("number") + .required(true); + }) + .property("propC", [](TypePropertySchema propertySchema) { + propertySchema + .type("string") + .defaultValue("George"); + }); + }) + .component("Events", [](ComponentSchema componentSchema) { + componentSchema + .event("EventA") + .event("EventB", [](EventSchema eventSchema) { + eventSchema.fastMode(false); + }); + }); + }); + } + + void onResourceReady(const ActivityDescriptor& activity, + const ResourceHolderPtr& resourceHolder) override { + mResourceIds.emplace_back(resourceHolder->resourceId()); + } + + void invokeEvent(const rapidjson::Value& event) { + invokeExtensionEventHandler(mActivityDescriptor, event); + } + + std::string + getMessage(const std::string& resourceId) { + auto it = mPayloads.find(resourceId); + if (it != mPayloads.end()) { + return it->second; + } + return ""; + } + +protected: + bool updateComponent(const ActivityDescriptor& activity, + const rapidjson::Value& command) override { + auto resourceId = command["resourceId"].GetString(); + rapidjson::Document document(rapidjson::kObjectType); + document.CopyFrom(command, document.GetAllocator()); + rapidjson::Value* payload = rapidjson::Pointer("/payload").Get(document); + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + payload->Accept(writer); + mPayloads.insert({resourceId, sb.GetString()}); + return true; + } + +private: + std::vector mResourceIds; + std::map mPayloads; + ActivityDescriptor mActivityDescriptor; +}; + +const char* ComponentExtension::URI = "test:component:1.0"; + +const char* COMPONENT_DOC = R"({ +"type": "APL", +"version": "1.9", +"theme": "dark", +"extensions": [ + { + "uri": "test:component:1.0", + "name": "Component" + } +], +"mainTemplate": { + "item": { + "type": "Container", + "width": "100%", + "height": "100%", + "items": [ + { + "type": "Component:Simple", + "id": "simple", + "width": 100, + "height": 100 + }, + { + "type": "Component:ResourceType", + "id": "resourceType", + "width": 100, + "height": 100, + "entities": [ "foo" ] + }, + { + "type": "Component:Properties", + "id": "properties", + "width": 100, + "height": 100, + "propA": true, + "propB": 42 + }, + { + "type": "Component:Events", + "id": "events", + "width": 100, + "height": 100, + "EventA": { + "type": "SetValue", + "property": "disabled", + "value": true + }, + "EventB": { + "type": "SendEvent", + "arguments": [ "do it" ] + } + } + ] + } +} +})"; + +TEST_F(ExtensionMediatorTest, ExtensionComponentSchema) { + auto session = ExtensionSession::create(); + + extensionProvider = std::make_shared(); + resourceProvider = std::make_shared(); + mediator = ExtensionMediator::create(extensionProvider, + resourceProvider, + alexaext::Executor::getSynchronousExecutor(), + session); + + auto extension = std::make_shared(); + auto proxy = std::make_shared(extension); + extensionProvider->registerExtension(proxy); + + createContent(COMPONENT_DOC, nullptr); + + config->enableExperimentalFeature(RootConfig::kExperimentalFeatureExtensionProvider) + .extensionProvider(extensionProvider) + .extensionMediator(mediator); + ASSERT_TRUE(content->isReady()); + mediator->initializeExtensions(config, content); + mediator->loadExtensions(config, content); + + inflate(); + + auto simpleComponent = root->findComponentById("simple"); + ASSERT_TRUE(simpleComponent); + + // Check resource type + auto resourceTypeComponent = root->findComponentById("resourceType"); + ASSERT_TRUE(resourceTypeComponent); + ASSERT_EQ("SURFACE", resourceTypeComponent->getCalculated(apl::kPropertyResourceType).asString()); + + // Check video type is reported in context + rapidjson::Document context(rapidjson::kObjectType); + auto visualContext = root->serializeVisualContext(context.GetAllocator()); + auto children = visualContext.FindMember("children")->value.GetArray(); + auto child = children[0].GetObject(); + ASSERT_EQ("resourceType", std::string(child["id"].GetString())); + ASSERT_EQ("video", std::string(child["type"].GetString())); + + + // Check custom properties (passed in payload of Component message) + auto propertiesComponent = root->findComponentById("properties"); + ASSERT_TRUE(propertiesComponent); + auto payload = extension->getMessage(propertiesComponent->getCalculated(apl::kPropertyResourceId).asString()); + rapidjson::Document document; + document.Parse(payload.c_str()); + ASSERT_EQ(true, document["propA"].GetBool()); + ASSERT_EQ(42.0, document["propB"].GetDouble()); + ASSERT_EQ("George", std::string(document["propC"].GetString())); + + // Check component events + auto eventsComponent = root->findComponentById("events"); + auto resourceId = eventsComponent->getCalculated(apl::kPropertyResourceId).asString(); + ASSERT_TRUE(eventsComponent); + + ASSERT_EQ(false, eventsComponent->getCalculated(apl::kPropertyDisabled).asBoolean()); + + auto eventA = alexaext::Event("1.0") + .uri(ComponentExtension::URI) + .target(ComponentExtension::URI) + .resourceId(resourceId) + .name("EventA"); + extension->invokeEvent(eventA); + root->updateTime(1); + root->clearPending(); + + ASSERT_EQ(true, eventsComponent->getCalculated(apl::kPropertyDisabled).asBoolean()); + + auto eventB = alexaext::Event("1.0") + .uri(ComponentExtension::URI) + .target(ComponentExtension::URI) + .resourceId(resourceId) + .name("EventB"); + extension->invokeEvent(eventB); + root->updateTime(1); + root->clearPending(); + auto sendEvent = root->popEvent(); + ASSERT_EQ(apl::kEventTypeSendEvent, sendEvent.getType()); + auto args = sendEvent.getValue(apl::kEventPropertyArguments).getArray(); + ASSERT_EQ("do it", args[0].asString()); +} + #endif \ No newline at end of file diff --git a/unit/extension/unittest_requested_extension.cpp b/unit/extension/unittest_requested_extension.cpp index afa0626..2b25fbf 100644 --- a/unit/extension/unittest_requested_extension.cpp +++ b/unit/extension/unittest_requested_extension.cpp @@ -450,7 +450,7 @@ TEST_F(RequestedExtensionTest, ExtensionWithSimpleConfig) { // verify config and environment for boolean auto c = config->getExtensionEnvironment("_URIXcolor"); - ASSERT_TRUE(c.isColor()); + ASSERT_TRUE(c.is()); ASSERT_EQ(Color::BLUE, c.getColor()); ASSERT_TRUE(IsEqual(Color(Color::BLUE), evaluate(*context, "${environment.extension.Xcolor}"))); } diff --git a/unit/focus/unittest_focus_manager.cpp b/unit/focus/unittest_focus_manager.cpp index 1c73838..8385d71 100644 --- a/unit/focus/unittest_focus_manager.cpp +++ b/unit/focus/unittest_focus_manager.cpp @@ -51,8 +51,8 @@ static const char *FOCUS_TEST = R"({ TEST_F(FocusManagerTest, ManualControl) { loadDocument(FOCUS_TEST); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -106,8 +106,8 @@ TEST_F(FocusManagerTest, ManualControl) TEST_F(FocusManagerTest, ManualControlDontNotifyViewhost) { loadDocument(FOCUS_TEST); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -139,8 +139,8 @@ TEST_F(FocusManagerTest, ManualControlDontNotifyViewhost) TEST_F(FocusManagerTest, ClearCheck) { loadDocument(FOCUS_TEST); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -220,8 +220,8 @@ TEST_F(FocusManagerTest, BlurFocus) { loadDocument(BLUR_FOCUS); - auto thing1 = std::dynamic_pointer_cast(root->context().findComponentById("thing1")); - auto thing2 = std::dynamic_pointer_cast(root->context().findComponentById("thing2")); + auto thing1 = CoreComponent::cast(root->context().findComponentById("thing1")); + auto thing2 = CoreComponent::cast(root->context().findComponentById("thing2")); ASSERT_TRUE(thing1); ASSERT_TRUE(thing2); @@ -479,7 +479,7 @@ TEST_F(FocusManagerTest, FocusOnComponentType) // Set focus using the "update" method for (const auto& m : sCanFocus) { - auto component = std::static_pointer_cast(root->context().findComponentById(m.first)); + auto component = CoreComponent::cast(root->context().findComponentById(m.first)); ASSERT_TRUE(component) << m.first; fm.clearFocus(false); @@ -497,7 +497,7 @@ TEST_F(FocusManagerTest, FocusOnComponentType) // Set focus using a command for (const auto& m : sCanFocus) { - auto component = std::static_pointer_cast(root->context().findComponentById(m.first)); + auto component = CoreComponent::cast(root->context().findComponentById(m.first)); ASSERT_TRUE(component) << m.first; fm.clearFocus(false); @@ -520,7 +520,7 @@ TEST_F(FocusManagerTest, FocusOnComponentType) // Now disable all of the components and verify they do not take focus for (const auto& m : sCanFocus) { - auto component = std::static_pointer_cast(root->context().findComponentById(m.first)); + auto component = CoreComponent::cast(root->context().findComponentById(m.first)); ASSERT_TRUE(component) << m.first; fm.clearFocus(false); @@ -595,8 +595,8 @@ TEST_F(FocusManagerTest, FocusWithInheritParentState) loadDocument(INHERIT_PARENT_STATE); auto text = root->context().findComponentById("MyText"); - auto a = std::static_pointer_cast(root->context().findComponentById("TouchWrapperA")); - auto b = std::static_pointer_cast(root->context().findComponentById("TouchWrapperB")); + auto a = CoreComponent::cast(root->context().findComponentById("TouchWrapperA")); + auto b = CoreComponent::cast(root->context().findComponentById("TouchWrapperB")); ASSERT_TRUE(text); ASSERT_TRUE(a); diff --git a/unit/focus/unittest_native_focus.cpp b/unit/focus/unittest_native_focus.cpp index 1c1d6c6..d980bf1 100644 --- a/unit/focus/unittest_native_focus.cpp +++ b/unit/focus/unittest_native_focus.cpp @@ -53,7 +53,7 @@ class NativeFocusTest : public DocumentWrapper { eventGlobalBoundsEqual(const ComponentPtr& ptr, const Event& event) { Rect expectedBounds; ptr->getBoundsInParent(component, expectedBounds); - auto eventBounds = event.getValue(kEventPropertyValue).getRect(); + auto eventBounds = event.getValue(kEventPropertyValue).get(); if (expectedBounds != eventBounds) { return ::testing::AssertionFailure() << "Reported bounds. " @@ -4864,7 +4864,7 @@ TEST_F(NativeFocusTest, RuntimeAPIFocusSimple) auto& fm = root->context().focusManager(); // Let's say we want to focus 21 - auto child = std::dynamic_pointer_cast(root->findComponentById("21")); + auto child = CoreComponent::cast(root->findComponentById("21")); auto result = root->setFocus( FocusDirection::kFocusDirectionRight, Rect(-100, 100, 100, 100), @@ -4885,7 +4885,7 @@ TEST_F(NativeFocusTest, RuntimeAPIFocusablesParentPager) TEST_F(NativeFocusTest, RuntimeAPIFocusParentPager) { loadDocument(TOUCHABLE_PAGER); - auto child = std::dynamic_pointer_cast(root->findComponentById("pager")); + auto child = CoreComponent::cast(root->findComponentById("pager")); executeCommand("SetPage", {{"componentId", "pager"}, {"position", "relative"}, {"value", 1}}, false); advanceTime(1000); @@ -4898,7 +4898,7 @@ TEST_F(NativeFocusTest, RuntimeAPIFocusParentPager) child->getUniqueId()); ASSERT_TRUE(result); - child = std::dynamic_pointer_cast(root->findComponentById("1")); + child = CoreComponent::cast(root->findComponentById("1")); ASSERT_EQ(child, fm.getFocus()); ASSERT_TRUE(verifyFocusSwitchEvent(child, root->popEvent())); } @@ -4915,14 +4915,14 @@ TEST_F(NativeFocusTest, RuntimeAPIFocusParentSequence) loadDocument(SEQUENCE_WITH_TOUCHABLES); auto& fm = root->context().focusManager(); - auto child = std::dynamic_pointer_cast(root->findComponentById("scrollable")); + auto child = CoreComponent::cast(root->findComponentById("scrollable")); auto result = root->setFocus( FocusDirection::kFocusDirectionRight, Rect(-100, 100, 100, 100), child->getUniqueId()); ASSERT_TRUE(result); - child = std::dynamic_pointer_cast(root->findComponentById("0")); + child = CoreComponent::cast(root->findComponentById("0")); LOG(LogLevel::kWarn) << fm.getFocus()->getId(); ASSERT_EQ(child, fm.getFocus()); ASSERT_TRUE(verifyFocusSwitchEvent(child, root->popEvent())); diff --git a/unit/graphic/unittest_dependant_graphic.cpp b/unit/graphic/unittest_dependant_graphic.cpp index aeb9474..e316186 100644 --- a/unit/graphic/unittest_dependant_graphic.cpp +++ b/unit/graphic/unittest_dependant_graphic.cpp @@ -57,7 +57,7 @@ TEST_F(DependantGraphicTest, Simple) ASSERT_TRUE(component); // Verify that the graphic was created and that the color is blue - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto box = graphic->getRoot(); @@ -129,7 +129,7 @@ TEST_F(DependantGraphicTest, Binding) ASSERT_TRUE(CheckDirty(component)); // Verify that the graphic was created - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_TRUE(CheckDirty(graphic)); @@ -226,7 +226,7 @@ TEST_F(DependantGraphicTest, ManyBindings) auto vg = component->getChildAt(0); // Verify that the graphic was created and that the color is blue - auto graphic = vg->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = vg->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_TRUE(CheckDirty(graphic)); @@ -322,12 +322,12 @@ TEST_F(DependantGraphicTest, Transformed) { loadDocument(TRANSFORMED_DOC); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto group = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); - auto transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + auto transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::translate(-36, 45.5), transform); auto path = group->getChildAt(0); @@ -336,11 +336,11 @@ TEST_F(DependantGraphicTest, Transformed) auto fill = path->getValue(kGraphicPropertyFill); ASSERT_EQ(Color::GREEN, fill.getColor()); - auto fillTransform = path->getValue(kGraphicPropertyFillTransform).getTransform2D(); + auto fillTransform = path->getValue(kGraphicPropertyFillTransform).get(); ASSERT_EQ(Transform2D::skewX(40), fillTransform); - ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).isColor()); - auto strokeTransform = path->getValue(kGraphicPropertyStrokeTransform).getTransform2D(); + ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).is()); + auto strokeTransform = path->getValue(kGraphicPropertyStrokeTransform).get(); ASSERT_EQ(Transform2D::scale(0.7, 0.5), strokeTransform); executeCommand("SetValue", {{"componentId", "gc"}, @@ -351,10 +351,10 @@ TEST_F(DependantGraphicTest, Transformed) {"property", "fillSkew"}, {"value", 7}}, true); - transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + transform = group->getValue(kGraphicPropertyTransform).get(); ASSERT_EQ(Transform2D::scale(0.7, 0.5), transform); - fillTransform = path->getValue(kGraphicPropertyFillTransform).getTransform2D(); + fillTransform = path->getValue(kGraphicPropertyFillTransform).get(); ASSERT_EQ(Transform2D::skewX(7), fillTransform); } @@ -412,7 +412,7 @@ TEST_F(DependantGraphicTest, ChangingGradient) { loadDocument(CHANGING_GRADIENT); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto group = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); @@ -421,15 +421,15 @@ TEST_F(DependantGraphicTest, ChangingGradient) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto pathGrad = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(pathGrad.isGradient()); - ASSERT_EQ(Object(Color(Color::RED)), pathGrad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(pathGrad.is()); + ASSERT_EQ(Object(Color(Color::RED)), pathGrad.get().getProperty(kGradientPropertyColorRange).at(0)); auto text = group->getChildAt(1); ASSERT_EQ(kGraphicElementTypeText, text->getType()); auto textGrad = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(textGrad.isGradient()); - ASSERT_EQ(Object(Color(Color::RED)), textGrad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(textGrad.is()); + ASSERT_EQ(Object(Color(Color::RED)), textGrad.get().getProperty(kGradientPropertyColorRange).at(0)); executeCommand("SetValue", {{"componentId", "gc"}, {"property", "gradientColor"}, @@ -439,12 +439,12 @@ TEST_F(DependantGraphicTest, ChangingGradient) ASSERT_TRUE(CheckDirty(text, kGraphicPropertyStroke)); pathGrad = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(pathGrad.isGradient()); - ASSERT_EQ(Object(Color(Color::GREEN)), pathGrad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(pathGrad.is()); + ASSERT_EQ(Object(Color(Color::GREEN)), pathGrad.get().getProperty(kGradientPropertyColorRange).at(0)); textGrad = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(textGrad.isGradient()); - ASSERT_EQ(Object(Color(Color::GREEN)), textGrad.getGradient().getProperty(kGradientPropertyColorRange).at(0)); + ASSERT_TRUE(textGrad.is()); + ASSERT_EQ(Object(Color(Color::GREEN)), textGrad.get().getProperty(kGradientPropertyColorRange).at(0)); } static const char *STROKE_VARIATION_TEST = R"apl( @@ -494,7 +494,7 @@ static const char *STROKE_VARIATION_TEST = R"apl( // Test that the stroke dash array can be dynamically updated TEST_F(DependantGraphicTest, StrokeVariation) { loadDocument(STROKE_VARIATION_TEST); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto container = graphic->getRoot(); ASSERT_TRUE(container); @@ -565,7 +565,7 @@ static const char * PARAMETER_TEST = TEST_F(DependantGraphicTest, Parameter) { loadDocument(PARAMETER_TEST); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto container = graphic->getRoot(); ASSERT_TRUE(container); diff --git a/unit/graphic/unittest_graphic.cpp b/unit/graphic/unittest_graphic.cpp index 1a84fd9..6958827 100644 --- a/unit/graphic/unittest_graphic.cpp +++ b/unit/graphic/unittest_graphic.cpp @@ -126,7 +126,7 @@ TEST_F(GraphicTest, Basic) ASSERT_EQ(kGraphicElementTypeGroup, child->getType()); auto filterArray = child->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(Object::EMPTY_ARRAY(), filterArray); ASSERT_EQ(Object(1), child->getValue(kGraphicPropertyOpacity)); ASSERT_EQ(Object(15), child->getValue(kGraphicPropertyRotation)); @@ -142,7 +142,7 @@ TEST_F(GraphicTest, Basic) auto path = child->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); filterArray = path->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(Object::EMPTY_ARRAY(), filterArray); ASSERT_TRUE(path->getValue(kGraphicPropertyPathData).size() > 30); ASSERT_EQ(Object(0.3), path->getValue(kGraphicPropertyFillOpacity)); @@ -151,11 +151,13 @@ TEST_F(GraphicTest, Basic) path = child->getChildAt(1); ASSERT_EQ(kGraphicElementTypePath, path->getType()); filterArray = path->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(Object::EMPTY_ARRAY(), filterArray); ASSERT_TRUE(path->getValue(kGraphicPropertyPathData).size() > 30); ASSERT_EQ(Object(1.0), path->getValue(kGraphicPropertyFillOpacity)); ASSERT_EQ(Object(Color(Color::GREEN)), path->getValue(kGraphicPropertyFill)); + + ASSERT_EQ("Graphic<>", Object(graphic).toDebugString()); } // Verify default properties get set correctly @@ -326,7 +328,7 @@ TEST_F(GraphicTest, MinimalProvenance) loadDocument(MINIMAL_DOCUMENT); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ("A", graphic->getContext()->opt("@test").asString()); @@ -403,7 +405,7 @@ TEST_F(GraphicTest, GraphicResources) loadDocument(GRAPHIC_RESOURCES); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto container = graphic->getRoot(); @@ -423,10 +425,10 @@ TEST_F(GraphicTest, GraphicResourceComponentContextScoping) loadDocument(GRAPHIC_RESOURCES); auto object = context->opt("@myColor"); - ASSERT_TRUE(object.isColor()); + ASSERT_TRUE(object.is()); ASSERT_EQ(Color::RED, object.getColor()); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto container = graphic->getRoot(); @@ -446,7 +448,7 @@ TEST_F(GraphicTest, GraphicResourcesSmallPort) ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto container = graphic->getRoot(); @@ -725,7 +727,7 @@ TEST_F(GraphicTest, PathProperties) ASSERT_EQ(Object("M0,0"), path->getValue(kGraphicPropertyPathData)); ASSERT_EQ(Object(42), path->getValue(kGraphicPropertyPathLength)); ASSERT_EQ(Object(Color(Color::GREEN)), path->getValue(kGraphicPropertyStroke)); - ASSERT_EQ(Object::kArrayType, path->getValue(kGraphicPropertyStrokeDashArray).getType()); + ASSERT_TRUE(path->getValue(kGraphicPropertyStrokeDashArray).isArray()); ASSERT_EQ(2, path->getValue(kGraphicPropertyStrokeDashArray).getArray().size()); ASSERT_EQ(Object(1), path->getValue(kGraphicPropertyStrokeDashArray).getArray()[0]); ASSERT_EQ(Object(2), path->getValue(kGraphicPropertyStrokeDashArray).getArray()[1]); @@ -988,7 +990,7 @@ TEST_F(GraphicTest, InvalidUpdateWithValidJson) { none = component->findComponentById("none"); ASSERT_EQ(Object::NULL_OBJECT(), none->getCalculated(kPropertyGraphic)); stretch = component->findComponentById("stretch"); - auto graphic = stretch->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = stretch->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ("A", graphic->getContext()->opt("@test").asString()); @@ -1334,7 +1336,7 @@ TEST_F(GraphicTest, Time) auto box = root->topComponent(); ASSERT_TRUE(box); - auto graphic = box->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = box->getCalculated(kPropertyGraphic).get(); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -1408,7 +1410,7 @@ TEST_F(GraphicTest, ParameterizedTime) auto box = root->topComponent(); ASSERT_TRUE(box); - auto graphic = box->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = box->getCalculated(kPropertyGraphic).get(); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -1523,7 +1525,7 @@ TEST_F(GraphicTest, FullClock) auto box = root->topComponent(); ASSERT_TRUE(box); - auto graphic = box->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = box->getCalculated(kPropertyGraphic).get(); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -1582,7 +1584,7 @@ TEST_F(GraphicTest, ClearDirty) ASSERT_TRUE(box); ASSERT_EQ(0,box->getChildCount()); - auto graphic = box->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = box->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_TRUE(graphic->isValid()); @@ -2041,7 +2043,7 @@ TEST_F(GraphicTest, Transformed) auto group = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); - auto transform = group->getValue(kGraphicPropertyTransform).getTransform2D(); + auto transform = group->getValue(kGraphicPropertyTransform).get(); Transform2D expected; expected *= Transform2D::translate(50, 75); expected *= Transform2D::rotate(-10); @@ -2051,10 +2053,10 @@ TEST_F(GraphicTest, Transformed) auto path = group->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); - ASSERT_TRUE(path->getValue(kGraphicPropertyFill).isGradient()); + ASSERT_TRUE(path->getValue(kGraphicPropertyFill).is()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillGrad = fill.get(); ASSERT_EQ(Gradient::LINEAR, fillGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = fillGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2078,17 +2080,17 @@ TEST_F(GraphicTest, Transformed) ASSERT_EQ(0.89, fillGrad.getProperty(kGradientPropertyY2).getDouble()); - auto fillTransform = path->getValue(kGraphicPropertyFillTransform).getTransform2D(); + auto fillTransform = path->getValue(kGraphicPropertyFillTransform).get(); Transform2D expectedFill; expectedFill *= Transform2D::translate(-36, 45.5); expectedFill *= Transform2D::skewX(40); ASSERT_EQ(expectedFill, fillTransform); - ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).isGradient()); + ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).is()); auto stroke = path->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(stroke.isGradient()); - auto& strokeGrad = stroke.getGradient(); + ASSERT_TRUE(stroke.is()); + auto& strokeGrad = stroke.get(); ASSERT_EQ(Gradient::RADIAL, strokeGrad.getProperty(kGradientPropertyType).getInteger()); colorRange = strokeGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2106,7 +2108,7 @@ TEST_F(GraphicTest, Transformed) ASSERT_EQ(0.3, strokeGrad.getProperty(kGradientPropertyCenterY).getDouble()); ASSERT_EQ(1.2, strokeGrad.getProperty(kGradientPropertyRadius).getDouble()); - auto strokeTransform = path->getValue(kGraphicPropertyStrokeTransform).getTransform2D(); + auto strokeTransform = path->getValue(kGraphicPropertyStrokeTransform).get(); Transform2D expectedStroke; expectedStroke *= Transform2D::skewY(5); expectedStroke *= Transform2D::scale(0.7, 0.5); @@ -2201,7 +2203,7 @@ TEST_F(GraphicTest, AVGResourceTypes) ASSERT_EQ(kGraphicElementTypePath, path->getType()); // Patterns checked separately - auto fill = path->getValue(kGraphicPropertyFill).getGradient(); + auto fill = path->getValue(kGraphicPropertyFill).get(); ASSERT_EQ(Gradient::LINEAR, fill.getType()); ASSERT_EQ(Gradient::kGradientUnitsUserSpace, fill.getProperty(kGradientPropertyUnits).getInteger()); ASSERT_EQ(std::vector({Color(Color::RED), Color(Color::TRANSPARENT)}), fill.getProperty(kGradientPropertyColorRange).getArray()); @@ -2300,10 +2302,10 @@ TEST_F(GraphicTest, LocalResourcedPattern) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fillPattern = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fillPattern.isGraphicPattern()); - auto fillPatternId = fillPattern.getGraphicPattern()->getUniqueId(); + ASSERT_TRUE(fillPattern.is()); + auto fillPatternId = fillPattern.get()->getUniqueId(); - auto fillPath = fillPattern.getGraphicPattern()->getItems().at(0); + auto fillPath = fillPattern.get()->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPath->getType()); ASSERT_EQ(Object(Color(Color::RED)), fillPath->getValue(kGraphicPropertyFill)); @@ -2311,10 +2313,10 @@ TEST_F(GraphicTest, LocalResourcedPattern) ASSERT_EQ(kGraphicElementTypeText, text->getType()); auto strokePattern = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(strokePattern.isGraphicPattern()); - auto strokePatternId = strokePattern.getGraphicPattern()->getUniqueId(); + ASSERT_TRUE(strokePattern.is()); + auto strokePatternId = strokePattern.get()->getUniqueId(); - auto strokePath = strokePattern.getGraphicPattern()->getItems().at(0); + auto strokePath = strokePattern.get()->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, strokePath->getType()); ASSERT_EQ(Object(Color(Color::GREEN)), strokePath->getValue(kGraphicPropertyFill)); @@ -2384,7 +2386,7 @@ TEST_F(GraphicTest, ExternalResourcedPattern) ASSERT_TRUE(ConsoleMessage()); auto fillPattern = path->getValue(kGraphicPropertyFill); - ASSERT_FALSE(fillPattern.isGraphicPattern()); + ASSERT_FALSE(fillPattern.is()); } static const char* PATTERN_INLINE = R"({ @@ -2439,7 +2441,7 @@ TEST_F(GraphicTest, PatternInline) auto fillPattern = path->getValue(kGraphicPropertyFill); // Inline not supported - ASSERT_TRUE(fillPattern.isColor()); + ASSERT_TRUE(fillPattern.is()); ASSERT_EQ(Object(Color()), fillPattern); ASSERT_TRUE(ConsoleMessage()); } @@ -2525,8 +2527,8 @@ TEST_F(GraphicTest, LocalResourcedGradient) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillGrad = fill.get(); ASSERT_EQ(Gradient::LINEAR, fillGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = fillGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2554,8 +2556,8 @@ TEST_F(GraphicTest, LocalResourcedGradient) ASSERT_EQ(kGraphicElementTypeText, text->getType()); auto stroke = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(stroke.isGradient()); - auto& strokeGrad = stroke.getGradient(); + ASSERT_TRUE(stroke.is()); + auto& strokeGrad = stroke.get(); ASSERT_EQ(Gradient::RADIAL, strokeGrad.getProperty(kGradientPropertyType).getInteger()); colorRange = strokeGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2646,8 +2648,8 @@ TEST_F(GraphicTest, ExternalResourcedGradient) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillGrad = fill.get(); ASSERT_EQ(Gradient::LINEAR, fillGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = fillGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2675,8 +2677,8 @@ TEST_F(GraphicTest, ExternalResourcedGradient) ASSERT_EQ(kGraphicElementTypeText, text->getType()); auto stroke = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(stroke.isGradient()); - auto& strokeGrad = stroke.getGradient(); + ASSERT_TRUE(stroke.is()); + auto& strokeGrad = stroke.get(); ASSERT_EQ(Gradient::RADIAL, strokeGrad.getProperty(kGradientPropertyType).getInteger()); colorRange = strokeGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2740,8 +2742,8 @@ TEST_F(GraphicTest, GradientInline) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillGrad = fill.get(); ASSERT_EQ(Gradient::LINEAR, fillGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = fillGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2837,9 +2839,9 @@ TEST_F(GraphicTest, MixedResources) auto fillPattern = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fillPattern.isGraphicPattern()); + ASSERT_TRUE(fillPattern.is()); - auto fillPath = fillPattern.getGraphicPattern()->getItems().at(0); + auto fillPath = fillPattern.get()->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPath->getType()); ASSERT_EQ(Object(Color(Color::RED)), fillPath->getValue(kGraphicPropertyFill)); @@ -2848,8 +2850,8 @@ TEST_F(GraphicTest, MixedResources) ASSERT_EQ(kGraphicElementTypeText, text->getType()); auto stroke = text->getValue(kGraphicPropertyStroke); - ASSERT_TRUE(stroke.isGradient()); - auto& strokeGrad = stroke.getGradient(); + ASSERT_TRUE(stroke.is()); + auto& strokeGrad = stroke.get(); ASSERT_EQ(Gradient::RADIAL, strokeGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = strokeGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -2906,7 +2908,7 @@ TEST_F(GraphicTest, Transform) auto box = root->topComponent(); ASSERT_TRUE(box); - auto graphic = box->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = box->getCalculated(kPropertyGraphic).get(); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -2924,17 +2926,17 @@ TEST_F(GraphicTest, Transform) ASSERT_EQ(0.5, group->getValue(kGraphicPropertyScaleY).getDouble()); auto transform = group->getValue(kGraphicPropertyTransform); - ASSERT_TRUE(transform.isTransform2D()); + ASSERT_TRUE(transform.is()); // Start -Pivot Scaled Rotate +Pivot Translated // ( 0, 0) -> (-20,-10) -> (-40, -5) -> ( 5,-40) -> (25,-30) -> (125, 20) - ASSERT_EQ(Point(125,20), transform.getTransform2D() * Point(0,0)); + ASSERT_EQ(Point(125,20), transform.get() * Point(0,0)); // (20,10) -> ( 0, 0) -> ( 0, 0) -> ( 0, 0) -> (20, 10) -> (120, 60) - ASSERT_EQ(Point(120,60), transform.getTransform2D() * Point(20,10)); + ASSERT_EQ(Point(120,60), transform.get() * Point(20,10)); // (30,20) -> ( 10, 10) -> ( 20, 5) -> (-5, 20) -> (15, 30) -> (115, 80) - ASSERT_EQ(Point(115,80), transform.getTransform2D() * Point(30,20)); + ASSERT_EQ(Point(115,80), transform.get() * Point(30,20)); } static const char* GRADIENT_REQUIRED = R"({ @@ -3018,7 +3020,7 @@ TEST_F(GraphicTest, GradientChecks) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isColor()); + ASSERT_TRUE(fill.is()); ASSERT_EQ(Color(Color::TRANSPARENT), fill.getColor()); // Defaults to default color when gradient is incorrect (no type) @@ -3026,7 +3028,7 @@ TEST_F(GraphicTest, GradientChecks) ASSERT_EQ(kGraphicElementTypePath, path->getType()); fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isColor()); + ASSERT_TRUE(fill.is()); ASSERT_EQ(Color(Color::TRANSPARENT), fill.getColor()); // Defaults to default color when gradient is incorrect (no type, no color range) @@ -3034,7 +3036,7 @@ TEST_F(GraphicTest, GradientChecks) ASSERT_EQ(kGraphicElementTypePath, path->getType()); fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isColor()); + ASSERT_TRUE(fill.is()); ASSERT_EQ(Color(Color::TRANSPARENT), fill.getColor()); // Default values on linear gradient @@ -3042,8 +3044,8 @@ TEST_F(GraphicTest, GradientChecks) ASSERT_EQ(kGraphicElementTypePath, path->getType()); fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillLinearGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillLinearGrad = fill.get(); ASSERT_EQ(Gradient::LINEAR, fillLinearGrad.getProperty(kGradientPropertyType).getInteger()); auto colorRange = fillLinearGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -3064,8 +3066,8 @@ TEST_F(GraphicTest, GradientChecks) ASSERT_EQ(kGraphicElementTypePath, path->getType()); fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGradient()); - auto& fillRadialGrad = fill.getGradient(); + ASSERT_TRUE(fill.is()); + auto& fillRadialGrad = fill.get(); ASSERT_EQ(Gradient::RADIAL, fillRadialGrad.getProperty(kGradientPropertyType).getInteger()); colorRange = fillRadialGrad.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -3196,9 +3198,9 @@ TEST_F(GraphicTest, DefaultGraphicFilter) ASSERT_EQ(kGraphicElementTypeGroup, child->getType()); auto filterArray = child->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(1, filterArray.size()); - auto graphicFilter = filterArray.at(0).getGraphicFilter(); + auto graphicFilter = filterArray.at(0).get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, graphicFilter.getType()); ASSERT_TRUE(IsEqual(Color::BLACK, graphicFilter.getValue(kGraphicPropertyFilterColor))); ASSERT_TRUE(IsEqual(0, graphicFilter.getValue(kGraphicPropertyFilterHorizontalOffset))); @@ -3208,14 +3210,14 @@ TEST_F(GraphicTest, DefaultGraphicFilter) auto path = child->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); filterArray = path->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(Object::EMPTY_ARRAY(), filterArray); auto text = child->getChildAt(1); ASSERT_EQ(kGraphicElementTypeText, text->getType()); filterArray = text->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); - graphicFilter = filterArray.at(0).getGraphicFilter(); + ASSERT_TRUE(filterArray.isArray()); + graphicFilter = filterArray.at(0).get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, graphicFilter.getType()); ASSERT_TRUE(IsEqual(Color::BLACK, graphicFilter.getValue(kGraphicPropertyFilterColor))); ASSERT_TRUE(IsEqual(0, graphicFilter.getValue(kGraphicPropertyFilterHorizontalOffset))); @@ -3301,9 +3303,9 @@ TEST_F(GraphicTest, GraphicFilterArray) ASSERT_EQ(kGraphicElementTypeGroup, child->getType()); auto filterArray = child->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(1, filterArray.size()); - auto graphicFilter = filterArray.at(0).getGraphicFilter(); + auto graphicFilter = filterArray.at(0).get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, graphicFilter.getType()); ASSERT_TRUE(IsEqual(Color(Color::GREEN), graphicFilter.getValue(kGraphicPropertyFilterColor))); ASSERT_TRUE(IsEqual(1, graphicFilter.getValue(kGraphicPropertyFilterHorizontalOffset))); @@ -3313,11 +3315,11 @@ TEST_F(GraphicTest, GraphicFilterArray) auto path = child->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); filterArray = path->getValue(kGraphicPropertyFilters); - ASSERT_EQ(rapidjson::kArrayType, filterArray.getType()); + ASSERT_TRUE(filterArray.isArray()); ASSERT_EQ(2, filterArray.size()); // check value of first filter - graphicFilter = filterArray.at(0).getGraphicFilter(); + graphicFilter = filterArray.at(0).get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, graphicFilter.getType()); ASSERT_TRUE(IsEqual(Color::BLACK, graphicFilter.getValue(kGraphicPropertyFilterColor))); ASSERT_TRUE(IsEqual(0, graphicFilter.getValue(kGraphicPropertyFilterHorizontalOffset))); @@ -3325,7 +3327,7 @@ TEST_F(GraphicTest, GraphicFilterArray) ASSERT_TRUE(IsEqual(0, graphicFilter.getValue(kGraphicPropertyFilterVerticalOffset))); // check value of second filter - graphicFilter = filterArray.at(1).getGraphicFilter(); + graphicFilter = filterArray.at(1).get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, graphicFilter.getType()); ASSERT_TRUE(IsEqual(Color(Color::BLUE), graphicFilter.getValue(kGraphicPropertyFilterColor))); ASSERT_TRUE(IsEqual(-1, graphicFilter.getValue(kGraphicPropertyFilterHorizontalOffset))); diff --git a/unit/graphic/unittest_graphic_bind.cpp b/unit/graphic/unittest_graphic_bind.cpp index dd4d584..73988fd 100644 --- a/unit/graphic/unittest_graphic_bind.cpp +++ b/unit/graphic/unittest_graphic_bind.cpp @@ -61,7 +61,7 @@ TEST_F(GraphicBindTest, BindTest) loadDocument(BIND_TEST); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto box = graphic->getRoot(); ASSERT_EQ(kGraphicElementTypeContainer, box->getType()); @@ -119,7 +119,7 @@ TEST_F(GraphicBindTest, BindToTime) loadDocument(BIND_TO_TIME_TEST); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto box = graphic->getRoot(); ASSERT_EQ(kGraphicElementTypeContainer, box->getType()); @@ -178,7 +178,7 @@ TEST_F(GraphicBindTest, Nested) loadDocument(NESTED); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto box = graphic->getRoot(); ASSERT_EQ(kGraphicElementTypeContainer, box->getType()); diff --git a/unit/graphic/unittest_graphic_component.cpp b/unit/graphic/unittest_graphic_component.cpp index b6c4207..3b56d2e 100644 --- a/unit/graphic/unittest_graphic_component.cpp +++ b/unit/graphic/unittest_graphic_component.cpp @@ -60,12 +60,12 @@ TEST_F(GraphicComponentTest, SimpleTest) ASSERT_EQ( kVectorGraphicAlignCenter, component->getCalculated(kPropertyAlign).getInteger()); ASSERT_EQ( kVectorGraphicScaleNone, component->getCalculated(kPropertyScale).getInteger()); ASSERT_EQ( Object("box"), component->getCalculated(kPropertySource)); - ASSERT_TRUE( component->getCalculated(kPropertyGraphic).isGraphic()); + ASSERT_TRUE( component->getCalculated(kPropertyGraphic).is()); // Check to see if the graphic will be drawn where we thought it should be ASSERT_EQ(Object(Rect(0, 0, 100, 100)), component->getCalculated(kPropertyMediaBounds)); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ(100, graphic->getIntrinsicWidth()); @@ -124,13 +124,13 @@ TEST_F(GraphicComponentTest, BasicNoScale) ASSERT_EQ( kVectorGraphicAlignCenter, component->getCalculated(kPropertyAlign).getInteger()); ASSERT_EQ( kVectorGraphicScaleNone, component->getCalculated(kPropertyScale).getInteger()); ASSERT_EQ( Object("box"), component->getCalculated(kPropertySource)); - ASSERT_TRUE( component->getCalculated(kPropertyGraphic).isGraphic()); + ASSERT_TRUE( component->getCalculated(kPropertyGraphic).is()); // Check to see if the graphic will be drawn where we thought it should be ASSERT_EQ(Object(Rect((metrics.getWidth() - 100)/2, (metrics.getHeight() - 100)/2, 100, 100)), component->getCalculated(kPropertyMediaBounds)); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); // The graphic element is not scaled, so it should be the original 100x100 size and centered @@ -178,14 +178,14 @@ TEST_F(GraphicComponentTest, BasicBestFit) ASSERT_EQ( kVectorGraphicAlignCenter, component->getCalculated(kPropertyAlign).getInteger()); ASSERT_EQ( kVectorGraphicScaleBestFit, component->getCalculated(kPropertyScale).getInteger()); ASSERT_EQ( Object("box"), component->getCalculated(kPropertySource)); - ASSERT_TRUE( component->getCalculated(kPropertyGraphic).isGraphic()); + ASSERT_TRUE( component->getCalculated(kPropertyGraphic).is()); // Check to see if the graphic will be drawn where we thought it should be double minSize = std::min(metrics.getWidth(), metrics.getHeight()); ASSERT_EQ(Object(Rect((metrics.getWidth() - minSize)/2, (metrics.getHeight() - minSize) / 2, minSize, minSize)), component->getCalculated(kPropertyMediaBounds)); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ(100, graphic->getIntrinsicWidth()); @@ -291,7 +291,7 @@ TEST_F(GraphicComponentTest, FitAndScale) root = RootContext::create(Metrics().size(1024, 800), content); ASSERT_TRUE(root) << "test case " << index; - component = std::dynamic_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component) << "test case " << index; // Verify that the scale and align were set correctly @@ -299,7 +299,7 @@ TEST_F(GraphicComponentTest, FitAndScale) ASSERT_EQ(Object(ftc.align), component->getCalculated(kPropertyAlign)) << "test case " << index; // Check that the media bounds have been set - ASSERT_EQ(component->getCalculated(kPropertyMediaBounds).getRect(), ftc.bounds) << "test case " << index; + ASSERT_EQ(component->getCalculated(kPropertyMediaBounds).get(), ftc.bounds) << "test case " << index; } } @@ -382,11 +382,11 @@ TEST_F(GraphicComponentTest, StretchAndGrow) root = RootContext::create(Metrics().size(1024, 800), content); ASSERT_TRUE(root) << "test case " << index; - component = std::dynamic_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component) << "test case " << index; - ASSERT_TRUE(component->getCalculated(kPropertyGraphic).isGraphic()) << "test case " << index; - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + ASSERT_TRUE(component->getCalculated(kPropertyGraphic).is()) << "test case " << index; + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic) << "test case " << index; auto top = graphic->getRoot(); @@ -452,7 +452,7 @@ TEST_F(GraphicComponentTest, StyleTest) ASSERT_EQ( kComponentTypeVectorGraphic, component->getType()); ASSERT_EQ( Rect(0, 0, metrics.getWidth(), metrics.getHeight()), component->getGlobalBounds()); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto box = graphic->getRoot(); @@ -525,10 +525,10 @@ TEST_F(GraphicComponentTest, StyleTestWithAlignment) ASSERT_EQ( kComponentTypeVectorGraphic, component->getType()); ASSERT_EQ( Rect(0, 0, metrics.getWidth(), metrics.getHeight()), component->getGlobalBounds()); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); - ASSERT_EQ(Rect(0, 350, 100, 100), component->getCalculated(kPropertyMediaBounds).getRect()); + ASSERT_EQ(Rect(0, 350, 100, 100), component->getCalculated(kPropertyMediaBounds).get()); auto box = graphic->getRoot(); ASSERT_TRUE(box); @@ -540,7 +540,7 @@ TEST_F(GraphicComponentTest, StyleTestWithAlignment) ASSERT_EQ(0, graphic->getDirty().size()); component->setState(kStatePressed, true); - ASSERT_EQ(Rect(924, 350, 100, 100), component->getCalculated(kPropertyMediaBounds).getRect()); + ASSERT_EQ(Rect(924, 350, 100, 100), component->getCalculated(kPropertyMediaBounds).get()); ASSERT_TRUE(CheckDirty(component, kPropertyAlign, kPropertyMediaBounds, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(path)); } @@ -597,9 +597,9 @@ TEST_F(GraphicComponentTest, StyleTestWithStretch) ASSERT_EQ(kComponentTypeVectorGraphic, component->getType()); ASSERT_EQ(Rect(0, 0, metrics.getWidth(), metrics.getHeight()), component->getGlobalBounds()); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyMediaBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyMediaBounds).get()); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ(400, graphic->getViewportWidth()); // Factor of 4 = 1024 / 256 ASSERT_EQ(1600, graphic->getViewportHeight()); // Factor of 16 = 800 / 50 @@ -641,7 +641,7 @@ TEST_F(GraphicComponentTest, StyleTestWithStretch) ASSERT_TRUE(CheckDirty(graphic, container, path)); // The vector graphic component should have a new scale, alignment, and media bounds - ASSERT_EQ(Rect(768, 375, 256, 50), component->getCalculated(kPropertyMediaBounds).getRect()); // Right-aligned + ASSERT_EQ(Rect(768, 375, 256, 50), component->getCalculated(kPropertyMediaBounds).get()); // Right-aligned ASSERT_TRUE(CheckDirty(component, kPropertyScale, kPropertyAlign, kPropertyMediaBounds, kPropertyGraphic, kPropertyVisualHash)); @@ -699,9 +699,9 @@ TEST_F(GraphicComponentTest, SetValue) ASSERT_EQ(kComponentTypeVectorGraphic, component->getType()); ASSERT_TRUE(IsEqual(Rect(0, 0, 512, 512), component->getGlobalBounds())); - ASSERT_TRUE(IsEqual(Rect(128, 192, 256, 128), component->getCalculated(kPropertyMediaBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect(128, 192, 256, 128), component->getCalculated(kPropertyMediaBounds).get())); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -727,7 +727,7 @@ TEST_F(GraphicComponentTest, SetValue) ASSERT_TRUE(CheckDirty(graphic)); // The vector graphic component should have a new alignment and media bounds - ASSERT_TRUE(IsEqual(Rect(256, 384, 256, 128), component->getCalculated(kPropertyMediaBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect(256, 384, 256, 128), component->getCalculated(kPropertyMediaBounds).get())); ASSERT_TRUE(CheckDirty(component, kPropertyAlign, kPropertyMediaBounds, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); @@ -755,7 +755,7 @@ TEST_F(GraphicComponentTest, SetValue) ASSERT_TRUE(CheckDirty(graphic, container, path)); // The vector graphic component should have a new scale, alignment, and media bounds - ASSERT_TRUE(IsEqual(Rect(0, 256, 512, 256), component->getCalculated(kPropertyMediaBounds).getRect())); + ASSERT_TRUE(IsEqual(Rect(0, 256, 512, 256), component->getCalculated(kPropertyMediaBounds).get())); ASSERT_TRUE(CheckDirty(component, kPropertyScale, kPropertyMediaBounds, kPropertyGraphic, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); } @@ -815,13 +815,13 @@ TEST_F(GraphicComponentTest, RelayoutTest) // The top component is a Frame ASSERT_EQ(kComponentTypeFrame, component->getType()); ASSERT_EQ(Rect(0, 0, metrics.getWidth(), metrics.getHeight()), component->getGlobalBounds()); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyInnerBounds).get()); auto vg = component->getChildAt(0); ASSERT_EQ(kComponentTypeVectorGraphic, vg->getType()); - ASSERT_EQ(Rect(0, 0, 1024, 800), vg->getCalculated(kPropertyMediaBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), vg->getCalculated(kPropertyMediaBounds).get()); - auto graphic = vg->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = vg->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); ASSERT_EQ(100, graphic->getViewportWidth()); ASSERT_EQ(100, graphic->getViewportHeight()); @@ -838,10 +838,10 @@ TEST_F(GraphicComponentTest, RelayoutTest) root->clearPending(); // Ensure that the layout has been updated // The vector graphic component has new, smaller media bounds - ASSERT_EQ(Rect(0, 0, 824, 600), vg->getCalculated(kPropertyMediaBounds).getRect()); - ASSERT_EQ(Rect(100, 100, 824, 600), vg->getCalculated(kPropertyBounds).getRect()); // Bounds in parent + ASSERT_EQ(Rect(0, 0, 824, 600), vg->getCalculated(kPropertyMediaBounds).get()); + ASSERT_EQ(Rect(100, 100, 824, 600), vg->getCalculated(kPropertyBounds).get()); // Bounds in parent // The kPropertyGraphic is marked as dirty. That's not right - it's merely resized - ASSERT_EQ(Rect(0, 0, 824, 600), vg->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 824, 600), vg->getCalculated(kPropertyInnerBounds).get()); // The container should have four updated values ASSERT_EQ(Object(Dimension(600)), container->getValue(kGraphicPropertyHeightActual)); @@ -852,7 +852,7 @@ TEST_F(GraphicComponentTest, RelayoutTest) // The border width has changed on the frame. ASSERT_EQ(Object(Dimension(100)), component->getCalculated(kPropertyBorderWidth)); - ASSERT_EQ(Rect(100, 100, 824, 600), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(100, 100, 824, 600), component->getCalculated(kPropertyInnerBounds).get()); ASSERT_TRUE(CheckDirty(component, kPropertyInnerBounds, kPropertyBorderWidth, kPropertyNotifyChildrenChanged, kPropertyVisualHash, kPropertyDrawnBorderWidth)); @@ -919,7 +919,7 @@ TEST_F(GraphicComponentTest, AssignGraphicLater) { // The top component is the graphic, but there is no content ASSERT_EQ(kComponentTypeVectorGraphic, component->getType()); ASSERT_EQ(Rect(0, 0, metrics.getWidth(), metrics.getHeight()), component->getGlobalBounds()); - ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyInnerBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 1024, 800), component->getCalculated(kPropertyInnerBounds).get()); ASSERT_EQ(Object::NULL_OBJECT(), component->getCalculated(kPropertyGraphic)); ASSERT_EQ(Object(kVectorGraphicAlignCenter), component->getCalculated(kPropertyAlign)); ASSERT_EQ(Object(kVectorGraphicScaleFill), component->getCalculated(kPropertyScale)); @@ -934,7 +934,7 @@ TEST_F(GraphicComponentTest, AssignGraphicLater) { ASSERT_TRUE(CheckDirty(component, kPropertyGraphic, kPropertyMediaBounds, kPropertyVisualHash)); ASSERT_TRUE(CheckDirty(root, component)); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto top = graphic->getRoot(); auto path = top->getChildAt(0); @@ -1007,8 +1007,8 @@ TEST_F(GraphicComponentTest, GraphicParameter) { auto stretch = component->getChildAt(1); auto obj = none->getCalculated(kPropertyGraphic); - ASSERT_EQ(obj.getType(), Object::kGraphicType); - auto graphic = obj.getGraphic(); + ASSERT_TRUE(obj.is()); + auto graphic = obj.get(); ASSERT_TRUE(graphic->getRoot() != nullptr); ASSERT_EQ(graphic->getRoot()->getChildCount(), 1); auto path = graphic->getRoot()->getChildAt(0); @@ -1016,8 +1016,8 @@ TEST_F(GraphicComponentTest, GraphicParameter) { ASSERT_EQ("M25,50 a25,25 0 1 1 50,0 l0 0 a25,25 0 1 1 -50,0 z", pathData.asString()); obj = stretch->getCalculated(kPropertyGraphic); - ASSERT_EQ(obj.getType(), Object::kGraphicType); - graphic = obj.getGraphic(); + ASSERT_TRUE(obj.is()); + graphic = obj.get(); ASSERT_TRUE(graphic->getRoot() != nullptr); ASSERT_EQ(graphic->getRoot()->getChildCount(), 1); path = graphic->getRoot()->getChildAt(0); @@ -1101,8 +1101,8 @@ TEST_F(GraphicComponentTest, GraphicFocusAndHover) { ASSERT_EQ(kComponentTypeVectorGraphic, gc->getType()); auto obj = gc->getCalculated(kPropertyGraphic); - ASSERT_EQ(obj.getType(), Object::kGraphicType); - auto graphic = obj.getGraphic(); + ASSERT_TRUE(obj.is()); + auto graphic = obj.get(); ASSERT_TRUE(graphic->getRoot() != nullptr); ASSERT_EQ(graphic->getRoot()->getChildCount(), 1); auto path = graphic->getRoot()->getChildAt(0); @@ -1579,7 +1579,7 @@ TEST_F(GraphicComponentTest, ExternalExpandedStyling) { loadDocument(EXTERNAL_EXPANDED_STYLING_DOC); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto group = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); @@ -1743,13 +1743,13 @@ TEST_F(GraphicComponentTest, StyleEverything) { loadDocument(STYLE_EVERYTHING_DOC); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto group = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypeGroup, group->getType()); ASSERT_EQ(0.7, group->getValue(kGraphicPropertyOpacity).asNumber()); ASSERT_EQ("M 50 0 L 100 50 L 50 100 L 0 50 z", group->getValue(kGraphicPropertyClipPath).asString()); - ASSERT_EQ(Transform2D::rotate(5), group->getValue(kGraphicPropertyTransform).getTransform2D()); + ASSERT_EQ(Transform2D::rotate(5), group->getValue(kGraphicPropertyTransform).get()); auto fillTransform = Transform2D::translate(-36, 45.5); @@ -1758,8 +1758,8 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGraphicPattern()); - auto fillPattern = fill.getGraphicPattern(); + ASSERT_TRUE(fill.is()); + auto fillPattern = fill.get(); auto fillPatternPath = fillPattern->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPatternPath->getType()); ASSERT_EQ(Color::RED, fillPatternPath->getValue(kGraphicPropertyFill).getColor()); @@ -1768,8 +1768,8 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ("M 50 0 L 100 50 L 50 100 L 0 50 z", path->getValue(kGraphicPropertyPathData).asString()); ASSERT_EQ(50, path->getValue(kGraphicPropertyPathLength).asNumber()); - ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).isGradient()); - auto stroke = path->getValue(kGraphicPropertyStroke).getGradient(); + ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).is()); + auto stroke = path->getValue(kGraphicPropertyStroke).get(); ASSERT_EQ(Gradient::LINEAR, stroke.getProperty(kGradientPropertyType).asInt()); auto colorRange = stroke.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -1796,16 +1796,16 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ(3, path->getValue(kGraphicPropertyStrokeMiterLimit).asNumber()); ASSERT_EQ(1.0, path->getValue(kGraphicPropertyStrokeOpacity).asNumber()); ASSERT_EQ(4, path->getValue(kGraphicPropertyStrokeWidth).asNumber()); - ASSERT_EQ(fillTransform, path->getValue(kGraphicPropertyFillTransform).getTransform2D()); - ASSERT_EQ(strokeTransform, path->getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + ASSERT_EQ(fillTransform, path->getValue(kGraphicPropertyFillTransform).get()); + ASSERT_EQ(strokeTransform, path->getValue(kGraphicPropertyStrokeTransform).get()); auto text = group->getChildAt(1); ASSERT_EQ(kGraphicElementTypeText, text->getType()); fill = text->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGraphicPattern()); - fillPattern = fill.getGraphicPattern(); + ASSERT_TRUE(fill.is()); + fillPattern = fill.get(); fillPatternPath = fillPattern->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPatternPath->getType()); ASSERT_EQ(Color::RED, fillPatternPath->getValue(kGraphicPropertyFill).getColor()); @@ -1817,14 +1817,14 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ(700, text->getValue(kGraphicPropertyFontWeight).asNumber()); ASSERT_EQ(1, text->getValue(kGraphicPropertyLetterSpacing).asNumber()); ASSERT_EQ("Texty text", text->getValue(kGraphicPropertyText).asString()); - ASSERT_TRUE(text->getValue(kGraphicPropertyStroke).isGradient()); + ASSERT_TRUE(text->getValue(kGraphicPropertyStroke).is()); ASSERT_EQ(1.0, text->getValue(kGraphicPropertyStrokeOpacity).asNumber()); ASSERT_EQ(4, text->getValue(kGraphicPropertyStrokeWidth).asNumber()); ASSERT_EQ(kGraphicTextAnchorStart, text->getValue(kGraphicPropertyTextAnchor).asInt()); ASSERT_EQ(2, text->getValue(kGraphicPropertyCoordinateX).asNumber()); ASSERT_EQ(3, text->getValue(kGraphicPropertyCoordinateY).asNumber()); - ASSERT_EQ(fillTransform, text->getValue(kGraphicPropertyFillTransform).getTransform2D()); - ASSERT_EQ(strokeTransform, text->getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + ASSERT_EQ(fillTransform, text->getValue(kGraphicPropertyFillTransform).get()); + ASSERT_EQ(strokeTransform, text->getValue(kGraphicPropertyStrokeTransform).get()); component->setState(StateProperty::kStateDisabled, true); @@ -1847,12 +1847,12 @@ TEST_F(GraphicComponentTest, StyleEverything) transform *= Transform2D::rotate(-10); transform *= Transform2D::translate(-50, -75); - ASSERT_EQ(transform, group->getValue(kGraphicPropertyTransform).getTransform2D()); + ASSERT_EQ(transform, group->getValue(kGraphicPropertyTransform).get()); fill = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGraphicPattern()); - fillPattern = fill.getGraphicPattern(); + ASSERT_TRUE(fill.is()); + fillPattern = fill.get(); fillPatternPath = fillPattern->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPatternPath->getType()); ASSERT_EQ(Color::BLUE, fillPatternPath->getValue(kGraphicPropertyFill).getColor()); @@ -1861,8 +1861,8 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ("M 25 0 L 50 25 L 25 50 L 0 25 z", path->getValue(kGraphicPropertyPathData).asString()); ASSERT_EQ(40, path->getValue(kGraphicPropertyPathLength).asNumber()); - ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).isGradient()); - stroke = path->getValue(kGraphicPropertyStroke).getGradient(); + ASSERT_TRUE(path->getValue(kGraphicPropertyStroke).is()); + stroke = path->getValue(kGraphicPropertyStroke).get(); ASSERT_EQ(Gradient::LINEAR, stroke.getProperty(kGradientPropertyType).asInt()); colorRange = stroke.getProperty(kGradientPropertyColorRange); ASSERT_EQ(2, colorRange.size()); @@ -1891,13 +1891,13 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ(2, path->getValue(kGraphicPropertyStrokeWidth).asNumber()); fillTransform *= Transform2D::skewX(40); strokeTransform *= Transform2D::scale(0.7, 0.5); - ASSERT_EQ(fillTransform, path->getValue(kGraphicPropertyFillTransform).getTransform2D()); - ASSERT_EQ(strokeTransform, path->getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + ASSERT_EQ(fillTransform, path->getValue(kGraphicPropertyFillTransform).get()); + ASSERT_EQ(strokeTransform, path->getValue(kGraphicPropertyStrokeTransform).get()); fill = text->getValue(kGraphicPropertyFill); - ASSERT_TRUE(fill.isGraphicPattern()); - fillPattern = fill.getGraphicPattern(); + ASSERT_TRUE(fill.is()); + fillPattern = fill.get(); fillPatternPath = fillPattern->getItems().at(0); ASSERT_EQ(kGraphicElementTypePath, fillPatternPath->getType()); ASSERT_EQ(Color::BLUE, fillPatternPath->getValue(kGraphicPropertyFill).getColor()); @@ -1909,14 +1909,14 @@ TEST_F(GraphicComponentTest, StyleEverything) ASSERT_EQ(400, text->getValue(kGraphicPropertyFontWeight).asNumber()); ASSERT_EQ(2, text->getValue(kGraphicPropertyLetterSpacing).asNumber()); ASSERT_EQ("Less texty text", text->getValue(kGraphicPropertyText).asString()); - ASSERT_TRUE(text->getValue(kGraphicPropertyStroke).isGradient()); + ASSERT_TRUE(text->getValue(kGraphicPropertyStroke).is()); ASSERT_EQ(0.9, text->getValue(kGraphicPropertyStrokeOpacity).asNumber()); ASSERT_EQ(2, text->getValue(kGraphicPropertyStrokeWidth).asNumber()); ASSERT_EQ(kGraphicTextAnchorMiddle, text->getValue(kGraphicPropertyTextAnchor).asInt()); ASSERT_EQ(5, text->getValue(kGraphicPropertyCoordinateX).asNumber()); ASSERT_EQ(7, text->getValue(kGraphicPropertyCoordinateY).asNumber()); - ASSERT_EQ(fillTransform, text->getValue(kGraphicPropertyFillTransform).getTransform2D()); - ASSERT_EQ(strokeTransform, text->getValue(kGraphicPropertyStrokeTransform).getTransform2D()); + ASSERT_EQ(fillTransform, text->getValue(kGraphicPropertyFillTransform).get()); + ASSERT_EQ(strokeTransform, text->getValue(kGraphicPropertyStrokeTransform).get()); } @@ -1997,19 +1997,19 @@ TEST_F(GraphicComponentTest, TransformInPattern) { loadDocument(TRANSFORM_IN_PATTERN); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto path = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); auto strokePattern = path->getValue(kGraphicPropertyFill); - ASSERT_TRUE(strokePattern.isGraphicPattern()); + ASSERT_TRUE(strokePattern.is()); - auto strokePatternGroup = strokePattern.getGraphicPattern()->getItems().at(0); + auto strokePatternGroup = strokePattern.get()->getItems().at(0); ASSERT_EQ(kGraphicElementTypeGroup, strokePatternGroup->getType()); ASSERT_EQ(Object(Transform2D::rotate(90)), strokePatternGroup->getValue(kGraphicPropertyTransform)); - auto strokePatternPath = strokePattern.getGraphicPattern()->getItems().at(1); + auto strokePatternPath = strokePattern.get()->getItems().at(1); ASSERT_EQ(kGraphicElementTypePath, strokePatternPath->getType()); ASSERT_EQ(Object(Transform2D::rotate(7)), strokePatternPath->getValue(kGraphicPropertyStrokeTransform)); ASSERT_EQ(Object(Transform2D::rotate(8)), strokePatternPath->getValue(kGraphicPropertyFillTransform)); @@ -2064,7 +2064,7 @@ static const char * SIMPLE_PRESS = R"( TEST_F(GraphicComponentTest, KeyboardPress) { loadDocument(SIMPLE_PRESS); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto container = graphic->getRoot(); ASSERT_TRUE(container); ASSERT_EQ(1, container->getChildCount()); @@ -2101,7 +2101,7 @@ TEST_F(GraphicComponentTest, KeyboardPress) { TEST_F(GraphicComponentTest, KeyboardPressNoFocus) { loadDocument(SIMPLE_PRESS); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto container = graphic->getRoot(); ASSERT_TRUE(container); ASSERT_EQ(1, container->getChildCount()); @@ -2718,7 +2718,7 @@ TEST_F(GraphicComponentTest, EnumParameterBinding) { loadDocument(ENUM_PARAMETER_BINDING); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto path = graphic->getRoot()->getChildAt(0); ASSERT_EQ(kGraphicElementTypePath, path->getType()); diff --git a/unit/graphic/unittest_graphic_data.cpp b/unit/graphic/unittest_graphic_data.cpp index 62227f7..889aa48 100644 --- a/unit/graphic/unittest_graphic_data.cpp +++ b/unit/graphic/unittest_graphic_data.cpp @@ -68,7 +68,7 @@ TEST_F(GraphicDataTest, MultipleTopLevelChildren) loadDocument(MULTIPLE_TOP_LEVEL_CHILDREN); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto box = graphic->getRoot(); @@ -132,7 +132,7 @@ TEST_F(GraphicDataTest, DataBinding) loadDocument(DATA_BINDING); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto box = graphic->getRoot(); @@ -197,7 +197,7 @@ TEST_F(GraphicDataTest, DataBindingToItems) loadDocument(DATA_BINDING_TO_ITEMS); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); ASSERT_TRUE(graphic); auto box = graphic->getRoot(); @@ -261,7 +261,7 @@ TEST_F(GraphicDataTest, Grid) loadDocument(GRID); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto box = graphic->getRoot(); ASSERT_EQ(3, box->getChildCount()); @@ -308,7 +308,7 @@ TEST_F(GraphicDataTest, TestVersion) loadDocument(TEST_VERSION); ASSERT_TRUE(component); - auto graphic = component->getCalculated(kPropertyGraphic).getGraphic(); + auto graphic = component->getCalculated(kPropertyGraphic).get(); auto box = graphic->getRoot(); // Data is ignored, so a single item should be inflated diff --git a/unit/graphic/unittest_graphic_filters.cpp b/unit/graphic/unittest_graphic_filters.cpp index 6d8a02e..ff2b9ed 100644 --- a/unit/graphic/unittest_graphic_filters.cpp +++ b/unit/graphic/unittest_graphic_filters.cpp @@ -34,12 +34,12 @@ TEST(GraphicFilterTest, Basic) JsonData json(R"({"type":"DropShadow"})"); auto f = GraphicFilter::create(*context, json.get()); - ASSERT_TRUE(f.isGraphicFilter()); - ASSERT_EQ(kGraphicFilterTypeDropShadow, f.getGraphicFilter().getType()); - ASSERT_TRUE(IsEqual(Color::BLACK, f.getGraphicFilter().getValue(kGraphicPropertyFilterColor))); - ASSERT_TRUE(IsEqual(Object(0), f.getGraphicFilter().getValue(kGraphicPropertyFilterHorizontalOffset))); - ASSERT_TRUE(IsEqual(Object(0), f.getGraphicFilter().getValue(kGraphicPropertyFilterRadius))); - ASSERT_TRUE(IsEqual(Object(0), f.getGraphicFilter().getValue(kGraphicPropertyFilterVerticalOffset))); + ASSERT_TRUE(f.is()); + ASSERT_EQ(kGraphicFilterTypeDropShadow, f.get().getType()); + ASSERT_TRUE(IsEqual(Color::BLACK, f.get().getValue(kGraphicPropertyFilterColor))); + ASSERT_TRUE(IsEqual(Object(0), f.get().getValue(kGraphicPropertyFilterHorizontalOffset))); + ASSERT_TRUE(IsEqual(Object(0), f.get().getValue(kGraphicPropertyFilterRadius))); + ASSERT_TRUE(IsEqual(Object(0), f.get().getValue(kGraphicPropertyFilterVerticalOffset))); } TEST(GraphicFilterTest, BadGraphicFilter) @@ -49,7 +49,7 @@ TEST(GraphicFilterTest, BadGraphicFilter) JsonData json(R"({"type":"DropShadoww"})"); auto f = GraphicFilter::create(*context, json.get()); - ASSERT_FALSE(f.isGraphicFilter()); + ASSERT_FALSE(f.is()); ASSERT_EQ(Object::NULL_OBJECT(), f); } @@ -91,8 +91,8 @@ TEST(GraphicFilterTest, DropShadowGraphicFilter) for (auto& m : DROP_SHADOW_TESTS) { JsonData json(m.json); auto filterObject = GraphicFilter::create(*context, json.get()); - ASSERT_TRUE(filterObject.isGraphicFilter()) << m.json; - const auto& filter = filterObject.getGraphicFilter(); + ASSERT_TRUE(filterObject.is()) << m.json; + const auto& filter = filterObject.get(); ASSERT_EQ(kGraphicFilterTypeDropShadow, filter.getType()) << m.json; ASSERT_TRUE(IsEqual(m.color, filter.getValue(kGraphicPropertyFilterColor).asColor(*context))) << m.json; ASSERT_TRUE(IsEqual(m.horizontalOffset, filter.getValue(kGraphicPropertyFilterHorizontalOffset))) << m.json; @@ -108,11 +108,11 @@ TEST(GraphicFilterTest, ResourceSubstitution) JsonData json1(R"({"type": "DropShadow", "radius": "@filterSize"})"); auto f = GraphicFilter::create(*context, json1.get()); - ASSERT_TRUE(f.isGraphicFilter()); - ASSERT_EQ(Object(10), f.getGraphicFilter().getValue(kGraphicPropertyFilterRadius)); + ASSERT_TRUE(f.is()); + ASSERT_EQ(Object(10), f.get().getValue(kGraphicPropertyFilterRadius)); JsonData json2(R"({"type": "DropShadow", "radius": "${@filterSize * 2}"})"); f = GraphicFilter::create(*context, json2.get()); - ASSERT_TRUE(f.isGraphicFilter()); - ASSERT_EQ(Object(20), f.getGraphicFilter().getValue(kGraphicPropertyFilterRadius)); + ASSERT_TRUE(f.is()); + ASSERT_EQ(Object(20), f.get().getValue(kGraphicPropertyFilterRadius)); } \ No newline at end of file diff --git a/unit/livedata/unittest_livearray_rebuild.cpp b/unit/livedata/unittest_livearray_rebuild.cpp index b916662..17c4000 100644 --- a/unit/livedata/unittest_livearray_rebuild.cpp +++ b/unit/livedata/unittest_livearray_rebuild.cpp @@ -68,7 +68,7 @@ class LiveArrayRebuildTest : public DocumentWrapper { s = child->getCalculated(kPropertySource) .getArray() .at(0) - .getURLRequest().getUrl(); + .get().getUrl(); } else if (child->getCalculated(kPropertySource).isString()) { s = child->getCalculated(kPropertySource).getString(); } @@ -829,8 +829,8 @@ TEST_F(LiveArrayRebuildTest, MultipleContexts) auto root1 = RootContext::create(metrics, content1, *config); auto root2 = RootContext::create(metrics, content1, *config); - auto component1 = std::dynamic_pointer_cast(root1->topComponent()); - auto component2 = std::dynamic_pointer_cast(root2->topComponent()); + auto component1 = CoreComponent::cast(root1->topComponent()); + auto component2 = CoreComponent::cast(root2->topComponent()); ASSERT_TRUE(CheckComponentChildOrder(component1, {"a", "b", "c", "d", "e", "f"})); ASSERT_TRUE(CheckComponentChildOrder(component2, {"a", "b", "c", "d", "e", "f"})); @@ -1659,7 +1659,7 @@ static ::testing::AssertionResult CheckSpacing(const CoreComponentPtr& comp, float spacing) { float ypos = 0; for (int i = 0; i < comp->getChildCount(); i++) { - auto rect = comp->getCoreChildAt(i)->getCalculated(kPropertyBounds).getRect(); + auto rect = comp->getCoreChildAt(i)->getCalculated(kPropertyBounds).get(); if (ypos != rect.getTop()) { return ::testing::AssertionFailure() << "Position wrong on: " << i << " expected='" << ypos @@ -1668,7 +1668,7 @@ CheckSpacing(const CoreComponentPtr& comp, float spacing) { ypos += (rect.getHeight() + spacing); } // Last one should end without spacing - auto lastChildRect = comp->getCoreChildAt(comp->getChildCount() - 1)->getCalculated(kPropertyBounds).getRect(); + auto lastChildRect = comp->getCoreChildAt(comp->getChildCount() - 1)->getCalculated(kPropertyBounds).get(); ypos -= spacing; if (ypos != lastChildRect.getBottom()) { return ::testing::AssertionFailure() << "Last child too big"; @@ -1919,7 +1919,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerRowFull) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"row\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -1934,7 +1934,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerRowReverseFull) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"rowReverse\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -1949,7 +1949,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerColumnFull) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"column\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -1964,7 +1964,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerColumnReverseFull) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"columnReverse\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -1982,7 +1982,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerChangeDirection) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"row\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2042,7 +2042,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerRowFullRTL) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"row\", \"layoutDir\": \"RTL\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2057,7 +2057,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerRowReverseFullRTL) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"rowReverse\", \"layoutDir\": \"RTL\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2072,7 +2072,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerColumnFullRTL) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"column\", \"layoutDir\": \"RTL\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2087,7 +2087,7 @@ TEST_F(LiveArrayRebuildTest, SpacedContainerColumnReverseFullRTL) { loadDocument(SPACED_CONTAINER_WITH_LAYOUTDIR, "{\"containerDir\": \"columnReverse\", \"layoutDir\": \"RTL\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("container")); + auto cont = CoreComponent::cast(root->context().findComponentById("container")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2140,7 +2140,7 @@ TEST_F(LiveArrayRebuildTest, SpacedSequenceChangeDirectionHorizontal) { loadDocument(SPACED_SEQUENCE_WITH_LAYOUTDIR, "{\"scrollDir\": \"horizontal\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("sequence")); + auto cont = CoreComponent::cast(root->context().findComponentById("sequence")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); @@ -2175,7 +2175,7 @@ TEST_F(LiveArrayRebuildTest, SpacedSequenceChangeDirectionVertical) { loadDocument(SPACED_SEQUENCE_WITH_LAYOUTDIR, "{\"scrollDir\": \"vertical\", \"layoutDir\": \"LTR\"}"); root->clearPending(); - auto cont = std::dynamic_pointer_cast(root->context().findComponentById("sequence")); + auto cont = CoreComponent::cast(root->context().findComponentById("sequence")); auto c = cont->getChildAt(0); auto c2 = cont->getChildAt(1); auto c3 = cont->getChildAt(2); diff --git a/unit/media/testmediaplayer.cpp b/unit/media/testmediaplayer.cpp index d769512..15ad69c 100644 --- a/unit/media/testmediaplayer.cpp +++ b/unit/media/testmediaplayer.cpp @@ -16,6 +16,8 @@ #include "testmediaplayer.h" #include "testmediaplayerfactory.h" +#include "apl/utils/log.h" + namespace apl { const bool DEBUG_MP = false; diff --git a/unit/media/testmediaplayerfactory.h b/unit/media/testmediaplayerfactory.h index ac48d6f..1a29ebc 100644 --- a/unit/media/testmediaplayerfactory.h +++ b/unit/media/testmediaplayerfactory.h @@ -44,7 +44,7 @@ class TestMediaPlayerFactory : public MediaPlayerFactory, * MediaPlayerFactory interface */ MediaPlayerPtr createPlayer( MediaPlayerCallback callback ) override { - auto self = std::dynamic_pointer_cast(shared_from_this()); + auto self = std::static_pointer_cast(shared_from_this()); auto player = std::make_shared(std::move(callback), std::move(self)); if (mEventCallback) player->setEventCallback(mEventCallback); mPlayers.emplace_back(player); diff --git a/unit/media/unittest_media_manager.cpp b/unit/media/unittest_media_manager.cpp index 5c01fa5..2d4f0b0 100644 --- a/unit/media/unittest_media_manager.cpp +++ b/unit/media/unittest_media_manager.cpp @@ -434,6 +434,28 @@ TEST_F(MediaManagerTest, VectorGraphicFailure) { ASSERT_EQ(kMediaStateError, component->getCalculated(kPropertyMediaState).getInteger()); } +TEST_F(MediaManagerTest, VectorGraphicChange) +{ + loadDocument(VECTOR_GRAPHIC_DOCUMENT); + + ASSERT_FALSE(root->isDirty()); + + // Let the first one load + ASSERT_TRUE(MediaRequested(kEventMediaTypeVectorGraphic, "http://myPillShape")); + ASSERT_EQ(kMediaStatePending, component->getCalculated(apl::kPropertyMediaState).getInteger()); + ASSERT_TRUE(CheckLoadedMedia(component, "http://myPillShape")); + ASSERT_EQ(kMediaStateReady, component->getCalculated(apl::kPropertyMediaState).getInteger()); + + // Change the source property + component->setProperty(kPropertySource, "http://foobar"); + ASSERT_TRUE(MediaRequested(kEventMediaTypeVectorGraphic, "http://foobar")); + ASSERT_TRUE(CheckDirty(component, kPropertySource, kPropertyMediaState, kPropertyVisualHash)); + ASSERT_EQ(kMediaStatePending, component->getCalculated(apl::kPropertyMediaState).getInteger()); + ASSERT_TRUE(CheckLoadedMedia(component, "http://foobar")); + ASSERT_EQ(kMediaStateReady, component->getCalculated(apl::kPropertyMediaState).getInteger()); +} + + static const char* VECTOR_GRAPHIC_LOCAL_SOURCE_DOCUMENT = R"( { "type": "APL", @@ -845,12 +867,12 @@ TEST_F(MediaManagerTest, SingleImageLoadChangeSourceTriggersOnLoad) ASSERT_EQ("bravo", textComponent->getCalculated(kPropertyText).asString()); - auto textComponentMedia = std::dynamic_pointer_cast(textComponent); + auto textComponentMedia = TextComponent::cast(textComponent); textComponentMedia->setProperty(kPropertyText, "torpedo"); ASSERT_EQ("torpedo", textComponent->getCalculated(kPropertyText).asString()); - auto imageComponent = std::dynamic_pointer_cast(root->findComponentById("myImage")); + auto imageComponent = ImageComponent::cast(root->findComponentById("myImage")); imageComponent->setProperty(kPropertySource, "universe1"); ASSERT_TRUE(CheckDirty(imageComponent, kPropertySource, kPropertyMediaState, kPropertyVisualHash)); ASSERT_TRUE(MediaRequested(kEventMediaTypeImage, "universe1")); @@ -1503,7 +1525,7 @@ TEST_F(MediaManagerTest, SingleImageOnLoadReinflate) configChangeReinflate(ConfigurationChange(500, 1000)); - component = std::static_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component); ASSERT_TRUE(MediaRequested(kEventMediaTypeImage, "source1")); @@ -1545,7 +1567,7 @@ TEST_F(MediaManagerTest, SingleImageReinflate) configChangeReinflate(ConfigurationChange(500, 1000)); - component = std::static_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component); ASSERT_TRUE(MediaRequested(kEventMediaTypeImage, "source1")); @@ -1599,7 +1621,7 @@ TEST_F(MediaManagerTest, SingleImageOnLoadReinflateSame) configChangeReinflate(ConfigurationChange(500, 1000)); - component = std::static_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component); ASSERT_TRUE(CheckSendEvent(root, "loaded0")); @@ -1641,7 +1663,7 @@ TEST_F(MediaManagerTest, SingleImageReinflateSame) configChangeReinflate(ConfigurationChange(500, 1000)); - component = std::static_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); ASSERT_TRUE(component); root->mediaLoaded("source0"); @@ -1725,7 +1747,7 @@ TEST_F(MediaManagerTest, ImageWithSourcesAsArraryWithHeaders) { auto sources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(sources.size(), 1); - auto asSource = sources.at(0).getURLRequest(); + auto asSource = sources.at(0).get(); auto headers = asSource.getHeaders(); ASSERT_EQ(headers.size(), 1); ASSERT_EQ(headers.at(0), "A: header"); @@ -1759,7 +1781,7 @@ TEST_F(MediaManagerTest, ImageWithSourcesAsArraryWithHeadersAsString) { auto sources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(sources.size(), 1); - auto asSource = sources.at(0).getURLRequest(); + auto asSource = sources.at(0).get(); auto headers = asSource.getHeaders(); ASSERT_EQ(headers.size(), 1); ASSERT_EQ(headers.at(0), "A: Let me in"); @@ -1795,7 +1817,7 @@ TEST_F(MediaManagerTest, ImageSourceAsObjectWithHeaders) { // Based on the spec, we will "array-fy" the property auto sources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(sources.size(), 1); - auto asSource = sources.at(0).getURLRequest(); + auto asSource = sources.at(0).get(); auto headers = asSource.getHeaders(); ASSERT_EQ(headers.size(), 1); ASSERT_EQ(headers.at(0), "A: Let me in please"); @@ -1846,8 +1868,8 @@ TEST_F(MediaManagerTest, MultipleImagesWithHeaders) { auto sources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(sources.size(), 4); - ASSERT_TRUE(sources.at(0).isURLRequest()); - auto asSource0 = sources.at(0).getURLRequest(); + ASSERT_TRUE(sources.at(0).is()); + auto asSource0 = sources.at(0).get(); ASSERT_EQ(asSource0.getUrl(), "universe0"); auto headers0 = asSource0.getHeaders(); ASSERT_EQ(headers0.size(), 1); @@ -1857,12 +1879,12 @@ TEST_F(MediaManagerTest, MultipleImagesWithHeaders) { auto asSource1 = sources.at(1).getString(); ASSERT_EQ(asSource1, "universe1"); - auto asSource2 = sources.at(2).getURLRequest(); + auto asSource2 = sources.at(2).get(); ASSERT_EQ(asSource2.getUrl(), "universe2"); auto headers2 = asSource2.getHeaders(); ASSERT_EQ(headers2.size(), 0); - auto asSource3 = sources.at(3).getURLRequest(); + auto asSource3 = sources.at(3).get(); ASSERT_EQ(asSource3.getUrl(), "universe3"); auto headers3 = asSource3.getHeaders(); ASSERT_EQ(headers3.size(), 2); @@ -1918,8 +1940,8 @@ TEST_F(MediaManagerTest, MultipleImagesWithFiltersAndHeaders) { auto sources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(sources.size(), 4); - ASSERT_TRUE(sources.at(0).isURLRequest()); - auto asSoource0 = sources.at(0).getURLRequest(); + ASSERT_TRUE(sources.at(0).is()); + auto asSoource0 = sources.at(0).get(); ASSERT_EQ(asSoource0.getUrl(), "universe0"); auto headers0 = asSoource0.getHeaders(); ASSERT_EQ(headers0.size(), 1); @@ -1929,12 +1951,12 @@ TEST_F(MediaManagerTest, MultipleImagesWithFiltersAndHeaders) { auto asSource1 = sources.at(1).getString(); ASSERT_EQ(asSource1, "universe1"); - auto asSource2 = sources.at(2).getURLRequest(); + auto asSource2 = sources.at(2).get(); ASSERT_EQ(asSource2.getUrl(), "universe2"); auto headers2 = asSource2.getHeaders(); ASSERT_EQ(headers2.size(), 0); - auto asSource3 = sources.at(3).getURLRequest(); + auto asSource3 = sources.at(3).get(); ASSERT_EQ(asSource3.getUrl(), "universe3"); auto headers3 = asSource3.getHeaders(); ASSERT_EQ(headers3.size(), 2); @@ -1969,8 +1991,8 @@ TEST_F(MediaManagerTest, VectorGraphicWithHeaders) { ASSERT_TRUE(CheckLoadedMedia(component, "universe0")); auto sourceProp = component->getCalculated(kPropertySource); - ASSERT_TRUE(sourceProp.isURLRequest()); - auto asSource = sourceProp.getURLRequest(); + ASSERT_TRUE(sourceProp.is()); + auto asSource = sourceProp.get(); auto headers = asSource.getHeaders(); ASSERT_EQ(headers.size(), 1); ASSERT_EQ(headers.at(0), "A: Let me in"); @@ -1998,9 +2020,9 @@ TEST_F(MediaManagerTest, VideoWithWrongHeaders) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 0); @@ -2029,9 +2051,9 @@ TEST_F(MediaManagerTest, VideoWithHeaders) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2046,9 +2068,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersSetsDirtyAfterHeaderChange) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2070,8 +2092,8 @@ TEST_F(MediaManagerTest, VideoWithHeadersSetsDirtyAfterHeaderChange) { mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); - asMediaSource = mediaSources.at(0).getMediaSource(); + ASSERT_TRUE(mediaSources.at(0).is()); + asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2092,8 +2114,8 @@ TEST_F(MediaManagerTest, VideoWithHeadersSetsDirtyAfterHeaderChange) { ASSERT_TRUE(CheckDirty(component, kPropertySource, kPropertyVisualHash)); mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); - asMediaSource = mediaSources.at(0).getMediaSource(); + ASSERT_TRUE(mediaSources.at(0).is()); + asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2109,9 +2131,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersDenyUppercase) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 0); @@ -2126,9 +2148,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersDenyLowercase) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 0); @@ -2143,9 +2165,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersAllowListUppercase) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2161,9 +2183,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersAllowListLowercase) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2179,9 +2201,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersAllowListNotPresent) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2217,9 +2239,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersAllowRegex) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 2); @@ -2240,9 +2262,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersDenyRegex) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2261,9 +2283,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersDenyAllRegex) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2298,9 +2320,9 @@ TEST_F(MediaManagerTest, VideoWithHeadersAcceptContentTypeDenyAllRegex) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 1); @@ -2338,9 +2360,9 @@ TEST_F(MediaManagerTest, VideoWithDuplicatedHeaders) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 2); @@ -2399,9 +2421,9 @@ TEST_F(MediaManagerTest, VideoWithMultipleHeaders) { // Based on the spec, sources get transformed into an array auto mediaSources = component->getCalculated(kPropertySource).getArray(); ASSERT_EQ(mediaSources.size(), 1); - ASSERT_TRUE(mediaSources.at(0).isMediaSource()); + ASSERT_TRUE(mediaSources.at(0).is()); - auto asMediaSource = mediaSources.at(0).getMediaSource(); + auto asMediaSource = mediaSources.at(0).get(); ASSERT_EQ(asMediaSource.getDescription(), "milky way"); auto headers = asMediaSource.getHeaders(); ASSERT_EQ(headers.size(), 5); diff --git a/unit/media/unittest_media_player.cpp b/unit/media/unittest_media_player.cpp index 0aed6bc..a90a570 100644 --- a/unit/media/unittest_media_player.cpp +++ b/unit/media/unittest_media_player.cpp @@ -805,6 +805,8 @@ TEST_F(MediaPlayerTest, PlayMedia) // Play a non-existent track. This will fail immediately executeCommand("PlayMedia", {{"componentId", "MyVideo"}, {"source", "track9" }}, false); + // A track fail terminates action which pauses the previously playing track + ASSERT_TRUE(CheckSendEvent(root, "Pause track3 250/P")); ASSERT_TRUE(CheckSendEvent(root, "Play track9 0/")); ASSERT_TRUE(CheckPlayerEvents(eventCounts, { @@ -854,6 +856,74 @@ TEST_F(MediaPlayerTest, PlayMedia) ASSERT_TRUE(CheckSendEvent(root, "TrackReady track3 0/P")); } +/** + * Check that the mediaplayer is paused when the screen is touched during a PlayMedia command execution + * and the audioTrack is foreground + */ +TEST_F(MediaPlayerTest, PlayMediaTerminationByTap) +{ + mediaPlayerFactory->addFakeContent({ + {"track1", 1000, 100, -1}, // 1000 ms long, 100 ms buffer delay + {"track2", 2000, 100, 1200}, // 2000 ms long, 100 ms buffer delay, fails at 1200 ms + {"track3", 500, 0, -1} // 500 ms long, no buffer delay + }); + + loadDocument(PLAY_MEDIA); + ASSERT_TRUE(component); + + ASSERT_TRUE(CheckPlayerEvents(eventCounts, { + {TestMediaPlayer::EventType::kPlayerEventSetTrackList, 1}, + {TestMediaPlayer::EventType::kPlayerEventSetAudioTrack, 1} + }) + ); + eventCounts.clear(); + + // After 100 milliseconds nothing happens + mediaPlayerFactory->advanceTime(100); + ASSERT_FALSE(root->hasEvent()); + + // Play an existing track with audioTrack background + executeCommand("PlayMedia", {{"componentId", "MyVideo"}, {"source", "track3" }}, false); + ASSERT_TRUE(CheckSendEvent(root, "Play track3 0/")); + + ASSERT_TRUE(CheckPlayerEvents(eventCounts, { + {TestMediaPlayer::EventType::kPlayerEventSetTrackList, 1}, + {TestMediaPlayer::EventType::kPlayerEventSetAudioTrack, 1}, + {TestMediaPlayer::EventType::kPlayerEventPlay, 1} + }) + ); + eventCounts.clear(); + + mediaPlayerFactory->advanceTime(250); + ASSERT_TRUE(CheckSendEvent(root, "TrackReady track3 0/")); + ASSERT_TRUE(CheckSendEvent(root, "TimeUpdate track3 250/")); + + performTap(1, 100); + ASSERT_TRUE(CheckSendEvent(root, "Pause track3 250/P")); + + // After 100 milliseconds nothing happens + mediaPlayerFactory->advanceTime(100); + ASSERT_FALSE(root->hasEvent()); + + // Play an existing track with audioTrack background + executeCommand("PlayMedia", {{"componentId", "MyVideo"}, {"source", "track3" }, {"audioTrack", "background"}}, false); + ASSERT_TRUE(CheckSendEvent(root, "Play track3 0/")); + ASSERT_TRUE(CheckPlayerEvents(eventCounts, { + {TestMediaPlayer::EventType::kPlayerEventSetTrackList, 1}, + {TestMediaPlayer::EventType::kPlayerEventSetAudioTrack, 1}, + {TestMediaPlayer::EventType::kPlayerEventPlay, 1} + }) + ); + eventCounts.clear(); + + mediaPlayerFactory->advanceTime(250); + ASSERT_TRUE(CheckSendEvent(root, "TrackReady track3 0/")); + ASSERT_TRUE(CheckSendEvent(root, "TimeUpdate track3 250/")); + + performTap(1, 100); + // Player is not paused if audioTrack is anything other than foreground + ASSERT_FALSE(CheckSendEvent(root, "Pause track3 250/P")); +} static const char *PLAY_MEDIA_IN_SEQUENCE = R"apl( { @@ -1385,8 +1455,8 @@ TEST_F(MediaPlayerTest, DestroyMediaPlayer) auto child = component->getChildAt(0); ASSERT_TRUE(child->getType() == kComponentTypeVideo); - auto mp = std::dynamic_pointer_cast(child)->getMediaPlayer(); - ASSERT_FALSE(std::dynamic_pointer_cast(mp)->isReleased()); + auto mp = child->getMediaPlayer(); + ASSERT_FALSE(std::static_pointer_cast(mp)->isReleased()); ASSERT_TRUE(child->remove()); child = nullptr; // This should release the media player @@ -1396,7 +1466,7 @@ TEST_F(MediaPlayerTest, DestroyMediaPlayer) root->clearPending(); root->clearVisualContextDirty(); - ASSERT_TRUE(std::dynamic_pointer_cast(mp)->isReleased()); + ASSERT_TRUE(std::static_pointer_cast(mp)->isReleased()); } static const char *MUTE_MEDIA_PLAYER = R"apl( @@ -1426,8 +1496,8 @@ TEST_F(MediaPlayerTest, MuteVideo) { auto child = component->getChildAt(0); ASSERT_TRUE(child->getType() == kComponentTypeVideo); - auto mp = std::dynamic_pointer_cast(child)->getMediaPlayer(); - auto testMediaPlayer = std::dynamic_pointer_cast(mp); + auto mp = child->getMediaPlayer(); + auto testMediaPlayer = std::static_pointer_cast(mp); ASSERT_TRUE(testMediaPlayer->isMuted()); executeCommand("SetValue", {{"componentId", "MyVideo"}, {"property", "muted"}, {"value", false}}, false); diff --git a/unit/primitives/unittest_color.cpp b/unit/primitives/unittest_color.cpp index d86a61d..7c58a3b 100644 --- a/unit/primitives/unittest_color.cpp +++ b/unit/primitives/unittest_color.cpp @@ -95,7 +95,13 @@ TEST_F(ColorTest, Basic) const static std::vector sErrorTests = { "rgb(123 ", "bluz", - "hsl(120, 0, 0, )" + "hsl(120, 0, 0, )", + "#fefefefefefefe", + "hsl(120, 0, 0, 48, 75)", + "hsl(120, 0)", + "rgb(green, 50%, 78, 54, 78)", + "rgb(green)", + "hsl(nan, 1, 1)", }; TEST_F(ColorTest, Error) diff --git a/unit/primitives/unittest_dimension.cpp b/unit/primitives/unittest_dimension.cpp index 5a7e527..fa007cf 100644 --- a/unit/primitives/unittest_dimension.cpp +++ b/unit/primitives/unittest_dimension.cpp @@ -71,19 +71,35 @@ ::testing::AssertionResult IsRelative(double value, const Dimension& dimen) TEST_F(DimensionTest, Basic) { - EXPECT_TRUE(Dimension(*c, "auto").isAuto()); - EXPECT_FALSE(Dimension(*c, "auto").isRelative()); - EXPECT_FALSE(Dimension(*c, "auto").isAbsolute()); - - EXPECT_TRUE(Dimension(*c, "10px").isAbsolute()); - EXPECT_FALSE(Dimension(*c, "10px").isRelative()); - EXPECT_FALSE(Dimension(*c, "10px").isAuto()); - EXPECT_EQ(5, Dimension(*c, "10px").getValue()); - - EXPECT_TRUE(Dimension(*c, "50%").isRelative()); - EXPECT_FALSE(Dimension(*c, "50%").isAbsolute()); - EXPECT_FALSE(Dimension(*c, "50%").isAuto()); - EXPECT_EQ(50, Dimension(*c, "50%").getValue()); + auto autoDim = Dimension(*c, "auto"); + EXPECT_TRUE(autoDim.isAuto()); + EXPECT_FALSE(autoDim.isRelative()); + EXPECT_FALSE(autoDim.isAbsolute()); + + auto absoluteDim = Dimension(*c, "10px"); + EXPECT_TRUE(absoluteDim.isAbsolute()); + EXPECT_FALSE(absoluteDim.isRelative()); + EXPECT_FALSE(absoluteDim.isAuto()); + EXPECT_EQ(5, absoluteDim.getValue()); + + auto absoluteDimObj = Object(absoluteDim); + ASSERT_TRUE(absoluteDimObj.asDimension(*c).isAbsolute()); + ASSERT_TRUE(absoluteDimObj.asAbsoluteDimension(*c).isAbsolute()); + ASSERT_TRUE(absoluteDimObj.asNonAutoDimension(*c).isAbsolute()); + ASSERT_TRUE(absoluteDimObj.asNonAutoRelativeDimension(*c).isAbsolute()); + ASSERT_EQ("AbsDim<5.000000>", absoluteDimObj.toDebugString()); + + auto relativeDim = Dimension(*c, "50%"); + EXPECT_TRUE(relativeDim.isRelative()); + EXPECT_FALSE(relativeDim.isAbsolute()); + EXPECT_FALSE(relativeDim.isAuto()); + EXPECT_EQ(50, relativeDim.getValue()); + + auto relativeDimObj = Object(relativeDim); + ASSERT_TRUE(relativeDimObj.asDimension(*c).isRelative()); + ASSERT_TRUE(relativeDimObj.asNonAutoDimension(*c).isRelative()); + ASSERT_TRUE(relativeDimObj.asNonAutoRelativeDimension(*c).isRelative()); + ASSERT_EQ("RelDim<50.000000>", relativeDimObj.toDebugString()); EXPECT_TRUE(Dimension(*c, " auto ").isAuto()); diff --git a/unit/primitives/unittest_filters.cpp b/unit/primitives/unittest_filters.cpp index e790163..fa31cd9 100644 --- a/unit/primitives/unittest_filters.cpp +++ b/unit/primitives/unittest_filters.cpp @@ -35,10 +35,10 @@ TEST(FilterTest, Basic) JsonData json(R"({"type":"Blur", "radius": 10})"); auto f = Filter::create(*context, json.get()); - ASSERT_TRUE(f.isFilter()); - ASSERT_EQ(kFilterTypeBlur, f.getFilter().getType()); - ASSERT_TRUE(IsEqual(Dimension(10), f.getFilter().getValue(kFilterPropertyRadius))); - ASSERT_TRUE(IsEqual(-1, f.getFilter().getValue(kFilterPropertySource))); + ASSERT_TRUE(f.is()); + ASSERT_EQ(kFilterTypeBlur, f.get().getType()); + ASSERT_TRUE(IsEqual(Dimension(10), f.get().getValue(kFilterPropertyRadius))); + ASSERT_TRUE(IsEqual(-1, f.get().getValue(kFilterPropertySource))); } TEST(FilterTest, BadFilter) @@ -48,7 +48,7 @@ TEST(FilterTest, BadFilter) JsonData json(R"({"type":"Blurry", "radius": 10})"); auto f = Filter::create(*context, json.get()); - ASSERT_FALSE(f.isFilter()); + ASSERT_FALSE(f.is()); ASSERT_EQ(Object::NULL_OBJECT(), f); } @@ -86,8 +86,8 @@ TEST(FilterTest, BlendFilter) for (auto& m : BLEND_TESTS) { JsonData json(m.json); auto filterObject = Filter::create(*context, json.get()); - ASSERT_TRUE(filterObject.isFilter()) << m.json; - const auto& filter = filterObject.getFilter(); + ASSERT_TRUE(filterObject.is()) << m.json; + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeBlend, filter.getType()) << m.json; ASSERT_TRUE(IsEqual(m.destination, filter.getValue(kFilterPropertyDestination))) << m.json; ASSERT_TRUE(IsEqual(m.mode, filter.getValue(kFilterPropertyMode))) << m.json; @@ -122,8 +122,8 @@ TEST(FilterTest, BlurFilter) for (auto& m : BLUR_TESTS) { JsonData json(m.json); auto filterObject = Filter::create(*context, json.get()); - ASSERT_TRUE(filterObject.isFilter()) << m.json; - const auto& filter = filterObject.getFilter(); + ASSERT_TRUE(filterObject.is()) << m.json; + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeBlur, filter.getType()) << m.json; ASSERT_TRUE(IsEqual(m.radius, filter.getValue(kFilterPropertyRadius))) << m.json; ASSERT_TRUE(IsEqual(m.source, filter.getValue(kFilterPropertySource))) << m.json; @@ -149,8 +149,8 @@ TEST(FilterTest, ColorFilter) { for (auto& m : COLOR_TESTS) { JsonData json(m.json); auto filterObject = Filter::create(*context, json.get()); - ASSERT_TRUE(filterObject.isFilter()) << m.json; - const auto& filter = filterObject.getFilter(); + ASSERT_TRUE(filterObject.is()) << m.json; + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeColor, filter.getType()) << m.json; ASSERT_TRUE(IsEqual(m.color, filter.getValue(kFilterPropertyColor))) << m.json; } @@ -198,15 +198,15 @@ TEST(FilterTest, GradientFilter) { for (auto& m : GRADIENT_TESTS) { JsonData json(m.json); auto filterObject = Filter::create(*context, json.get()); - ASSERT_TRUE(filterObject.isFilter()) << m.json; - const auto& filter = filterObject.getFilter(); + ASSERT_TRUE(filterObject.is()) << m.json; + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeGradient, filter.getType()) << m.json; const auto& gradient = filter.getValue(kFilterPropertyGradient); if (m.type == static_cast(-1)) { ASSERT_TRUE(gradient.isNull()) << m.json; } else { - const auto& g = gradient.getGradient(); + const auto& g = gradient.get(); ASSERT_EQ(m.type, g.getType()) << m.json; ASSERT_EQ(m.colorRange, g.getProperty(kGradientPropertyColorRange).getArray()) << m.json; ASSERT_EQ(m.inputRange, g.getProperty(kGradientPropertyInputRange).getArray()) << m.json; @@ -236,10 +236,10 @@ TEST(FilterTest, GrayscaleFilter) for (auto& m : GRAYSCALE_TESTS) { JsonData json(m.json); auto f = Filter::create(*context, json.get()); - ASSERT_TRUE(f.isFilter()) << m.json; - ASSERT_EQ(kFilterTypeGrayscale, f.getFilter().getType()) << m.json; - ASSERT_TRUE(IsEqual(m.amount, f.getFilter().getValue(kFilterPropertyAmount))) << m.json; - ASSERT_TRUE(IsEqual(m.source, f.getFilter().getValue(kFilterPropertySource))) << m.json; + ASSERT_TRUE(f.is()) << m.json; + ASSERT_EQ(kFilterTypeGrayscale, f.get().getType()) << m.json; + ASSERT_TRUE(IsEqual(m.amount, f.get().getValue(kFilterPropertyAmount))) << m.json; + ASSERT_TRUE(IsEqual(m.source, f.get().getValue(kFilterPropertySource))) << m.json; } } @@ -268,12 +268,12 @@ TEST(FilterTest, NoiseFilter) for (auto& m : NOISE_TESTS) { JsonData json(m.json); auto f = Filter::create(*context, json.get()); - ASSERT_TRUE(f.isFilter()) << m.json; - ASSERT_EQ(kFilterTypeNoise, f.getFilter().getType()) << m.json; - ASSERT_TRUE(IsEqual(m.useColor, f.getFilter().getValue(kFilterPropertyUseColor))) << m.json; - ASSERT_TRUE(IsEqual(m.kind, f.getFilter().getValue(kFilterPropertyKind))) << m.json; - ASSERT_TRUE(IsEqual(m.sigma, f.getFilter().getValue(kFilterPropertySigma))) << m.json; - ASSERT_TRUE(IsEqual(m.source, f.getFilter().getValue(kFilterPropertySource))) << m.json; + ASSERT_TRUE(f.is()) << m.json; + ASSERT_EQ(kFilterTypeNoise, f.get().getType()) << m.json; + ASSERT_TRUE(IsEqual(m.useColor, f.get().getValue(kFilterPropertyUseColor))) << m.json; + ASSERT_TRUE(IsEqual(m.kind, f.get().getValue(kFilterPropertyKind))) << m.json; + ASSERT_TRUE(IsEqual(m.sigma, f.get().getValue(kFilterPropertySigma))) << m.json; + ASSERT_TRUE(IsEqual(m.source, f.get().getValue(kFilterPropertySource))) << m.json; } } @@ -298,10 +298,10 @@ TEST(FilterTest, SaturateFilter) for (auto& m : SATURATE_TESTS) { JsonData json(m.json); auto f = Filter::create(*context, json.get()); - ASSERT_TRUE(f.isFilter()) << m.json; - ASSERT_EQ(kFilterTypeSaturate, f.getFilter().getType()) << m.json; - ASSERT_TRUE(IsEqual(m.amount, f.getFilter().getValue(kFilterPropertyAmount))) << m.json; - ASSERT_TRUE(IsEqual(m.source, f.getFilter().getValue(kFilterPropertySource))) << m.json; + ASSERT_TRUE(f.is()) << m.json; + ASSERT_EQ(kFilterTypeSaturate, f.get().getType()) << m.json; + ASSERT_TRUE(IsEqual(m.amount, f.get().getValue(kFilterPropertyAmount))) << m.json; + ASSERT_TRUE(IsEqual(m.source, f.get().getValue(kFilterPropertySource))) << m.json; } } @@ -313,8 +313,8 @@ TEST(FilterTest, ResourceSubstitution) JsonData json(R"({"type": "Blur", "radius": "${@filterSize * 2}"})"); auto f = Filter::create(*context, json.get()); - ASSERT_TRUE(f.isFilter()); - ASSERT_EQ(Object(Dimension(20)), f.getFilter().getValue(kFilterPropertyRadius)); + ASSERT_TRUE(f.is()); + ASSERT_EQ(Object(Dimension(20)), f.get().getValue(kFilterPropertyRadius)); } static const char *COMPONENT_FILTER = @@ -342,8 +342,8 @@ TEST_F(FilterTestDocument, InComponent) auto filters = component->getCalculated(kPropertyFilters); ASSERT_EQ(1, filters.size()); - ASSERT_EQ(kFilterTypeBlur, filters.at(0).getFilter().getType()); - ASSERT_EQ(Object(Dimension(20)), filters.at(0).getFilter().getValue(kFilterPropertyRadius)); + ASSERT_EQ(kFilterTypeBlur, filters.at(0).get().getType()); + ASSERT_EQ(Object(Dimension(20)), filters.at(0).get().getValue(kFilterPropertyRadius)); } static const char *COMPONENT_MIXED_FILTERS = @@ -378,13 +378,13 @@ TEST_F(FilterTestDocument, InComponentMixed) auto filters = component->getCalculated(kPropertyFilters); ASSERT_EQ(2, filters.size()); - ASSERT_EQ(kFilterTypeNoise, filters.at(0).getFilter().getType()); - ASSERT_TRUE(IsEqual(true, filters.at(0).getFilter().getValue(kFilterPropertyUseColor))); - ASSERT_TRUE(IsEqual(kFilterNoiseKindGaussian, filters.at(0).getFilter().getValue(kFilterPropertyKind))); - ASSERT_TRUE(IsEqual(10, filters.at(0).getFilter().getValue(kFilterPropertySigma))); + ASSERT_EQ(kFilterTypeNoise, filters.at(0).get().getType()); + ASSERT_TRUE(IsEqual(true, filters.at(0).get().getValue(kFilterPropertyUseColor))); + ASSERT_TRUE(IsEqual(kFilterNoiseKindGaussian, filters.at(0).get().getValue(kFilterPropertyKind))); + ASSERT_TRUE(IsEqual(10, filters.at(0).get().getValue(kFilterPropertySigma))); - ASSERT_EQ(kFilterTypeBlur, filters.at(1).getFilter().getType()); - ASSERT_TRUE(IsEqual(Dimension(10), filters.at(1).getFilter().getValue(kFilterPropertyRadius))); + ASSERT_EQ(kFilterTypeBlur, filters.at(1).get().getType()); + ASSERT_TRUE(IsEqual(Dimension(10), filters.at(1).get().getValue(kFilterPropertyRadius))); ASSERT_TRUE(ConsoleMessage()); // The Blurry filter should have generated a console message } @@ -431,9 +431,9 @@ TEST_F(FilterTestDocument, ExtensionWithSource) ASSERT_EQ(1, filters.size()); auto filterObject = filters.at(0); - ASSERT_TRUE(filterObject.isFilter()); + ASSERT_TRUE(filterObject.is()); - const auto& filter = filterObject.getFilter(); + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeExtension, filter.getType()); ASSERT_TRUE(IsEqual("aplext:CannyEdgeFilters:10", filter.getValue(kFilterPropertyExtensionURI))); ASSERT_TRUE(IsEqual("FindEdges", filter.getValue(kFilterPropertyName))); @@ -486,9 +486,9 @@ TEST_F(FilterTestDocument, ExtensionWithSourceAndDestination) ASSERT_EQ(1, filters.size()); auto filterObject = filters.at(0); - ASSERT_TRUE(filterObject.isFilter()); + ASSERT_TRUE(filterObject.is()); - const auto& filter = filterObject.getFilter(); + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeExtension, filter.getType()); ASSERT_TRUE(IsEqual("aplext:MorphingFilters:10", filter.getValue(kFilterPropertyExtensionURI))); ASSERT_TRUE(IsEqual("MergeTwo", filter.getValue(kFilterPropertyName))); @@ -546,9 +546,9 @@ TEST_F(FilterTestDocument, ExtensionNoInputImages) ASSERT_EQ(1, filters.size()); auto filterObject = filters.at(0); - ASSERT_TRUE(filterObject.isFilter()); + ASSERT_TRUE(filterObject.is()); - const auto& filter = filterObject.getFilter(); + const auto& filter = filterObject.get(); ASSERT_EQ(kFilterTypeExtension, filter.getType()); ASSERT_TRUE(IsEqual("aplext:NoiseGeneration:10", filter.getValue(kFilterPropertyExtensionURI))); ASSERT_TRUE(IsEqual("Perlin", filter.getValue(kFilterPropertyName))); diff --git a/unit/primitives/unittest_object.cpp b/unit/primitives/unittest_object.cpp index c343e46..c14b004 100644 --- a/unit/primitives/unittest_object.cpp +++ b/unit/primitives/unittest_object.cpp @@ -23,7 +23,12 @@ #include "apl/animation/easing.h" #include "apl/engine/context.h" +#include "apl/component/component.h" +#include "apl/component/componenteventsourcewrapper.h" #include "apl/content/metrics.h" +#include "apl/livedata/livedataobject.h" +#include "apl/livedata/livearrayobject.h" +#include "apl/livedata/livemapobject.h" #include "apl/primitives/object.h" #include "apl/primitives/gradient.h" #include "apl/primitives/rect.h" @@ -44,9 +49,9 @@ TEST(ObjectTest, Constants) ASSERT_TRUE( Object::NULL_OBJECT().isNull()); ASSERT_TRUE( Object::NAN_OBJECT().isNumber()); - ASSERT_TRUE( Object::AUTO_OBJECT().isAutoDimension()); + ASSERT_TRUE( Object(Dimension()).isAutoDimension()); ASSERT_TRUE( Object::EMPTY_ARRAY().isArray()); - ASSERT_TRUE( Object::EMPTY_RECT().isRect()); + ASSERT_TRUE( Object(Rect()).is()); } TEST(ObjectTest, Basic) @@ -101,7 +106,7 @@ TEST(ObjectTest, Size) ASSERT_EQ(0, a.size()); ASSERT_TRUE(Object::EMPTY_ARRAY().empty()); - ASSERT_TRUE(Object::EMPTY_RECT().empty()); + ASSERT_TRUE(Object(Rect()).empty()); } TEST(ObjectTest, SharedMap) @@ -209,7 +214,7 @@ TEST(ObjectTest, Color) auto session = std::make_shared(); Object o = Object(Color(Color::RED)); - ASSERT_TRUE(o.isColor()); + ASSERT_TRUE(o.is()); ASSERT_EQ(Color::RED, o.asColor(session)); o = Object(Color::RED); @@ -251,20 +256,20 @@ TEST(ObjectTest, Gradient) Object a = Gradient::create(*context, doc); - ASSERT_TRUE(a.isGradient()); - ASSERT_EQ(Gradient::RADIAL, a.getGradient().getType()); - ASSERT_EQ(0xff0000ff, a.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(a.is()); + ASSERT_EQ(Gradient::RADIAL, a.get().getType()); + ASSERT_EQ(0xff0000ff, a.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); Object b(a); - ASSERT_TRUE(b.isGradient()); - ASSERT_EQ(Gradient::RADIAL, b.getGradient().getType()); - ASSERT_EQ(0xff0000ff, b.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(b.is()); + ASSERT_EQ(Gradient::RADIAL, b.get().getType()); + ASSERT_EQ(0xff0000ff, b.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); Object c; c = a; - ASSERT_TRUE(c.isGradient()); - ASSERT_EQ(Gradient::RADIAL, c.getGradient().getType()); - ASSERT_EQ(0xff0000ff, c.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(c.is()); + ASSERT_EQ(Gradient::RADIAL, c.get().getType()); + ASSERT_EQ(0xff0000ff, c.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); { rapidjson::Document doc2; @@ -281,19 +286,19 @@ TEST(ObjectTest, Gradient) c = p; } - ASSERT_TRUE(c.isGradient()); - ASSERT_EQ(Gradient::LINEAR, c.getGradient().getType()); - ASSERT_EQ(0x0000ffff, c.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(c.is()); + ASSERT_EQ(Gradient::LINEAR, c.get().getType()); + ASSERT_EQ(0x0000ffff, c.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); b = c; - ASSERT_TRUE(b.isGradient()); - ASSERT_EQ(Gradient::LINEAR, b.getGradient().getType()); - ASSERT_EQ(0x0000ffff, b.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(b.is()); + ASSERT_EQ(Gradient::LINEAR, b.get().getType()); + ASSERT_EQ(0x0000ffff, b.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); // Make sure a has not changed - ASSERT_TRUE(a.isGradient()); - ASSERT_EQ(Gradient::RADIAL, a.getGradient().getType()); - ASSERT_EQ(0xff0000ff, a.getGradient().getProperty(kGradientPropertyColorRange).at(0).getColor()); + ASSERT_TRUE(a.is()); + ASSERT_EQ(Gradient::RADIAL, a.get().getType()); + ASSERT_EQ(0xff0000ff, a.get().getProperty(kGradientPropertyColorRange).at(0).getColor()); } const char *BAD_CASES = @@ -378,8 +383,8 @@ TEST(ObjectTest, MalformedGradient) TEST(ObjectTest, Rect) { Object a = Object(Rect(0,10,100,200)); - ASSERT_TRUE(a.isRect()); - auto r = a.getRect(); + ASSERT_TRUE(a.is()); + auto r = a.get(); ASSERT_EQ(0, r.getX()); ASSERT_EQ(10, r.getY()); ASSERT_EQ(100, r.getWidth()); @@ -404,59 +409,59 @@ TEST(ObjectTest, Transform) auto transform = Transformation::create(*context, arrayify(*context, Object(doc))); Object a = Object(transform); - ASSERT_TRUE(a.isTransform()); - ASSERT_EQ(Point(-20,-20), a.getTransformation()->get(20,20) * Point()); + ASSERT_TRUE(a.is()); + ASSERT_EQ(Point(-20,-20), a.get()->get(20,20) * Point()); } TEST(ObjectTest, Transform2) { Object a = Object(Transform2D::rotate(90)); - ASSERT_TRUE(a.isTransform2D()); - ASSERT_EQ(Transform2D::rotate(90), a.getTransform2D()); + ASSERT_TRUE(a.is()); + ASSERT_EQ(Transform2D::rotate(90), a.get()); } TEST(ObjectTest, Easing) { Object a = Object(Easing::linear()); - ASSERT_TRUE(a.isEasing()); - ASSERT_EQ(0.5, a.getEasing()->calc(0.5)); + ASSERT_TRUE(a.is()); + ASSERT_EQ(0.5, a.get()->calc(0.5)); auto session = makeDefaultSession(); a = Object(Easing::parse(session, "ease")); - ASSERT_TRUE(a.isEasing()); - ASSERT_NEAR(0.80240017, a.getEasing()->calc(0.5), 0.0001); + ASSERT_TRUE(a.is()); + ASSERT_NEAR(0.80240017, a.get()->calc(0.5), 0.0001); } TEST(ObjectTest, Radii) { Object a = Object(Radii()); - ASSERT_EQ(Object::EMPTY_RADII(), a); - ASSERT_TRUE(a.getRadii().empty()); + ASSERT_EQ(Object(Radii()), a); + ASSERT_TRUE(a.get().empty()); Object b = Object(Radii(4)); - ASSERT_TRUE(b.isRadii()); - ASSERT_EQ(4, b.getRadii().bottomLeft()); - ASSERT_EQ(4, b.getRadii().bottomRight()); - ASSERT_EQ(4, b.getRadii().topLeft()); - ASSERT_EQ(4, b.getRadii().topRight()); - ASSERT_FALSE(b.getRadii().empty()); + ASSERT_TRUE(b.is()); + ASSERT_EQ(4, b.get().bottomLeft()); + ASSERT_EQ(4, b.get().bottomRight()); + ASSERT_EQ(4, b.get().topLeft()); + ASSERT_EQ(4, b.get().topRight()); + ASSERT_FALSE(b.get().empty()); Object c = Object(Radii(1,2,3,4)); - ASSERT_TRUE(c.isRadii()); - ASSERT_EQ(1, c.getRadii().topLeft()); - ASSERT_EQ(2, c.getRadii().topRight()); - ASSERT_EQ(3, c.getRadii().bottomLeft()); - ASSERT_EQ(4, c.getRadii().bottomRight()); - ASSERT_EQ(1, c.getRadii().radius(Radii::kTopLeft)); - ASSERT_EQ(2, c.getRadii().radius(Radii::kTopRight)); - ASSERT_EQ(3, c.getRadii().radius(Radii::kBottomLeft)); - ASSERT_EQ(4, c.getRadii().radius(Radii::kBottomRight)); - ASSERT_EQ(Radii(1,2,3,4), c.getRadii()); - ASSERT_NE(Radii(1,2,3,5), c.getRadii()); - ASSERT_FALSE(c.getRadii().empty()); + ASSERT_TRUE(c.is()); + ASSERT_EQ(1, c.get().topLeft()); + ASSERT_EQ(2, c.get().topRight()); + ASSERT_EQ(3, c.get().bottomLeft()); + ASSERT_EQ(4, c.get().bottomRight()); + ASSERT_EQ(1, c.get().radius(Radii::kTopLeft)); + ASSERT_EQ(2, c.get().radius(Radii::kTopRight)); + ASSERT_EQ(3, c.get().radius(Radii::kBottomLeft)); + ASSERT_EQ(4, c.get().radius(Radii::kBottomRight)); + ASSERT_EQ(Radii(1,2,3,4), c.get()); + ASSERT_NE(Radii(1,2,3,5), c.get()); + ASSERT_FALSE(c.get().empty()); auto foo = std::array{1,2,3,4}; - ASSERT_EQ(foo, c.getRadii().get()); + ASSERT_EQ(foo, c.get().get()); } // NOTE: These test cases assume a '.' decimal separator. @@ -578,7 +583,7 @@ TEST(ObjectTest, AbsoluteDimensionConversion) TEST(ObjectTest, MutableObjects) { - ASSERT_FALSE(Object::EMPTY_RADII().isMutable()); + ASSERT_FALSE(Object(Radii()).isMutable()); ASSERT_FALSE(Object::NULL_OBJECT().isMutable()); ASSERT_FALSE(Object::EMPTY_ARRAY().isMutable()); @@ -592,11 +597,7 @@ TEST(ObjectTest, MutableObjects) ASSERT_TRUE(Object(mapPtr, true).isMutable()); // Check that retrieving the mutable map fails if the object is not marked as mutable - try { - Object(mapPtr).getMutableMap(); - FAIL(); - } catch (...) { - } + ASSERT_DEATH(Object(mapPtr).getMutableMap(), "Attempted to retrieve mutable map for non-mutable object"); // Check that retrieving the mutable map succeeds if the object is marked as mutable ObjectMap& map = Object(mapPtr, true).getMutableMap(); @@ -606,13 +607,11 @@ TEST(ObjectTest, MutableObjects) auto arrayPtr = std::make_shared(ObjectArray{1,2,3,4}); ASSERT_FALSE(Object(arrayPtr).isMutable()); ASSERT_TRUE(Object(arrayPtr, true).isMutable()); + ASSERT_EQ(Object(arrayPtr).toDebugString(), "Array[1.000000, 2.000000, 3.000000, 4.000000, ]"); + ASSERT_EQ(Object(arrayPtr).at(10), Object::NULL_OBJECT()); // Check that retrieving the mutable array fails if the object is not marked as mutable - try { - Object(arrayPtr).getMutableArray(); - FAIL(); - } catch (...) { - } + ASSERT_DEATH(Object(arrayPtr).getMutableArray(), "Attempted to retrieve mutable array for non-mutable object"); // Check that retrieving the mutable array succeeds if the object is marked as mutable ObjectArray& array = Object(arrayPtr, true).getMutableArray(); @@ -622,11 +621,7 @@ TEST(ObjectTest, MutableObjects) ASSERT_FALSE(Object(ObjectArray{1,2}).isMutable()); ASSERT_TRUE(Object(ObjectArray{2,3}, true).isMutable()); - try { - Object(ObjectArray{1,2,3}).getMutableArray(); - FAIL(); - } catch (...) { - } + ASSERT_DEATH(Object(ObjectArray{1,2,3}).getMutableArray(), "Attempted to retrieve mutable array for non-mutable object"); array = Object(ObjectArray{2,3,4}, true).getMutableArray(); ASSERT_EQ(3, array.size()); @@ -852,4 +847,44 @@ TEST_F(DocumentObjectTest, MapComparison) ASSERT_TRUE(Object(objectMap2) == Object(jsonMap2)); ASSERT_TRUE(Object(jsonMap1) != Object(objectMap3)); ASSERT_TRUE(Object(objectMap1) != Object(jsonMap3)); -} \ No newline at end of file +} + +static const char * STYLED_TEXT_CAST = R"apl({ + "type": "APL", + "version": "2022.2", + "mainTemplate": { + "item": { + "type": "Text", + "text": "10.5" + } + } +})apl"; + +TEST_F(DocumentObjectTest, StyledTextCast) +{ + loadDocument(STYLED_TEXT_CAST); + auto st = component->getProperty(kPropertyText); + ASSERT_EQ(st.asNumber(), 10.5); + ASSERT_EQ(st.asInt(), 10); + ASSERT_EQ(st.asInt64(), 10); +} + +TEST_F(DocumentObjectTest, Truthy) +{ + loadDocument(STYLED_TEXT_CAST); + ASSERT_TRUE(Object(ComponentEventSourceWrapper::create(component, "", Object::NULL_OBJECT())).truthy()); + ASSERT_FALSE(Object(ComponentEventSourceWrapper::create(nullptr, "", Object::NULL_OBJECT())).truthy()); +} + +TEST_F(DocumentObjectTest, LiveDataAccess) +{ + loadDocument(STYLED_TEXT_CAST); + auto liveMap = Object(LiveDataObject::create(LiveMap::create(), context, "MAPPY")->asMap()); + ASSERT_TRUE(liveMap.isMap()); + ASSERT_TRUE(liveMap.isTrueMap()); + ASSERT_TRUE(liveMap.getLiveDataObject()); + + auto liveArray = Object(LiveDataObject::create(LiveArray::create(), context, "ARRAYI")->asArray()); + ASSERT_TRUE(liveArray.isArray()); + ASSERT_TRUE(liveArray.getLiveDataObject()); +} diff --git a/unit/primitives/unittest_styledtext.cpp b/unit/primitives/unittest_styledtext.cpp index 2038c80..fa190cc 100644 --- a/unit/primitives/unittest_styledtext.cpp +++ b/unit/primitives/unittest_styledtext.cpp @@ -28,9 +28,9 @@ class StyledTextTest : public ::testing::Test { void createAndVerifyStyledText(const std::string& rawText, const std::string& expectedText, size_t spansCount) { styledText = StyledText::create(*context, rawText); - ASSERT_EQ(Object::kStyledTextType, styledText.getType()); - auto iterator = StyledText::Iterator(styledText.getStyledText()); - ASSERT_EQ(expectedText, styledText.getStyledText().getText()); + ASSERT_TRUE(styledText.is()); + auto iterator = StyledText::Iterator(styledText.get()); + ASSERT_EQ(expectedText, styledText.get().getText()); ASSERT_EQ(spansCount, iterator.spanCount()); } @@ -80,7 +80,7 @@ class StyledTextTest : public ::testing::Test { verifyColorAttribute(StyledText::Iterator& it, size_t attributeIndex, const std::string& attributeValue) { auto attribute = it.getSpanAttributes().at(attributeIndex); if (attribute.name != 0) return ::testing::AssertionFailure() << "Wrong attribute name."; - if (!attribute.value.isColor()) return ::testing::AssertionFailure() << "Not a color."; + if (!attribute.value.is()) return ::testing::AssertionFailure() << "Not a color."; if (attribute.value.asString() != attributeValue) return ::testing::AssertionFailure() << "Wron value."; return ::testing::AssertionSuccess(); @@ -151,7 +151,7 @@ TEST_F(StyledTextTest, NotStyled) TEST_F(StyledTextTest, Simple) { createAndVerifyStyledText(u8"Simple styled text.", u8"Simple styled text.", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Simple ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "styled")); @@ -162,7 +162,7 @@ TEST_F(StyledTextTest, Simple) TEST_F(StyledTextTest, Multiple) { createAndVerifyStyledText(u8"Simple somewhat styled text.", u8"Simple somewhat styled text.", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Simple ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "somewhat")); @@ -177,7 +177,7 @@ TEST_F(StyledTextTest, Multiple) TEST_F(StyledTextTest, LineBreak) { createAndVerifyStyledText(u8"Line
break
text.", u8"Linebreaktext.", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Line")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeLineBreak)); ASSERT_TRUE(verifySpanEnd(iterator, StyledText::kSpanTypeLineBreak)); @@ -196,7 +196,7 @@ TEST_F(StyledTextTest, Wchar) { createAndVerifyStyledText(u8"\u524D\u9031\n\u672B\u6BD434\u518680\u92AD", u8"\u524D\u9031 \u672B\u6BD434\u518680\u92AD", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, u8"\u524D\u9031 \u672B")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, u8"\u6BD434\u5186")); @@ -209,7 +209,7 @@ TEST_F(StyledTextTest, Cyrillics) { createAndVerifyStyledText(u8"\u0440\0443\u0441\u043a\u0438\u0439 \u044F\u0437\u044B\u043a", u8"\u0440\0443\u0441\u043a\u0438\u0439 \u044F\u0437\u044B\u043a", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, u8"\u0440\0443\u0441\u043a\u0438\u0439 ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifyText(iterator, u8"\u044F\u0437\u044B\u043a")); @@ -219,7 +219,7 @@ TEST_F(StyledTextTest, Cyrillics) { TEST_F(StyledTextTest, UnclosedTag) { createAndVerifyStyledText(u8"Unclosed tag.", u8"Unclosed tag.", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Unclosed")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, " tag.")); @@ -230,7 +230,7 @@ TEST_F(StyledTextTest, UnclosedTagIntersect) createAndVerifyStyledText(u8"This is bold text, this is bold-italic and plain.", u8"This is bold text, this is bold-italic and plain.", 3); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "This is")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifyText(iterator, " bold text,")); @@ -254,7 +254,7 @@ TEST_F(StyledTextTest, UnopenedTagComplex) createAndVerifyStyledText(u8"Hello, I'm a turtle who likes lettuce.", u8"Hello, I'm a turtle who likes lettuce.", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifyText(iterator, "Hello, ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); @@ -267,7 +267,7 @@ TEST_F(StyledTextTest, UnopenedTagNested) { createAndVerifyStyledText(u8"Unopened tag.", u8"Unopened tag.", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "Unopened")); ASSERT_TRUE(verifySpanEnd(iterator, StyledText::kSpanTypeItalic)); @@ -278,7 +278,7 @@ TEST_F(StyledTextTest, UnopenedTagDeepNested) { createAndVerifyStyledText(u8"Unopened tag.", u8"Unopened tag.", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "Unopened")); @@ -292,7 +292,7 @@ TEST_F(StyledTextTest, UnclosedTagComplex) createAndVerifyStyledText(u8"Multiple very unclosed tags. And few unclosed at the end.", u8"Multiple very unclosed tags. And few unclosed at the end.", 7); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Multiple ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifyText(iterator, "very ")); @@ -322,7 +322,7 @@ TEST_F(StyledTextTest, UnclosedSameTypeTagNested) createAndVerifyStyledText(u8"Multiple nested very unclosed tags.", u8"Multiple nested very unclosed tags.", 4); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Multiple nested ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); @@ -341,7 +341,7 @@ TEST_F(StyledTextTest, UnclosedSameTypeTagNestedComplex) createAndVerifyStyledText(u8"Multiple very unclosed tags. And few unclosed at the end.", u8"Multiple very unclosed tags. And few unclosed at the end.", 8); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Multiple ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); @@ -377,7 +377,7 @@ TEST_F(StyledTextTest, SingleChildStyle) { createAndVerifyStyledText(u8"Text with one child.", u8"Text with one child.", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Text ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "with ")); @@ -394,7 +394,7 @@ TEST_F(StyledTextTest, SeveralChildStyles) createAndVerifyStyledText(u8"Text with child and another child.", u8"Text with child and another child.", 3); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Text ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "with ")); @@ -425,7 +425,7 @@ TEST_F(StyledTextTest, Complex) u8"Since you are not going on a? holiday this year! Boss,I thought I should give your office " "a holiday; \u524D\u9031\u672B\u6BD434\u518680\u92ad look.", 12); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Since ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "you")); @@ -490,7 +490,7 @@ TEST_F(StyledTextTest, LongSpecialEntity) { createAndVerifyStyledText(u8"go → right", u8"go \u2192 right", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, u8"go \u2192 ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "right")); @@ -501,7 +501,7 @@ TEST_F(StyledTextTest, UppercaseTags) { createAndVerifyStyledText(u8"Simple styled text.", u8"Simple styled text.", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Simple ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "styled")); @@ -515,7 +515,7 @@ TEST_F(StyledTextTest, UnneededSpansSimple) createAndVerifyStyledText(u8"", u8"", 0); createAndVerifyStyledText(u8"
", u8"", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeLineBreak)); } @@ -523,7 +523,7 @@ TEST_F(StyledTextTest, UnneededSpansCollapse) { createAndVerifyStyledText(u8"spancalypse", u8"spancalypse", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "spancalypse")); ASSERT_TRUE(verifySpanEnd(iterator, StyledText::kSpanTypeItalic)); @@ -532,7 +532,7 @@ TEST_F(StyledTextTest, UnneededSpansCollapseComplex) { createAndVerifyStyledText(u8"spancalypse", u8"spancalypse", 3); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeStrong)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_TRUE(verifyText(iterator, "spanca")); @@ -551,7 +551,7 @@ TEST_F(StyledTextTest, TagAttribute) // single attr createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeItalic)); ASSERT_EQ(0, iterator.getSpanAttributes().size()); @@ -561,7 +561,7 @@ TEST_F(StyledTextTest, TagAttribute) // multiple attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator2 = StyledText::Iterator(styledText.getStyledText()); + auto iterator2 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator2, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator2, StyledText::kSpanTypeItalic)); ASSERT_EQ(0, iterator2.getSpanAttributes().size()); @@ -571,7 +571,7 @@ TEST_F(StyledTextTest, TagAttribute) // special allowed characters for attribute name and value createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator3 = StyledText::Iterator(styledText.getStyledText()); + auto iterator3 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator3, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator3, StyledText::kSpanTypeItalic)); ASSERT_EQ(0, iterator.getSpanAttributes().size()); @@ -581,7 +581,7 @@ TEST_F(StyledTextTest, TagAttribute) // special allowed characters for break tag's attribute name and value createAndVerifyStyledText(u8"Hello
this
is an attr", u8"Hellothis is an attr", 1); - auto iterator4 = StyledText::Iterator(styledText.getStyledText()); + auto iterator4 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator4, "Hello")); ASSERT_TRUE(verifySpanStart(iterator4, StyledText::kSpanTypeLineBreak)); ASSERT_EQ(0, iterator4.getSpanAttributes().size()); @@ -590,7 +590,7 @@ TEST_F(StyledTextTest, TagAttribute) // using special start character and all three types of entity references createAndVerifyStyledText(u8"Hello
this is an attr", u8"Hellothis is an attr", 2); - auto iterator5 = StyledText::Iterator(styledText.getStyledText()); + auto iterator5 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator5, "Hello")); ASSERT_TRUE(verifySpanStart(iterator5, StyledText::kSpanTypeLineBreak)); ASSERT_EQ(0, iterator5.getSpanAttributes().size()); @@ -603,7 +603,7 @@ TEST_F(StyledTextTest, TagAttribute) // Checking for dec entity collisions createAndVerifyStyledText(u8"go → right", u8"go \u2192 right", 1); - auto iterator6 = StyledText::Iterator(styledText.getStyledText()); + auto iterator6 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator6, u8"go \u2192 ")); ASSERT_TRUE(verifySpanStart(iterator6, StyledText::kSpanTypeItalic)); ASSERT_EQ(0, iterator6.getSpanAttributes().size()); @@ -622,7 +622,7 @@ TEST_F(StyledTextTest, TagAttribute) createAndVerifyStyledText(u8"hello
world", u8"helloworld", 1); createAndVerifyStyledText(u8"helloworld", u8"helloworld", 1); - auto iterator7 = StyledText::Iterator(styledText.getStyledText()); + auto iterator7 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator7, "hello")); ASSERT_TRUE(verifySpanStart(iterator7, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator7, "world")); @@ -631,7 +631,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator8 = StyledText::Iterator(styledText.getStyledText()); + auto iterator8 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator8, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator8, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator8, "this is an attr")); @@ -639,7 +639,7 @@ TEST_F(StyledTextTest, TagAttribute) ASSERT_TRUE(verifyColorAttribute(iterator8, 0, "#ff0000ff")); createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator9 = StyledText::Iterator(styledText.getStyledText()); + auto iterator9 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator9, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator9, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator9, "this is an attr")); @@ -648,7 +648,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with attribute name with resource binding createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator10 = StyledText::Iterator(styledText.getStyledText()); + auto iterator10 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator10, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator10, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator10, "this is an attr")); @@ -657,7 +657,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with multiple attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator11 = StyledText::Iterator(styledText.getStyledText()); + auto iterator11 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator11, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator11, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator11, "this is an attr")); @@ -667,7 +667,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with different kinds of color attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator12 = StyledText::Iterator(styledText.getStyledText()); + auto iterator12 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator12, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator12, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator12, "this is an attr")); @@ -675,7 +675,7 @@ TEST_F(StyledTextTest, TagAttribute) ASSERT_TRUE(verifyColorAttribute(iterator12, 0, "#eeddbbff")); createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator13 = StyledText::Iterator(styledText.getStyledText()); + auto iterator13 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator13, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator13, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator13, "this is an attr")); @@ -683,7 +683,7 @@ TEST_F(StyledTextTest, TagAttribute) ASSERT_TRUE(verifyColorAttribute(iterator13, 0, "#0000ff7f")); createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator14 = StyledText::Iterator(styledText.getStyledText()); + auto iterator14 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator14, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator14, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator14, "this is an attr")); @@ -691,7 +691,7 @@ TEST_F(StyledTextTest, TagAttribute) ASSERT_TRUE(verifyColorAttribute(iterator14, 0, "#0080003f")); createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator15 = StyledText::Iterator(styledText.getStyledText()); + auto iterator15 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator15, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator15, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator15, "this is an attr")); @@ -699,7 +699,7 @@ TEST_F(StyledTextTest, TagAttribute) ASSERT_TRUE(verifyColorAttribute(iterator15, 0, "#ff0000ff")); createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator16 = StyledText::Iterator(styledText.getStyledText()); + auto iterator16 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator16, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator16, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator16, "this is an attr")); @@ -708,7 +708,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with inherit attribute value createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator17 = StyledText::Iterator(styledText.getStyledText()); + auto iterator17 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator17, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator17, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator17, "this is an attr")); @@ -716,7 +716,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with same attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator18 = StyledText::Iterator(styledText.getStyledText()); + auto iterator18 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator18, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator18, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator18, "this is an attr")); @@ -726,7 +726,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag without attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator19 = StyledText::Iterator(styledText.getStyledText()); + auto iterator19 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator19, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator19, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator19, "this is an attr")); @@ -734,7 +734,7 @@ TEST_F(StyledTextTest, TagAttribute) // span tag with non-supported attributes createAndVerifyStyledText(u8"Hello this is an attr", u8"Hello this is an attr", 1); - auto iterator20 = StyledText::Iterator(styledText.getStyledText()); + auto iterator20 = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator20, "Hello ")); ASSERT_TRUE(verifySpanStart(iterator20, StyledText::kSpanTypeSpan)); ASSERT_TRUE(verifyText(iterator20, "this is an attr")); @@ -745,7 +745,7 @@ TEST_F(StyledTextTest, NobrSimple) { createAndVerifyStyledText(u8"He screamed \"Run fasterthetiger isrightbehindyou!!!\"", u8"He screamed \"Run fasterthetiger isrightbehindyou!!!\"", 3); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, u8"He screamed \"Run ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); ASSERT_TRUE(verifyText(iterator, "faster")); @@ -766,7 +766,7 @@ TEST_F(StyledTextTest, NobrMerge) // Only some tags can be merged. For example "text" can become "text" createAndVerifyStyledText(u8"This should not merge into one big tag", u8"This should not merge into one big tag", 2); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); ASSERT_TRUE(verifyText(iterator, "This should not")); ASSERT_TRUE(verifySpanEnd(iterator, StyledText::kSpanTypeNoBreak)); @@ -781,7 +781,7 @@ TEST_F(StyledTextTest, NobrNested) createAndVerifyStyledText(u8"He screamed \"Run fasterthetiger is" "rightbehindyou!!!\"", u8"He screamed \"Run fasterthetiger isrightbehindyou!!!\"", 7); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, "He screamed \"Run ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); @@ -813,7 +813,7 @@ TEST_F(StyledTextTest, NobrComplex) createAndVerifyStyledText(u8"He screamed \"Run
fas
ter
thetiger is" "rightbehindyou!!!\"", u8"He screamed \"Run fasterthetiger isrightbehindyou!!!\"", 10); - auto iterator = StyledText::Iterator(styledText.getStyledText()); + auto iterator = StyledText::Iterator(styledText.get()); ASSERT_TRUE(verifyText(iterator, u8"He screamed \"Run ")); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); ASSERT_TRUE(verifySpanStart(iterator, StyledText::kSpanTypeNoBreak)); @@ -853,7 +853,7 @@ TEST_F(StyledTextTest, StyledTextIteratorBasic) createAndVerifyStyledText(u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", 4); - StyledText st = styledText.getStyledText(); + StyledText st = styledText.get(); StyledText::Iterator it(st); EXPECT_EQ(it.next(), StyledText::Iterator::kString); @@ -863,7 +863,7 @@ TEST_F(StyledTextTest, StyledTextIteratorBasic) EXPECT_EQ(it.getSpanType(), StyledText::kSpanTypeSpan); auto attribute = it.getSpanAttributes().at(0); EXPECT_EQ(attribute.name, 0); - EXPECT_TRUE(attribute.value.isColor()); + EXPECT_TRUE(attribute.value.is()); EXPECT_EQ(attribute.value.asString(), "#ff0000ff"); EXPECT_EQ(it.next(), StyledText::Iterator::kString); @@ -909,7 +909,7 @@ TEST_F(StyledTextTest, StyledTextIteratorEmpty) { createAndVerifyStyledText("", "", 0); - StyledText st = styledText.getStyledText(); + StyledText st = styledText.get(); StyledText::Iterator it(st); EXPECT_EQ(it.next(), StyledText::Iterator::kEnd); @@ -1013,19 +1013,19 @@ TEST_F(StyledTextTest, StyledTextTruthy) createAndVerifyStyledText(u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", 0); - StyledText st = styledText.getStyledText(); + StyledText st = styledText.get(); EXPECT_TRUE(st.truthy()); createAndVerifyStyledText(u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", 1); - st = styledText.getStyledText(); + st = styledText.get(); EXPECT_TRUE(st.truthy()); createAndVerifyStyledText(u8"", u8"", 0); - st = styledText.getStyledText(); + st = styledText.get(); EXPECT_FALSE(st.truthy()); } @@ -1034,7 +1034,7 @@ TEST_F(StyledTextTest, StyledTextIteratorNoTags) createAndVerifyStyledText(u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", u8"He screamed \"Runfasterthetigerisbehindyou!!!\"", 0); - StyledText st = styledText.getStyledText(); + StyledText st = styledText.get(); StyledText::Iterator it(st); EXPECT_EQ(it.next(), StyledText::Iterator::kString); @@ -1071,7 +1071,7 @@ TEST_F(StyledTextTest, SpanMultipleBreaklines) { createAndVerifyStyledText(u8"Example 1:hello world
Example 2: hola mundo
Example 3: hallo welt
Example 4: ciao mondo", u8"Example 1:hello worldExample 2: hola mundoExample 3: hallo weltExample 4: ciao mondo", 11 /* 2 per line + 3 break lines 2 * 4 + 3 = 11*/); - auto st = styledText.getStyledText(); + auto st = styledText.get(); StyledText::Iterator it(st); EXPECT_EQ(it.next(), StyledText::Iterator::kString); @@ -1188,7 +1188,7 @@ TEST_F(StyledTextTest, SpanTransitionUnicodes) createAndVerifyStyledText(u8"\u524D\u9031\n\u672B\u6BD434\u518680\u92AD
", u8"\u524D\u9031 \u672B\u6BD434\u518680\u92AD", 2); - StyledText st = styledText.getStyledText(); + StyledText st = styledText.get(); StyledText::Iterator it(st); EXPECT_EQ(it.next(), StyledText::Iterator::kString); diff --git a/unit/primitives/unittest_transform.cpp b/unit/primitives/unittest_transform.cpp index 20143d0..8878353 100644 --- a/unit/primitives/unittest_transform.cpp +++ b/unit/primitives/unittest_transform.cpp @@ -326,7 +326,7 @@ TEST_F(TransformTest, SimpleInterpolation) { interpolate(SIMPLE_INTERPOLATION); - auto interpolator = std::dynamic_pointer_cast(array); + auto interpolator = std::static_pointer_cast(array); ASSERT_TRUE(interpolator); // (0,0) -> (-50,-10) -> (-50,-10) -> (0,0) @@ -372,7 +372,7 @@ TEST_F(TransformTest, ComplexInterpolation) { interpolate(COMPLEX_INTERPOLATION); - auto interpolator = std::dynamic_pointer_cast(array); + auto interpolator = std::static_pointer_cast(array); ASSERT_TRUE(interpolator); // Center Rotate: 0 Scale X=2 Trans(-100, -20) Center @@ -553,7 +553,6 @@ TEST_F(TransformTest, ParseBadTransforms) */ TEST_F(TransformTest, AABB) { - auto rect = Rect (-1,-1,2,2); auto t2d = Transform2D(); @@ -580,4 +579,16 @@ TEST_F(TransformTest, AABB) ASSERT_PRED2(Close,r.getBottomRight(), result.getBottomRight()); } +TEST_F(TransformTest, Array) +{ + // Empty vectors transform without any effect + ASSERT_EQ(std::vector{}, Transform2D::scale(2) * std::vector{}); + + auto vector = std::vector{0, 0, 10, 0, 0, 20, 30, 40}; + ASSERT_EQ(Transform2D()*vector, vector); + + auto half = std::vector{0, 0, 5, 0, 0, 10, 15, 20}; + ASSERT_EQ(Transform2D::scale(0.5)*vector, half); +} + // TEST ARRAYIFICATION diff --git a/unit/scenegraph/CMakeLists.txt b/unit/scenegraph/CMakeLists.txt index 008ec2d..da7d6dd 100644 --- a/unit/scenegraph/CMakeLists.txt +++ b/unit/scenegraph/CMakeLists.txt @@ -17,14 +17,24 @@ target_sources_local(unittest testedittext.cpp unittest_sg_accessibility.cpp unittest_sg_edit_text.cpp + unittest_sg_edit_text_config.cpp + unittest_sg_filter.cpp unittest_sg_frame.cpp unittest_sg_graphic.cpp unittest_sg_graphic_component.cpp + unittest_sg_graphic_loading.cpp unittest_sg_image.cpp + unittest_sg_layer.cpp unittest_sg_line_highlighting.cpp + unittest_sg_node.cpp + unittest_sg_nodebounds.cpp unittest_sg_pager.cpp + unittest_sg_paint.cpp unittest_sg_path.cpp + unittest_sg_pathbounds.cpp + unittest_sg_pathop.cpp unittest_sg_pathparser.cpp unittest_sg_text.cpp + unittest_sg_text_properties.cpp unittest_sg_touch.cpp ) \ No newline at end of file diff --git a/unit/scenegraph/test_sg.cpp b/unit/scenegraph/test_sg.cpp index 10db250..cebfa43 100644 --- a/unit/scenegraph/test_sg.cpp +++ b/unit/scenegraph/test_sg.cpp @@ -22,6 +22,44 @@ #include "apl/scenegraph/textchunk.h" #include "apl/scenegraph/textproperties.h" + + +/** + * Template to convert an array of objects into a single string, where each object + * is converted into a string using the "convert" method. This is useful for printing + * debugging information + */ +template +std::string asString(const std::vector& array, std::function convert, std::string join = ",") +{ + std::string result; + auto len = array.size(); + if (len) + result = convert(array.at(0)); + + for (int i = 1 ; i < len ; i++) + result += join + convert(array.at(i)); + + return result; +} + +/** + * Convert an array of objects into a single comma-separated string. These objects must + * be convertable into a std::string using the std::to_string(x) function. + */ +template +std::string asArray(const std::vector& array) { + return asString(array, static_cast(std::to_string)); +} + +/** + * Convert an array of color values into a comma-separated string + */ +inline std::string asColorArray(const std::vector& array) { + return asString(array, [](Color color) { return color.asString(); }); +} + + /* * Macro for testing a condition and returning the ::testing::AssertionFailure() from the * function if it, in fact, fails. Use this inside of larger testing functions. @@ -97,6 +135,24 @@ CompareGeneral(T actual, T expected, const char *name, std::string (*f)(const T& return ::testing::AssertionSuccess(); } +template +::testing::AssertionResult +CompareNumericArray(const std::vector& actual, const std::vector& expected, + const char *name, T epsilon=1e-3) +{ + if (actual.size() != expected.size()) + return ::testing::AssertionFailure() << name << " mismatch size; actual=" << asArray(actual) + << " expected=" << asArray(expected); + + for (size_t i = 0; i < actual.size(); i++) + if (std::abs(actual.at(i) - expected.at(i)) > epsilon) + return ::testing::AssertionFailure() + << name << " mismatched elements at index=" << i << " actual=" << asArray(actual) + << " expected=" << asArray(expected); + + return ::testing::AssertionSuccess(); +} + /** * Template to check an item with a "toDebugString()" method. */ @@ -219,42 +275,6 @@ CompareOptional(ItemType item, TestType test, const char *name) { return ::testing::AssertionSuccess(); } - -/** - * Template to convert an array of objects into a single string, where each object - * is converted into a string using the "convert" method. This is useful for printing - * debugging information - */ -template -std::string asString(const std::vector& array, std::function convert, std::string join = ",") -{ - std::string result; - auto len = array.size(); - if (len) - result = convert(array.at(0)); - - for (int i = 1 ; i < len ; i++) - result += join + convert(array.at(i)); - - return result; -} - -/** - * Convert an array of objects into a single comma-separated string. These objects must - * be convertable into a std::string using the std::to_string(x) function. - */ -template -std::string asArray(const std::vector& array) { - return asString(array, static_cast(std::to_string)); -} - -/** - * Convert an array of color values into a comma-separated string - */ -inline std::string asColorArray(const std::vector& array) { - return asString(array, [](Color color) { return color.asString(); }); -} - /** * Convert a boolean value into the string "true" or "false". */ @@ -270,46 +290,9 @@ CheckNode(const sg::NodePtr& node, const NodeTest& nodeTest) return ::testing::AssertionFailure() << "Found a node where no node expected"; } - if (!node->visible()) - return ::testing::AssertionFailure() << "Testing on an invisible node"; - return nodeTest(node); } -::testing::AssertionResult -checkChildNodes(const sg::NodePtr& node, const std::vector& nodeTests) -{ - if (!node) { - if (nodeTests.empty()) - return ::testing::AssertionSuccess(); - return ::testing::AssertionFailure() << "Missing child node doesn't match node test size"; - } - - auto child = node->child(); - while (child && !child->visible()) - child = child->next(); - - for (const auto& m : nodeTests) { - if (!child) - return ::testing::AssertionFailure() << "More tests than child nodes"; - - auto result = m(child); - if (!result) - return result; - - child = child->next(); - - while (child && !child->visible()) - child = child->next(); - } - - if (child) - return ::testing::AssertionFailure() << "More child nodes than tests"; - - return ::testing::AssertionSuccess(); -} - - ::testing::AssertionResult checkPathOps(const sg::NodePtr& node, const std::vector& pathTests) { @@ -367,6 +350,21 @@ CheckPathOps(sg::PathOpPtr pathOp, const std::vector& pathTests) return ::testing::AssertionSuccess(); } +::testing::AssertionResult +CheckPath(sg::PathPtr path, const PathTest& pathTest) { + if (!path) { + if (pathTest) + return ::testing::AssertionFailure() << "Path test without a path"; + + return ::testing::AssertionSuccess(); + } + + if (!pathTest) + return ::testing::AssertionFailure() << "Path provided but no path test"; + + return pathTest(path); +} + ::testing::AssertionResult checkPaintProps( const sg::PaintPtr& paint, float opacity, Transform2D transform) @@ -402,7 +400,7 @@ checkGradientProps( const sg::GradientPaint *paint, Gradient::GradientSpreadMethod spreadMethod, bool useBoundingBox ) { - SGASSERT(CompareGeneral(paint->getPoints(), points, "Points", asArray), ""); + SGASSERT(CompareNumericArray(paint->getPoints(), points, "Points"), ""); SGASSERT(CompareGeneral(paint->getColors(), colors, "Colors", asColorArray), ""); SGASSERT(CompareBasic(paint->getSpreadMethod(), spreadMethod, "SpreadMethod"), ""); SGASSERT(CompareBasic(paint->getUseBoundingBox(), useBoundingBox, "useBoundingBox"), ""); @@ -538,7 +536,7 @@ IsGeneralPath( std::string value, std::vector points, const std::string& SGASSERT(CheckTrue(sg::GeneralPath::is_type(path), "general path"), msg); auto ptr = sg::GeneralPath::cast(path); SGASSERT(CompareBasic(ptr->getValue(), value, "value"), msg); - SGASSERT(CompareGeneral(ptr->getPoints(), points, "points", asArray), msg); + SGASSERT(CompareNumericArray(ptr->getPoints(), points, "points"), msg); return ::testing::AssertionSuccess(); }; } @@ -565,7 +563,7 @@ PathOpTest IsStrokeOp( PaintTest paintTest, SGASSERT(CompareBasic(ptr->dashOffset, dashOffset, "dashOffset"), msg); SGASSERT(CompareBasic(ptr->lineCap, lineCap, "lineCap"), msg); SGASSERT(CompareBasic(ptr->lineJoin, lineJoin, "lineJoin"), msg); - SGASSERT(CompareGeneral(ptr->dashes, dashes, "dashes", asArray), msg); + SGASSERT(CompareNumericArray(ptr->dashes, dashes, "dashes"), msg); return ::testing::AssertionSuccess(); }; } @@ -614,19 +612,26 @@ IsNode::CheckBase(sg::NodePtr node) ::testing::AssertionResult IsNode::CheckChildren(sg::NodePtr node) { - return checkChildNodes(node, mChildTests) << mMsg; + SGASSERT(CheckNode(node->child(), mChildTest), mMsg); + return CheckNode(node->next(), mNextTest); } -::testing::AssertionResult -IsGenericNode::operator()(sg::NodePtr node) +/** + * To simplify writing unit tests, nodes that are not visible are ignored in the `CheckSceneGraph` + * method. This method advances the node pointer until it finds a visible node. + */ +sg::NodePtr +AdvanceToVisibleNode(sg::NodePtr node) { - SGASSERT(CheckBase(node), mMsg); - return CheckChildren(node); + while (node && !node->visible()) + node = node->next(); + return node; } ::testing::AssertionResult IsClipNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *clip = sg::ClipNode::cast(node); SGASSERT(mPathTest(clip->getPath()), mMsg); @@ -636,6 +641,7 @@ IsClipNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsOpacityNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *opacity = sg::OpacityNode::cast(node); SGASSERT(CompareBasic(opacity->getOpacity(), mOpacity, "Opacity"), mMsg); @@ -645,6 +651,7 @@ IsOpacityNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsTransformNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *tnode = sg::TransformNode::cast(node); SGASSERT(CompareDebug(tnode->getTransform(), mTransform, "Transform"), mMsg); @@ -654,16 +661,20 @@ IsTransformNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsDrawNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *dnode = sg::DrawNode::cast(node); - SGASSERT(mPathTest(dnode->getPath()), mMsg); - SGASSERT(CheckPathOps(dnode->getOp(), mPathOpTests), mMsg); + if (dnode->visible() || mPathTest || !mPathOpTests.empty()) { + SGASSERT(CheckPath(dnode->getPath(), mPathTest), mMsg); + SGASSERT(CheckPathOps(dnode->getOp(), mPathOpTests), mMsg); + } return CheckChildren(node); } ::testing::AssertionResult IsEditNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *enode = sg::EditTextNode::cast(node); SGASSERT(CompareBasic(enode->getText(), mText, "Text"), mMsg); @@ -674,6 +685,7 @@ IsEditNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsTextNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *tnode = sg::TextNode::cast(node); @@ -695,6 +707,7 @@ IsTextNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsImageNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *ptr = sg::ImageNode::cast(node); SGASSERT(mFilterTest(ptr->getImage()), mMsg); @@ -706,6 +719,7 @@ IsImageNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsVideoNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *ptr = sg::VideoNode::cast(node); // TODO: Fix the URL @@ -717,6 +731,7 @@ IsVideoNode::operator()(sg::NodePtr node) ::testing::AssertionResult IsShadowNode::operator()(sg::NodePtr node) { + node = AdvanceToVisibleNode(node); SGASSERT(CheckBase(node), mMsg); auto *ptr = sg::ShadowNode::cast(node); SGASSERT(mShadowTest(ptr->getShadow()), mMsg); @@ -737,7 +752,7 @@ IsLayer::operator()(sg::LayerPtr layer) SGASSERT(CompareOptional(layer->getAccessibility(), mAccessibilityTest, "Layer Accessibility"), mMsg); SGASSERT(CompareDebug(layer->getChildOffset(), mChildOffset, "Layer Child Offset"), mMsg); SGASSERT(CompareBasic(layer->getInteraction(), mInteraction, "Interaction"), mMsg); - SGASSERT(CompareVisible(layer->content(), mContentTests, "Layer Content"), mMsg); + SGASSERT(CheckNode(layer->content(), mContentTest), std::string("Layer Content") + mMsg); SGASSERT(CompareDebug(layer->children(), mLayerTests, "Layer Children"), mMsg); // Check dirty flags @@ -947,8 +962,11 @@ DumpSceneGraph(const sg::NodePtr& ptr, int inset) // NOLINT(misc-no-recursio break; } - for (auto child = ptr->child() ; child ; child = child->next()) - DumpSceneGraph(child, inset+2); + if (ptr->child()) + DumpSceneGraph(ptr->child(), inset + 2); + + if (ptr->next()) + DumpSceneGraph(ptr->next(), inset); } void @@ -996,10 +1014,9 @@ DumpSceneGraph(const sg::LayerPtr& ptr, int inset) // NOLINT(misc-no-recursi if (ptr->getShadow()) std::cout << p << "Shadow " << ptr->getShadow()->toDebugString() << std::endl; - if (!ptr->content().empty()) { + if (ptr->content()) { std::cout << p << "Content" << std::endl; - for (const auto& content : ptr->content()) - DumpSceneGraph(content, inset + 4); + DumpSceneGraph(ptr->content(), inset + 4); } if (!ptr->children().empty()) { diff --git a/unit/scenegraph/test_sg.h b/unit/scenegraph/test_sg.h index a2de80f..a6222d6 100644 --- a/unit/scenegraph/test_sg.h +++ b/unit/scenegraph/test_sg.h @@ -110,26 +110,24 @@ class IsNode { protected: sg::Node::Type mType; std::string mMsg; - std::vector mChildTests; + NodeTest mChildTest; + NodeTest mNextTest; }; template class IsWrapper : public IsNode { public: IsWrapper(sg::Node::Type type, std::string msg) : IsNode(type, std::move(msg)) {} - T& child(NodeTest test) { mChildTests.emplace_back(std::move(test)); return static_cast(*this); } - T& children(std::vector nodeTests) { mChildTests = std::move(nodeTests); return static_cast(*this); } -}; - -class IsGenericNode : public IsWrapper { -public: - explicit IsGenericNode(std::string msg="") : IsWrapper(sg::Node::kGeneric, std::move(msg)) {} - IsGenericNode(std::vector nodeTests, std::string msg="") - : IsWrapper(sg::Node::kGeneric, std::move(msg)) { - mChildTests = std::move(nodeTests); + T& child(NodeTest test) { + assert(!mChildTest); + mChildTest = test; + return static_cast(*this); + } + T& next(NodeTest test) { + assert(!mNextTest); + mNextTest = test; + return static_cast(*this); } - - ::testing::AssertionResult operator()(sg::NodePtr node); }; class IsClipNode : public IsWrapper { @@ -279,10 +277,8 @@ class IsLayer { IsLayer& horizontal() { mInteraction |= sg::Layer::kInteractionScrollHorizontal; return *this; } IsLayer& vertical() { mInteraction |= sg::Layer::kInteractionScrollVertical; return *this; } - IsLayer& content(NodeTest test) { mContentTests.emplace_back(std::move(test)); return *this; } + IsLayer& content(NodeTest test) { assert(!mContentTest); mContentTest = test; return *this; } IsLayer& child(LayerTest test) { mLayerTests.emplace_back(std::move(test)); return *this; } - - IsLayer& contents(std::vector tests) { mContentTests = std::move(tests); return *this; } IsLayer& children(std::vector tests) { mLayerTests = std::move(tests); return *this; } IsLayer& dirty(unsigned flags) { mDirtyFlags = flags; return *this; } @@ -298,7 +294,7 @@ class IsLayer { Transform2D mTransform; Point mChildOffset; float mOpacity = 1.0f; - std::vector mContentTests; + NodeTest mContentTest; std::vector mLayerTests; std::string mMsg; unsigned mDirtyFlags = 0; diff --git a/unit/scenegraph/unittest_sg_accessibility.cpp b/unit/scenegraph/unittest_sg_accessibility.cpp index ae5e4f4..72b2a4a 100644 --- a/unit/scenegraph/unittest_sg_accessibility.cpp +++ b/unit/scenegraph/unittest_sg_accessibility.cpp @@ -268,4 +268,54 @@ TEST_F(SGAccessibilityTest, InteractionCheckedEnabled) .pressable() .disabled() .dirty(sg::Layer::kFlagInteractionChanged))); +} + +TEST_F(SGAccessibilityTest, Serialize) +{ + auto a = std::make_shared([](const std::string&){}); + a->setLabel("The Label"); + a->setRole(Role::kRoleAlert); + a->appendAction("bounce", "this is a bounce", true); + a->appendAction("debounce", "this is not a bounce", false); + + rapidjson::Document document; + ASSERT_TRUE(IsEqual(a->serialize(document.GetAllocator()), StringToMapObject(R"apl( + { + "label": "The Label", + "role": "alert", + "actions": [ + { + "name": "bounce", + "label": "this is a bounce", + "enabled": true + }, + { + "name": "debounce", + "label": "this is not a bounce", + "enabled": false + } + ] + } + )apl"))); +} + +TEST_F(SGAccessibilityTest, Comparisons) +{ + auto a = std::make_shared([](const std::string&){}); + auto b = std::make_shared([](const std::string&){}); + + ASSERT_EQ(*a, *b); + b->setRole(Role::kRoleAlert); + ASSERT_NE(*a, *b); + a->setRole(Role::kRoleAlert); + ASSERT_EQ(*a, *b); + + b->setLabel("I am an alert"); + ASSERT_NE(*a, *b); + a->setLabel("I am an alert"); + ASSERT_EQ(*a, *b); + + b->appendAction("bounce", "this is a bounce", true); + a->appendAction("bounce", "this is a bounce", false); + ASSERT_NE(*a, *b); } \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_edit_text.cpp b/unit/scenegraph/unittest_sg_edit_text.cpp index 606f3ce..c6bb93f 100644 --- a/unit/scenegraph/unittest_sg_edit_text.cpp +++ b/unit/scenegraph/unittest_sg_edit_text.cpp @@ -202,9 +202,7 @@ TEST_F(SGEditTextTest, Everything) .path(IsFramePath(RoundedRect{Rect{0, 0, 1000, 1000}, 0}, 4)) .pathOp(IsFillOp(IsColorPaint(Color::RED)))) .child(IsLayer(Rect{10, 10, 980, 980}) - .content(IsEditNode() - .text("foo@bar.") - .color(Color::PURPLE))))); + .content(IsEditNode().text("foo@bar.").color(Color::PURPLE))))); // Change the text executeCommand("SetValue", {{"componentId", "TEST"}, {"property", "text"}, {"value", "a"}}, @@ -229,12 +227,12 @@ TEST_F(SGEditTextTest, Everything) .dirty(sg::Layer::kFlagRedrawContent) .content(IsDrawNode() .path(IsFramePath(RoundedRect{Rect{0, 0, 1000, 1000}, 0}, 4)) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .content(IsTransformNode() - .translate(Point{10, 480}) - .child(IsTextNode() - .text("e-mail address") - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTransformNode() + .translate(Point{10, 480}) + .child(IsTextNode() + .text("e-mail address") + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))) .child(IsLayer(Rect{10, 10, 980, 980}) .dirty(sg::Layer::kFlagRedrawContent) .content(IsEditNode().text("").color(Color::PURPLE))))); @@ -383,7 +381,6 @@ TEST_F(SGEditTextTest, Resize) { configChange(ConfigurationChange(200, 200)); root->clearPending(); sg = root->getSceneGraph(); - DumpSceneGraph(sg); ASSERT_TRUE(CheckSceneGraph( sg, IsLayer(Rect{0, 0, 200, 200}) @@ -512,13 +509,13 @@ TEST_F(SGEditTextTest, FocusStyle) .content(IsEditNode().text("Beta").color(Color::BLACK)))))); // Pull out the two edit nodes - auto edit1 = sg::EditTextNode::cast(sg->getLayer()->children().at(0)->children().at(0)->content().at(0)); - auto edit2 = sg::EditTextNode::cast(sg->getLayer()->children().at(1)->children().at(0)->content().at(0)); + auto edit1 = sg::EditTextNode::cast(sg->getLayer()->children().at(0)->children().at(0)->content()); + auto edit2 = sg::EditTextNode::cast(sg->getLayer()->children().at(1)->children().at(0)->content()); ASSERT_TRUE(edit1); ASSERT_TRUE(edit2); - auto test1 = dynamic_cast(edit1->getEditText().get()); - auto test2 = dynamic_cast(edit2->getEditText().get()); + auto test1 = static_cast(edit1->getEditText().get()); + auto test2 = static_cast(edit2->getEditText().get()); ASSERT_TRUE(test1); ASSERT_TRUE(test2); diff --git a/unit/scenegraph/unittest_sg_edit_text_config.cpp b/unit/scenegraph/unittest_sg_edit_text_config.cpp new file mode 100644 index 0000000..ba4445a --- /dev/null +++ b/unit/scenegraph/unittest_sg_edit_text_config.cpp @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/textpropertiescache.h" + +using namespace apl; + +class SGEditTextConfig : public ::testing::Test {}; + +TEST_F(SGEditTextConfig, Basic) +{ + sg::TextPropertiesCache cache; + auto tp = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900); + + auto config = sg::EditTextConfig::create(Color::BLUE, + Color::RED, + KeyboardType::kKeyboardTypeEmailAddress, + "en-US", + 20, + false, + SubmitKeyType::kSubmitKeyTypeNext, + "a-zA-Z0-9@", + true, + KeyboardBehaviorOnFocus::kBehaviorOnFocusOpenKeyboard, + tp); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(config->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "textColor": "#0000ffff", + "highlightColor": "#ff0000ff", + "keyboardType": "emailAddress", + "keyboardBehaviorOnFocus": "openKeyboard", + "language": "en-US", + "maxLength": 20, + "secureInput": false, + "selectOnFocus": true, + "submitKeyType": "next", + "validCharacters": "a-zA-Z0-9@", + "textProperties": { + "fontFamily": ["Arial","Helvetica"], + "fontSize": 22.0, + "fontStyle": "normal", + "fontWeight": 900, + "letterSpacing": 0.0, + "lineHeight": 1.25, + "maxLines": 0, + "textAlign": "auto", + "textAlignVertical": "auto" + } + } + )apl"))); +} + +TEST_F(SGEditTextConfig, ValidateAndStrip) +{ + sg::TextPropertiesCache cache; + auto tp = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900); + + auto config = sg::EditTextConfig::create(Color::BLUE, + Color::RED, + KeyboardType::kKeyboardTypeEmailAddress, + "en-US", + 10, + false, + SubmitKeyType::kSubmitKeyTypeNext, + "a-zA-Z0-9@", + true, + KeyboardBehaviorOnFocus::kBehaviorOnFocusOpenKeyboard, + tp); + + // Validation + ASSERT_TRUE(config->validate("abcdeZZ9")); + ASSERT_FALSE(config->validate("alpha!")); + ASSERT_FALSE(config->validate("a really long string that is too long")); + + // Strip + ASSERT_EQ(config->strip("abc"), "abc"); + ASSERT_EQ(config->strip("__ab__c__"), "abc"); + ASSERT_EQ(config->strip("0123456789abcde"), "0123456789"); + ASSERT_EQ(config->strip(u8"ab😀c"), "abc"); +} diff --git a/unit/scenegraph/unittest_sg_filter.cpp b/unit/scenegraph/unittest_sg_filter.cpp new file mode 100644 index 0000000..99f2efc --- /dev/null +++ b/unit/scenegraph/unittest_sg_filter.cpp @@ -0,0 +1,215 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/builder.h" + +using namespace apl; + +class FakeMediaObject : public MediaObject { +public: + FakeMediaObject(std::string url) + : mURL(std::move(url)) {} + + std::string url() const override { return mURL; } + State state() const override { return kReady; } + const HeaderArray& headers() const override { return mHeaders; } + EventMediaType type() const override { return EventMediaType::kEventMediaTypeImage; } + Size size() const override { return apl::Size(20,20); } + CallbackID addCallback(MediaObjectCallback callback) override { return 0; } + void removeCallback(CallbackID callbackToken) override {} + int errorCode() const override { return 0; } + std::string errorDescription() const override { return std::string(); } + + std::string mURL; + HeaderArray mHeaders; +}; + + +class SGFilterTest : public ::testing::Test { +public: + sg::FilterPtr makeFilter(std::string url) { + return sg::filter(std::make_shared(std::move(url))); + } +}; + + +TEST_F(SGFilterTest, Filter) +{ + auto filter = makeFilter("URL"); + ASSERT_EQ(filter->toDebugString(), "MediaObject url=URL"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(filter->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL" + } + } + )apl"))); +} + +TEST_F(SGFilterTest, Blend) +{ + auto filter1 = makeFilter("URL1"); + auto filter2 = makeFilter("URL2"); + auto blend = sg::blend(filter1, filter2, BlendMode::kBlendModeDifference); + + ASSERT_EQ(blend->toDebugString(), "Blend mode=difference"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(blend->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "blendFilter", + "back": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL1" + } + }, + "front": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL2" + } + }, + "mode": "difference" + } + )apl"))); +} + +TEST_F(SGFilterTest, Blur) +{ + auto filter = makeFilter("URL"); + auto blur = sg::blur(filter, 10.0f); + + ASSERT_EQ(blur->toDebugString(), "Blur radius=10.000000"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(blur->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "blurFilter", + "filter": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL" + } + }, + "radius": 10.0 + } + )apl"))); +} + + +TEST_F(SGFilterTest, Grayscale) +{ + auto filter = makeFilter("URL"); + auto grayscale = sg::grayscale(filter, 0.5f); + + ASSERT_EQ(grayscale->toDebugString(), "Grayscale amount=0.500000"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(grayscale->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "grayscaleFilter", + "filter": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL" + } + }, + "amount": 0.5 + } + )apl"))); +} + + +TEST_F(SGFilterTest, Noise) +{ + auto filter = makeFilter("URL"); + auto noise = sg::noise(filter, NoiseFilterKind::kFilterNoiseKindUniform, true, 0.5f); + + ASSERT_EQ(noise->toDebugString(), "Noise kind=uniform useColor=yes sigma=0.500000"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(noise->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "noiseFilter", + "filter": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL" + } + }, + "kind": "uniform", + "useColor": true, + "sigma": 0.5 + } + )apl"))); +} + + +TEST_F(SGFilterTest, Saturate) +{ + auto filter = makeFilter("URL"); + auto saturate = sg::saturate(filter, 0.5f); + + ASSERT_EQ(saturate->toDebugString(), "Saturate amount=0.500000"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(saturate->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "saturateFilter", + "filter": { + "type": "mediaObjectFilter", + "mediaObject": { + "url": "URL" + } + }, + "amount": 0.5 + } + )apl"))); +} + + +TEST_F(SGFilterTest, Solid) +{ + auto solid = sg::solid(sg::paint(Color(Color::RED), 0.5f)); + + ASSERT_EQ(solid->toDebugString(), "Solid"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(solid->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "solidFilter", + "paint": { + "type": "colorPaint", + "color": "#ff0000ff", + "opacity": 0.5 + } + } + )apl"))); +} \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_frame.cpp b/unit/scenegraph/unittest_sg_frame.cpp index 761959a..c0b8a85 100644 --- a/unit/scenegraph/unittest_sg_frame.cpp +++ b/unit/scenegraph/unittest_sg_frame.cpp @@ -72,13 +72,12 @@ TEST_F(SGFrameTest, FrameWithBorder) ASSERT_TRUE(sg); ASSERT_TRUE(sg->getLayer()->visible()); - ASSERT_TRUE(CheckSceneGraph( - sg, - IsLayer(Rect{0, 0, 200, 300}) - .childClip(IsRoundRectPath(10, 10, 180, 280, 0)) - .content(IsDrawNode() - .path(IsFramePath(RoundedRect({0, 0, 200, 300}, 0), 4)) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); + ASSERT_TRUE( + CheckSceneGraph(sg, IsLayer(Rect{0, 0, 200, 300}) + .childClip(IsRoundRectPath(10, 10, 180, 280, 0)) + .content(IsDrawNode() + .path(IsFramePath(RoundedRect({0, 0, 200, 300}, 0), 4)) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); } @@ -108,15 +107,14 @@ TEST_F(SGFrameTest, FrameWithBorderAndFill) ASSERT_TRUE(sg); ASSERT_TRUE(CheckSceneGraph( - sg, - IsLayer(Rect{0, 0, 200, 300}, "checking group") - .childClip(IsRoundRectPath(10, 10, 180, 280, 0)) - .contents({IsDrawNode() - .path(IsRoundRectPath(10, 10, 180, 280, 0)) - .pathOp(IsFillOp(IsColorPaint(Color::WHITE))), - IsDrawNode() - .path(IsFramePath(RoundedRect({0, 0, 200, 300}, 0), 10)) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))}))); + sg, IsLayer(Rect{0, 0, 200, 300}, "checking group") + .childClip(IsRoundRectPath(10, 10, 180, 280, 0)) + .content(IsDrawNode() + .path(IsRoundRectPath(10, 10, 180, 280, 0)) + .pathOp(IsFillOp(IsColorPaint(Color::WHITE))) + .next(IsDrawNode() + .path(IsFramePath(RoundedRect({0, 0, 200, 300}, 0), 10)) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); } diff --git a/unit/scenegraph/unittest_sg_graphic.cpp b/unit/scenegraph/unittest_sg_graphic.cpp index f592555..f86fdd5 100644 --- a/unit/scenegraph/unittest_sg_graphic.cpp +++ b/unit/scenegraph/unittest_sg_graphic.cpp @@ -16,6 +16,7 @@ #include "../testeventloop.h" #include "test_sg.h" #include "apl/scenegraph/builder.h" +#include "apl/scenegraph/node.h" using namespace apl; @@ -65,15 +66,53 @@ TEST_F(SGGraphicTest, BasicRect) loadGraphic(BASIC_RECT); auto node = graphic->getSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph(updates, node, + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 100, 0, 100, 100, 0, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::RED))))); +} + + +static const char *TWO_RECTS = R"apl( +{ + "type": "AVG", + "version": "1.2", + "height": 100, + "width": 100, + "items": [ + { + "type": "path", + "fill": "red", + "pathData": "M0,0 L100,0 L100,100 L0,100 z" + }, + { + "type": "path", + "fill": "blue", + "pathData": "M20,20 L60,20 L60,60 L20,60 z" + } + ] +} +)apl"; +TEST_F(SGGraphicTest, TwoRects) +{ + loadGraphic(TWO_RECTS); + auto node = graphic->getSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode({IsDrawNode() - .path(IsGeneralPath("MLLLZ", {0, 0, 100, 0, 100, 100, 0, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))}))); + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 100, 0, 100, 100, 0, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsDrawNode() + .path(IsGeneralPath("MLLLZ", {20, 20, 60, 20, 60, 60, 20, 60})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); + + } + + static const char *COMPLICATED_RECT = R"apl( { "type": "AVG", @@ -118,25 +157,25 @@ static const char *COMPLICATED_RECT = R"apl( } )apl"; -TEST_F(SGGraphicTest, ComplicatedRect) { +TEST_F(SGGraphicTest, ComplicatedRect) +{ loadGraphic(COMPLICATED_RECT); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph(updates, - node, IsGenericNode( - {IsDrawNode() - .path(IsGeneralPath("MLLLZ", {0, 0, 100, 0, 100, 100, 0, 100})) - .pathOp({IsFillOp(IsColorPaint(Color::RED, 0.5)), - IsStrokeOp(IsLinearGradientPaint( - {0, 1}, {Color::RED, Color::WHITE}, - Gradient::GradientSpreadMethod::PAD, true, {0, 0}, - {1, 1}, 0.25f, Transform2D::rotate(90.f)), - 2.0f, 10.0f, 100.0f, 1.0f, - GraphicLineCap::kGraphicLineCapRound, - GraphicLineJoin::kGraphicLineJoinRound, - {1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f}) - - })}))); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 100, 0, 100, 100, 0, 100})) + .pathOp( + {IsFillOp(IsColorPaint(Color::RED, 0.5)), + IsStrokeOp(IsLinearGradientPaint({0, 1}, {Color::RED, Color::WHITE}, + Gradient::GradientSpreadMethod::PAD, true, {0, 0}, + {1, 1}, 0.25f, Transform2D::rotate(90.f)), + 2.0f, 10.0f, 100.0f, 1.0f, GraphicLineCap::kGraphicLineCapRound, + GraphicLineJoin::kGraphicLineJoinRound, + {1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f}) + + }))); } @@ -209,7 +248,8 @@ static const char *PARAMETERIZED = R"apl( } )apl"; -TEST_F(SGGraphicTest, Parameterized) { +TEST_F(SGGraphicTest, Parameterized) +{ loadGraphic(PARAMETERIZED); auto node = graphic->getSceneGraph(updates); @@ -220,83 +260,75 @@ TEST_F(SGGraphicTest, Parameterized) { graphic->setProperty("opacity", 1.0f); graphic->updateSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph( - updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 10.0f, 0.0f, - kGraphicLineCapButt, kGraphicLineJoinMiter, {}))))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 10.0f, 0.0f, + kGraphicLineCapButt, kGraphicLineJoinMiter, {})))); graphic->setProperty("lineJoin", "round"); graphic->updateSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph( - updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 10.0f, 0.0f, - kGraphicLineCapButt, kGraphicLineJoinRound, {}))))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 10.0f, 0.0f, + kGraphicLineCapButt, kGraphicLineJoinRound, {})))); graphic->setProperty("pathLength", 20); graphic->updateSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph( - updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 0.0f, - kGraphicLineCapButt, kGraphicLineJoinRound, {}))))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 0.0f, + kGraphicLineCapButt, kGraphicLineJoinRound, {})))); graphic->setProperty("dashArray", std::vector(2, 2)); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 0.0f, - kGraphicLineCapButt, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 0.0f, + kGraphicLineCapButt, kGraphicLineJoinRound, {2.0f, 2.0f})))); graphic->setProperty("dashOffset", 1.5f); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 1.5f, - kGraphicLineCapButt, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 1.5f, + kGraphicLineCapButt, kGraphicLineJoinRound, {2.0f, 2.0f})))); graphic->setProperty("lineCap", "square"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 1.5f, - kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 5.0f, 20.0f, 1.5f, + kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f})))); graphic->setProperty("miterLimit", 23.0f); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 23.0f, 20.0f, 1.5f, - kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN), 1.0f, 23.0f, 20.0f, 1.5f, + kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f})))); graphic->setProperty("opacity", 0.5f); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN, 0.5f), 1.0f, 23.0f, 20.0f, 1.5f, - kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN, 0.5f), 1.0f, 23.0f, 20.0f, 1.5f, + kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f})))); // Update the transform - but color paint doesn't use transform, so nothing changes // However, the draw node is marked as modified because the transform did actually change @@ -304,26 +336,24 @@ TEST_F(SGGraphicTest, Parameterized) { graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN, 0.5f), 1.0f, 23.0f, 20.0f, 1.5f, - kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp(IsStrokeOp(IsColorPaint(Color::GREEN, 0.5f), 1.0f, 23.0f, 20.0f, 1.5f, + kGraphicLineCapSquare, kGraphicLineJoinRound, {2.0f, 2.0f})))); // Assign a gradient. This will pick up the translate graphic->setProperty("color", "@FOO"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("ML", {0, 0, 100, 100})) - .pathOp(IsStrokeOp(IsLinearGradientPaint({0, 1}, {Color::RED, Color::WHITE}, - Gradient::GradientSpreadMethod::PAD, true, - {0, 0}, {1, 1}, 0.5f, - Transform2D::translate({1, 2})), - 1.0f, 23.0f, 20.0f, 1.5f, kGraphicLineCapSquare, - kGraphicLineJoinRound, {2.0f, 2.0f}))))); + IsDrawNode() + .path(IsGeneralPath("ML", {0, 0, 100, 100})) + .pathOp( + IsStrokeOp(IsLinearGradientPaint({0, 1}, {Color::RED, Color::WHITE}, + Gradient::GradientSpreadMethod::PAD, true, {0, 0}, + {1, 1}, 0.5f, Transform2D::translate({1, 2})), + 1.0f, 23.0f, 20.0f, 1.5f, kGraphicLineCapSquare, kGraphicLineJoinRound, + {2.0f, 2.0f})))); // Clear the opacity graphic->setProperty("opacity", 0); @@ -358,14 +388,13 @@ TEST_F(SGGraphicTest, BasicGroup) ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode("...top").child( - IsOpacityNode("...opacity") - .child(IsTransformNode().child( - IsClipNode("...clip") - .path(IsGeneralPath("", {})) - .child(IsDrawNode("...draw") - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))))); + IsOpacityNode("...opacity") + .child(IsTransformNode().child( + IsClipNode("...clip") + .path(IsGeneralPath("", {})) + .child(IsDrawNode("...draw") + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); } @@ -394,15 +423,16 @@ TEST_F(SGGraphicTest, FullGroup) loadGraphic(FULL_GROUP); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph(updates, - node, IsGenericNode({IsOpacityNode().opacity(0.5f).child( - IsTransformNode() - .transform(Transform2D::rotate(45)) - .child(IsClipNode() - .path(IsGeneralPath("MLLLZ", {0, 50, 50, 0, 100, 50, 50, 100})) - .child(IsDrawNode() - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))}))); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsOpacityNode().opacity(0.5f).child( + IsTransformNode() + .transform(Transform2D::rotate(45)) + .child(IsClipNode() + .path(IsGeneralPath("MLLLZ", {0, 50, 50, 0, 100, 50, 50, 100})) + .child(IsDrawNode() + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); } @@ -440,57 +470,52 @@ TEST_F(SGGraphicTest, ParameterizedGroup) loadGraphic(PARAMETERIZED_GROUP); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph( - updates, node, - IsGenericNode().child(IsOpacityNode().opacity(0.5f).child(IsTransformNode().child( - IsClipNode() - .path(IsGeneralPath("", {})) - .child(IsDrawNode() - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsOpacityNode().opacity(0.5f).child(IsTransformNode().child( + IsClipNode() + .path(IsGeneralPath("", {})) + .child(IsDrawNode() + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); graphic->setProperty("clipPath", "M50,0 L100,100 L0,50 z"); graphic->updateSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph( - updates, node, - IsGenericNode().child(IsOpacityNode().opacity(0.5f).child(IsTransformNode().child( - IsClipNode() - .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) - .child(IsDrawNode() - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsOpacityNode().opacity(0.5f).child(IsTransformNode().child( + IsClipNode() + .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) + .child(IsDrawNode() + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); graphic->setProperty("transform", "scale(2)"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode("..generic") - .child(IsOpacityNode("..opacity") - .opacity(0.5f) - .child(IsTransformNode("..transform") - .transform(Transform2D::scale(2.0f)) - .child(IsClipNode("..clip") - .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) - .child(IsDrawNode("..draw") - .path(IsGeneralPath( - "MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp( - IsColorPaint(Color::BLUE))))))))); + IsOpacityNode("..opacity") + .opacity(0.5f) + .child(IsTransformNode("..transform") + .transform(Transform2D::scale(2.0f)) + .child(IsClipNode("..clip") + .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) + .child(IsDrawNode("..draw") + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); graphic->setProperty("opacity", 1.0); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsOpacityNode() - .child( - IsTransformNode() - .transform(Transform2D::scale(2.0f)) - .child(IsClipNode() - .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) - .child(IsDrawNode() - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))))); + IsOpacityNode().child( + IsTransformNode() + .transform(Transform2D::scale(2.0f)) + .child(IsClipNode() + .path(IsGeneralPath("MLLZ", {50, 0, 100, 100, 0, 50})) + .child(IsDrawNode() + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); graphic->setProperty("opacity", 0.0); graphic->updateSceneGraph(updates); @@ -534,13 +559,14 @@ TEST_F(SGGraphicTest, MultiChild) loadGraphic(MULTI_CHILD_ONE); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph(updates, - node, IsGenericNode({IsOpacityNode().child(IsTransformNode().child( - IsClipNode() - .path(IsGeneralPath("", {})) - .child(IsDrawNode() - .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE, 0.5f))))))}))); + ASSERT_TRUE( + CheckSceneGraph(updates, node, + IsOpacityNode().child(IsTransformNode().child( + IsClipNode() + .path(IsGeneralPath("", {})) + .child(IsDrawNode() + .path(IsGeneralPath("MLLZ", {0, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE, 0.5f)))))))); graphic->setProperty("opacity", 0.0f); graphic->updateSceneGraph(updates); @@ -569,12 +595,11 @@ TEST_F(SGGraphicTest, BasicText) loadGraphic(BASIC_TEXT); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph(updates, - node, IsGenericNode({IsTransformNode() - .translate(Point{0, -8}) - .child(IsTextNode() - .text("Hello, World!") - .pathOp(IsFillOp(IsColorPaint(Color::RED))))}))); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("Hello, World!").pathOp(IsFillOp(IsColorPaint(Color::RED)))))); } @@ -620,19 +645,18 @@ TEST_F(SGGraphicTest, ComplicatedText) loadGraphic(COMPLICATED_TEXT); auto node = graphic->getSceneGraph(updates); - ASSERT_TRUE(CheckSceneGraph(updates, - node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{0, -8}) - .child(IsTextNode() - .text("Fill and Stroke") - .pathOp(IsFillOp(IsColorPaint(Color::RED, 0.5))) - .pathOp(IsStrokeOp(IsLinearGradientPaint( - {0, 1}, {Color::RED, Color::WHITE}, - Gradient::GradientSpreadMethod::PAD, true, {0, 0}, - {1, 1}, 0.25f, Transform2D::rotate(90.f)), - 2.0f)))))); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode() + .text("Fill and Stroke") + .pathOp(IsFillOp(IsColorPaint(Color::RED, 0.5))) + .pathOp(IsStrokeOp(IsLinearGradientPaint({0, 1}, {Color::RED, Color::WHITE}, + Gradient::GradientSpreadMethod::PAD, + true, {0, 0}, {1, 1}, 0.25f, + Transform2D::rotate(90.f)), + 2.0f))))); } @@ -692,52 +716,39 @@ TEST_F(SGGraphicTest, ParameterizedText) graphic->setProperty("text", "Woof!"); graphic->updateSceneGraph(updates); - ASSERT_TRUE( - CheckSceneGraph(updates, node, - IsGenericNode(".generic") - .child(IsTransformNode(".transform") - .translate(Point{0, -8}) - .child(IsTextNode(".text") - .text("Woof!") - .pathOp(IsFillOp(IsColorPaint(Color::GREEN))))))); + ASSERT_TRUE(CheckSceneGraph(updates, node, + IsTransformNode(".transform") + .translate(Point{0, -8}) + .child(IsTextNode(".text").text("Woof!").pathOp( + IsFillOp(IsColorPaint(Color::GREEN)))))); graphic->setProperty("opacity", 0.5f); graphic->updateSceneGraph(updates); - ASSERT_TRUE( - CheckSceneGraph(updates, node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{0, -8}) - .child(IsTextNode() - .text("Woof!") - .pathOp(IsFillOp(IsColorPaint(Color::GREEN, 0.5f))))))); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("Woof!").pathOp(IsFillOp(IsColorPaint(Color::GREEN, 0.5f)))))); graphic->setProperty("color", "@FOO"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child(IsTransformNode() - .translate(Point{0, -8}) - .child(IsTextNode() - .text("Woof!") - .pathOp(IsFillOp(IsLinearGradientPaint( - {0, 1}, {Color::RED, Color::WHITE}, - Gradient::GradientSpreadMethod::PAD, true, {0, 0}, - {1, 1}, 0.5f, Transform2D()))))))); + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("Woof!").pathOp(IsFillOp(IsLinearGradientPaint( + {0, 1}, {Color::RED, Color::WHITE}, Gradient::GradientSpreadMethod::PAD, true, + {0, 0}, {1, 1}, 0.5f, Transform2D())))))); graphic->setProperty("transform", "translate(1,2)"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{0, -8}) - .child( - IsTextNode() - .text("Woof!") - .pathOp(IsFillOp(IsLinearGradientPaint( - {0, 1}, {Color::RED, Color::WHITE}, Gradient::GradientSpreadMethod::PAD, - true, {0, 0}, {1, 1}, 0.5f, Transform2D::translate({1, 2})))))))); + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("Woof!").pathOp(IsFillOp(IsLinearGradientPaint( + {0, 1}, {Color::RED, Color::WHITE}, Gradient::GradientSpreadMethod::PAD, true, + {0, 0}, {1, 1}, 0.5f, Transform2D::translate({1, 2}))))))); graphic->setProperty("text", ""); graphic->updateSceneGraph(updates); @@ -754,38 +765,133 @@ TEST_F(SGGraphicTest, ParameterizedText) graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child(IsTransformNode() - .translate(Point{0, -8}) - .child(IsTextNode() - .text("123") - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); graphic->setProperty("x", 10); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{10, -8}) - .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); + IsTransformNode() + .translate(Point{10, -8}) + .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); graphic->setProperty("y", 20); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{10, 12}) - .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); + IsTransformNode() + .translate(Point{10, 12}) + .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); graphic->setProperty("anchor", "end"); graphic->updateSceneGraph(updates); ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsTransformNode() - .translate(Point{-20, 12}) - .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); + IsTransformNode() + .translate(Point{-20, 12}) + .child(IsTextNode().text("123").pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); +} + + +static const char *PARAMETERIZED_TEXT_STROKE = R"apl( + { + "type": "AVG", + "version": "1.2", + "height": 100, + "width": 100, + "resources": { + "gradients": { + "FOO": { + "type": "linear", + "colorRange": [ + "red", + "white" + ], + "inputRange": [ + 0, + 1 + ], + "angle": 90 + } + } + }, + "parameters": [ + "color", + { "name": "opacity", "default": 1.0 }, + { "name": "swidth", "default": 1.0 }, + "transform" + ], + "items": { + "type": "text", + "stroke": "${color}", + "strokeOpacity": "${opacity}", + "strokeTransform": "${transform}", + "strokeWidth": "${swidth}", + "fill": "transparent", + "text": "HELLO", + "fontSize": 10 + } + } +)apl"; + + +TEST_F(SGGraphicTest, ParameterizedTextStroke) +{ + loadGraphic(PARAMETERIZED_TEXT_STROKE); + auto node = graphic->getSceneGraph(updates); + + ASSERT_FALSE(node->visible()); + + graphic->setProperty("color", Color::GREEN); + graphic->updateSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph(updates, node, + IsTransformNode(".transform") + .translate(Point{0, -8}) + .child(IsTextNode(".text").text("HELLO").pathOp( + IsStrokeOp(IsColorPaint(Color::GREEN), 1))))); + + graphic->setProperty("opacity", 0.5f); + graphic->updateSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("HELLO").pathOp(IsStrokeOp(IsColorPaint(Color::GREEN, 0.5f), 1))))); + + graphic->setProperty("color", "@FOO"); + graphic->updateSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("HELLO").pathOp(IsStrokeOp(IsLinearGradientPaint( + {0, 1}, {Color::RED, Color::WHITE}, Gradient::GradientSpreadMethod::PAD, true, + {0, 0}, {1, 1}, 0.5f, Transform2D()), 1.0f))))); + + graphic->setProperty("transform", "translate(1,2)"); + graphic->updateSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("HELLO").pathOp(IsStrokeOp(IsLinearGradientPaint( + {0, 1}, {Color::RED, Color::WHITE}, Gradient::GradientSpreadMethod::PAD, true, + {0, 0}, {1, 1}, 0.5f, Transform2D::translate({1, 2})), 1.0f))))); + + graphic->setProperty("opacity", 1.0f); + graphic->setProperty("color", "blue"); + graphic->updateSceneGraph(updates); + ASSERT_TRUE(CheckSceneGraph( + updates, node, + IsTransformNode() + .translate(Point{0, -8}) + .child(IsTextNode().text("HELLO").pathOp(IsStrokeOp(IsColorPaint(Color::BLUE), 1))))); + + graphic->setProperty("swidth", 0); + graphic->updateSceneGraph(updates); + ASSERT_FALSE(node->visible()); } @@ -824,12 +930,11 @@ TEST_F(SGGraphicTest, Shadow) ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsShadowNode() - .shadowTest(IsShadow(Color::BLUE, Point{3, 3}, 5)) - .child(IsDrawNode() - .path(IsGeneralPath("MLLLZ", {10, 10, 90, 10, 90, 90, 10, 90})) - .pathOp(IsFillOp(IsColorPaint(Color::BLUE))))))); + IsShadowNode() + .shadowTest(IsShadow(Color::BLUE, Point{3, 3}, 5)) + .child(IsDrawNode() + .path(IsGeneralPath("MLLLZ", {10, 10, 90, 10, 90, 90, 10, 90})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))); graphic->setProperty("COLOR", "red"); graphic->updateSceneGraph(updates); @@ -837,13 +942,266 @@ TEST_F(SGGraphicTest, Shadow) // Note: For now the filter is not dynamic ASSERT_TRUE(CheckSceneGraph( updates, node, - IsGenericNode().child( - IsShadowNode() - .shadowTest(IsShadow(Color::BLUE, Point{3, 3}, 5)) - .child(IsDrawNode() - .path(IsGeneralPath("MLLLZ", {10, 10, 90, 10, 90, 90, 10, 90})) - .pathOp(IsFillOp(IsColorPaint(Color::RED))))))); + IsShadowNode() + .shadowTest(IsShadow(Color::BLUE, Point{3, 3}, 5)) + .child(IsDrawNode() + .path(IsGeneralPath("MLLLZ", {10, 10, 90, 10, 90, 90, 10, 90})) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))); +} + +// Use a custom test for checking the nodes. The regular test_sg.h code skips over operations +// that are not visible. But we want to verify that we have exactly the correct operations. +static ::testing::AssertionResult +checkOps(sg::PathOp *op, bool hasFill, Color fillColor, bool hasStroke, Color strokeColor) { + if (hasFill) { + if (!sg::FillPathOp::is_type(op)) + return ::testing::AssertionFailure() << "Expected a fill operation"; + auto *fill = sg::FillPathOp::cast(op); + auto *paint = sg::ColorPaint::cast(fill->paint); + if (!paint || paint->getColor() != fillColor) + return ::testing::AssertionFailure() << "Fill color mismatch"; + op = op->nextSibling.get(); + } + + if (hasStroke) { + if (!sg::StrokePathOp::is_type(op)) + return ::testing::AssertionFailure() << "Missing stroke operation"; + auto *stroke = sg::StrokePathOp::cast(op); + auto *paint = sg::ColorPaint::cast(stroke->paint); + if (!paint || paint->getColor() != strokeColor) + return ::testing::AssertionFailure() << "Stroke color mismatch"; + op = op->nextSibling.get(); + } + + if (op) + return ::testing::AssertionFailure() << "Extra operation"; + + return ::testing::AssertionSuccess(); +} + +static ::testing::AssertionResult +checkDraw(sg::NodePtr node, float x, bool hasFill, Color fillColor, bool hasStroke, Color strokeColor) { + auto *draw = sg::DrawNode::cast(node); + if (!draw) + return ::testing::AssertionFailure() << "not a draw node"; + + auto *path = sg::GeneralPath::cast(draw->getPath()); + if (!path) + return ::testing::AssertionFailure() << "missing path node"; + + // negative x used to indicate no points + if (path->getPoints() != (x < 0 ? std::vector{} : std::vector{0,0,0,x,x,x})) + return ::testing::AssertionFailure() << "mismatched points"; + + return checkOps(draw->getOp().get(), hasFill, fillColor, hasStroke, strokeColor); } +static ::testing::AssertionResult +checkText(sg::NodePtr node, std::string textString, bool hasFill, Color fillColor, bool hasStroke, Color strokeColor) { + auto *text = sg::TextNode::cast(node); + if (!text) + return ::testing::AssertionFailure() << "not a text node"; + if (text->getTextLayout()->toDebugString() != textString) + return ::testing::AssertionFailure() << "text mismatch"; + + return checkOps(text->getOp().get(), hasFill, fillColor, hasStroke, strokeColor); +} + +static const char *DRAW_OPTIMIZATION = R"apl( +{ + "type": "AVG", + "version": "1.2", + "height": 100, + "width": 100, + "parameters": [ + { + "name": "X", + "default": false + } + ], + "items": [ + { + "type": "path", + "description": "Empty path", + "fill": "red", + "pathData": "M10,10 M20,20" + }, + { + "type": "path", + "description": "Just fill", + "fill": "blue", + "pathData": "M0,0 L0,1 L1,1 z" + }, + { + "type": "path", + "description": "Just stroke", + "stroke": "red", + "pathData": "M0,0 L0,2 L2,2 z" + }, + { + "type": "path", + "description": "Stroke, but no width", + "stroke": "green", + "strokeWidth": 0, + "pathData": "M0,0 L0,3 L3,3 z" + }, + { + "type": "path", + "description": "Stroke and fill", + "stroke": "yellow", + "fill": "black", + "strokeWidth": 5, + "pathData": "M0,0 L0,4 L4,4 z" + }, + { + "type": "path", + "description": "Stroke and fill opacity zero", + "stroke": "pink", + "strokeOpacity": 0, + "fill": "blue", + "fillOpacity": 0, + "strokeWidth": 5, + "pathData": "M0,0 L0,5 L5,5 z" + }, + { + "type": "path", + "description": "Path depends on X", + "pathData": "${X ? 'M0,0 L0,6 L6,6 z' : 'M0,0'}", + "fill": "purple" + }, + { + "type": "path", + "description": "Fill depends on X", + "pathData": "M0,0 L0,7 L7,7 z", + "fill": "${X ? 'blue' : 'transparent'}" + }, + { + "type": "path", + "description": "Stroke depends on X", + "pathData": "M0,0 L0,8 L8,8 z", + "stroke": "${X ? 'red' : 'transparent'}" + } + ] +} +)apl"; + +TEST_F(SGGraphicTest, DrawOptimization) +{ + loadGraphic(DRAW_OPTIMIZATION); + auto node = graphic->getSceneGraph(updates); + + // Skip the empty path - there is no path data + + // Fill blue + ASSERT_TRUE(checkDraw(node, 1, true, Color::BLUE, false, Color::TRANSPARENT)); + node = node->next(); + + // Stroke red + ASSERT_TRUE(checkDraw(node, 2, false, Color::TRANSPARENT, true, Color::RED)); + node = node->next(); + + // Skip the stroke with green because there is no stroke width + + // Stroke yellow, fill black + ASSERT_TRUE(checkDraw(node, 4, true, Color::BLACK, true, Color::YELLOW)); + node = node->next(); + + // Skip stroke pink, fill blue because the opacities hide all colors + + // Allow the fill purple even though there is no path because the path is mutable + ASSERT_TRUE(checkDraw(node, -1, true, Color::PURPLE, false, Color::TRANSPARENT)); + node = node->next(); + + // Allow the 7th case - the fill color can be changed + ASSERT_TRUE(checkDraw(node, 7, true, Color::TRANSPARENT, false, Color::TRANSPARENT)); + node = node->next(); + + // Allow the 8th case - the stroke color can be changed + ASSERT_TRUE(checkDraw(node, 8, false, Color::TRANSPARENT, true, Color::TRANSPARENT)); + node = node->next(); + + ASSERT_FALSE(node); +} + + +static const char *TEXT_OPTIMIZATION = R"apl( +{ + "type": "AVG", + "version": "1.2", + "height": 100, + "width": 100, + "parameters": [ + { + "name": "X", + "default": false + } + ], + "items": [ + { + "type": "text", + "text": "Just fill", + "fill": "red" + }, + { + "type": "text", + "text": "Just stroke", + "stroke": "yellow", + "fillOpacity": 0, + "strokeWidth": 1 + }, + { + "type": "text", + "text": "Stroke and fill", + "stroke": "green", + "strokeWidth": 2, + "fill": "blue" + }, + { + "type": "text", + "text": "Nothing to draw", + "fillOpacity": 0 + }, + { + "type": "text", + "text": "", + "fill": "purple" + }, + { + "type": "text", + "text": "Default" + } + ] +} +)apl"; + +TEST_F(SGGraphicTest, TextOptimization) +{ + loadGraphic(TEXT_OPTIMIZATION); + auto node = graphic->getSceneGraph(updates); + + // Fill red (well, it's hidden under a transform) + ASSERT_TRUE(checkText(node->child(), "Just fill", true, Color::RED, false, Color::TRANSPARENT)); + node = node->next(); + + // Stroke yellow + ASSERT_TRUE(checkText(node->child(), "Just stroke", false, Color::TRANSPARENT, true, Color::YELLOW)); + node = node->next(); + + // Stroke green, fill blue + ASSERT_TRUE(checkText(node->child(), "Stroke and fill", true, Color::BLUE, true, Color::GREEN)); + node = node->next(); + + // Skip the "Nothing to draw" - there is no fill or stroke + + // Fill purple (even though there is no text to draw. Fix this?) + ASSERT_TRUE(checkText(node->child(), "", true, Color::PURPLE, false, Color::TRANSPARENT)); + node = node->next(); + + // Fill with black (the default color) + ASSERT_TRUE(checkText(node->child(), "Default", true, Color::BLACK, false, Color::TRANSPARENT)); + node = node->next(); + + ASSERT_FALSE(node); +} // TODO: Check style changes - they should update properties as appropriate \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_graphic_component.cpp b/unit/scenegraph/unittest_sg_graphic_component.cpp index 899e2df..1be9c1e 100644 --- a/unit/scenegraph/unittest_sg_graphic_component.cpp +++ b/unit/scenegraph/unittest_sg_graphic_component.cpp @@ -64,14 +64,11 @@ TEST_F(SGGraphicComponentTest, Basic) sg, IsLayer(Rect{0, 0, 200, 200}, ".VectorGraphic") .child(IsLayer(Rect{50, 50, 100, 100}, ".MediaLayer") - .content( - IsTransformNode(".transform") - .child(IsGenericNode(".generic") - .child(IsDrawNode(".draw") - .path(IsGeneralPath( - "MLLLZ", {0, 50, 50, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp( - IsColorPaint(Color::GREEN))))))))); + .content(IsTransformNode(".transform") + .child(IsDrawNode(".draw") + .path(IsGeneralPath( + "MLLLZ", {0, 50, 50, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))))))); executeCommand("SetValue", {{"componentId", "VG"}, {"property", "align"}, {"value", "top-right"}}, true); @@ -82,11 +79,9 @@ TEST_F(SGGraphicComponentTest, Basic) .child(IsLayer(Rect{100, 0, 100, 100}, ".MediaLayer") .dirty(sg::Layer::kFlagPositionChanged) .content(IsTransformNode().child( - IsGenericNode(".generic") - .child(IsDrawNode() - .path(IsGeneralPath("MLLLZ", - {0, 50, 50, 0, 100, 50, 50, 100})) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN))))))))); + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 50, 50, 0, 100, 50, 50, 100})) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))))))); } @@ -165,21 +160,19 @@ TEST_F(SGGraphicComponentTest, MultiText) ASSERT_TRUE(CheckSceneGraph( sg, IsLayer(Rect{0, 0, 800, 800}, "...Frame") - .child( - IsLayer(Rect{0, 0, 800, 800}, "...VectorGraphic") - .child( - IsLayer(Rect{0, 0, 800, 800}, "...Graphic") - .content( - IsTransformNode() - .transform(Transform2D::scale(4. / 3.)) - .child(IsGenericNode().child( - IsTransformNode() - .translate(Point{10, 92}) - .child(IsTextNode() - .text("Hello, world!") - .pathOp(IsFillOp(IsColorPaint(Color::BLACK))) - .pathOp(IsStrokeOp(IsColorPaint(Color::BLUE), - 2)))))))))); + .child(IsLayer(Rect{0, 0, 800, 800}, "...VectorGraphic") + .child(IsLayer(Rect{0, 0, 800, 800}, "...Graphic") + .content(IsTransformNode() + .transform(Transform2D::scale(4. / 3.)) + .child(IsTransformNode() + .translate(Point{10, 92}) + .child(IsTextNode() + .text("Hello, world!") + .pathOp(IsFillOp(IsColorPaint( + Color::BLACK))) + .pathOp(IsStrokeOp( + IsColorPaint(Color::BLUE), + 2))))))))); } static const char *MOVING = R"apl( @@ -218,28 +211,25 @@ static const char *MOVING = R"apl( } )apl"; -TEST_F(SGGraphicComponentTest, Moving) { +TEST_F(SGGraphicComponentTest, Moving) +{ loadDocument(MOVING); auto sg = root->getSceneGraph(); ASSERT_TRUE(CheckSceneGraph( - sg, - IsLayer(Rect{0, 0, 200, 200}) - .child( - IsLayer(Rect{0, 0, 200, 200}) - .content( - IsTransformNode(".alignment") - .child(IsGenericNode(".elementContainer") - .child(IsOpacityNode(".groupOpacity") - .child(IsTransformNode(".group").child( - IsClipNode(".groupClip") - .path(IsGeneralPath("", {})) - .child(IsDrawNode() - .path(IsGeneralPath( - "MLLLZ", {0, 0, 10, 0, 10, - 10, 0, 10})) - .pathOp(IsFillOp(IsColorPaint( - Color::BLUE)))))))))))); + sg, IsLayer(Rect{0, 0, 200, 200}) + .child(IsLayer(Rect{0, 0, 200, 200}) + .content(IsTransformNode(".alignment") + .child(IsOpacityNode(".groupOpacity") + .child(IsTransformNode(".group").child( + IsClipNode(".groupClip") + .path(IsGeneralPath("", {})) + .child(IsDrawNode() + .path(IsGeneralPath( + "MLLLZ", {0, 0, 10, 0, 10, + 10, 0, 10})) + .pathOp(IsFillOp(IsColorPaint( + Color::BLUE))))))))))); root->updateTime(100); root->clearPending(); @@ -255,21 +245,13 @@ TEST_F(SGGraphicComponentTest, Moving) { .content( IsTransformNode(".alignment") .child( - IsGenericNode(".elementContainer") - .child( - IsOpacityNode(".groupOpacity") - .child( - IsTransformNode(".group") - .translate({100, 0}) - .child( - IsClipNode(".groupClip") - .path(IsGeneralPath("", {})) - .child( - IsDrawNode() - .path(IsGeneralPath( - "MLLLZ", - {0, 0, 10, 0, 10, 10, 0, - 10})) - .pathOp(IsFillOp(IsColorPaint( - Color::BLUE)))))))))))); + IsOpacityNode(".groupOpacity") + .child(IsTransformNode(".group").translate({100, 0}).child( + IsClipNode(".groupClip") + .path(IsGeneralPath("", {})) + .child(IsDrawNode() + .path(IsGeneralPath( + "MLLLZ", {0, 0, 10, 0, 10, 10, 0, 10})) + .pathOp(IsFillOp( + IsColorPaint(Color::BLUE))))))))))); } \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_graphic_loading.cpp b/unit/scenegraph/unittest_sg_graphic_loading.cpp new file mode 100644 index 0000000..2ce248d --- /dev/null +++ b/unit/scenegraph/unittest_sg_graphic_loading.cpp @@ -0,0 +1,381 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" +#include "apl/media/mediaobject.h" + +using namespace apl; +// Custom media manager that has some pre-defined media objects +class SGAVGManager : public MediaManager { +private: + class MO : public MediaObject { + public: + MO(std::string url, MediaObject::State state) + : mURL(std::move(url)), + mState(state) + {} + + std::string url() const override { return mURL; } + State state() const override { return mState; } + EventMediaType type() const override { return apl::kEventMediaTypeVectorGraphic; } + Size size() const override { return {10,10}; } + int errorCode() const override { return mErrorCode; } + std::string errorDescription() const override { return mErrorDescription; } + const HeaderArray& headers() const override { return mHeaders; } + GraphicContentPtr graphic() override { return mGraphic; } + + CallbackID addCallback(MediaObjectCallback callback) override { + if (mState == kPending) { + mCallbackCounter += 1; + mCallbacks.emplace(mCallbackCounter, callback); + return mCallbackCounter; + } + return false; + } + + void removeCallback(CallbackID callbackId) override { + mCallbacks.erase(callbackId); + } + + std::string mURL; + State mState; + std::map mCallbacks; + int mErrorCode = 0; + std::string mErrorDescription; + int mCallbackCounter = 0; + HeaderArray mHeaders; + GraphicContentPtr mGraphic; + }; + +public: + SGAVGManager(const SessionPtr& session) : mSession(session) {} + + MediaObjectPtr request(const std::string& url, EventMediaType type) override { + assert(type == apl::kEventMediaTypeVectorGraphic); + + auto it = mObjectMap.find(url); + if (it != mObjectMap.end()) + return it->second; + + auto result = std::make_shared(url, MediaObject::State::kPending); + mObjectMap.emplace(url, result); + return result; + } + + MediaObjectPtr request(const std::string& url, EventMediaType type, const HeaderArray& headers) override { + return request(url, type); + } + + void addMedia(const std::string& url, const char *data) { + auto it = mObjectMap.find(url); + if (it != mObjectMap.end()) { + it->second->mGraphic = GraphicContent::create(mSession, data); + it->second->mState = MediaObject::State::kReady; + for (const auto& m : it->second->mCallbacks) + m.second(it->second); + it->second->mCallbacks.clear(); + return; + } + + auto ptr = std::make_shared(url, MediaObject::State::kReady); + ptr->mGraphic = GraphicContent::create(mSession, data); + mObjectMap.emplace(url, ptr); + } + + void failMedia(const std::string& url, int code, const std::string& description) { + auto it = mObjectMap.find(url); + if (it != mObjectMap.end()) { + it->second->mState = MediaObject::State::kError; + for (const auto& m : it->second->mCallbacks) + m.second(it->second); + it->second->mCallbacks.clear(); + return; + } + + auto ptr = std::make_shared(url, MediaObject::State::kError); + mObjectMap.emplace(url, ptr); + } + + std::vector pendingMediaRequests() const { + auto result = std::vector{}; + for (const auto& m : mObjectMap) + if (m.second->mState == MediaObject::State::kPending) + result.emplace_back(m.first); + return result; + } + + std::map> mObjectMap; + SessionPtr mSession; +}; + +class SGGraphicLoadingTest : public DocumentWrapper { +public: + SGGraphicLoadingTest() : DocumentWrapper() { + config->enableExperimentalFeature(RootConfig::kExperimentalFeatureManageMediaRequests); + mediaManager = std::make_shared(session); + config->mediaManager(mediaManager); + } + + void addMedia(const std::string& url, const char *data) { + mediaManager->addMedia(url, data); + } + + void failMedia(const std::string& url) { + mediaManager->failMedia(url, 99, "Something went wrong"); + } + + void TearDown() override { + mediaManager.reset(); + DocumentWrapper::TearDown(); + } + + std::vector pendingMediaRequests() const { + return mediaManager->pendingMediaRequests(); + } + + std::shared_ptr mediaManager; +}; + + +static const char *BLUE_BOX = R"apl( +{ + "type": "AVG", + "version": "1.2", + "width": 200, + "height": 200, + "items": { + "type": "path", + "fill": "blue", + "pathData": "h200 v200 h-200 z" + } +} +)apl"; + +static const char *RED_BOX = R"apl( +{ + "type": "AVG", + "version": "1.2", + "width": 200, + "height": 200, + "items": { + "type": "path", + "fill": "red", + "pathData": "h200 v200 h-200 z" + } +} +)apl"; + +static const char * BASIC_TEST = R"apl( +{ + "type": "APL", + "version": "1.6", + "mainTemplate": { + "items": { + "type": "VectorGraphic", + "id": "TestVG", + "width": 200, + "height": 200, + "source": "http://bluebox" + } + } +} +)apl"; + +TEST_F(SGGraphicLoadingTest, Preloaded) +{ + addMedia("http://bluebox", BLUE_BOX); + loadDocument(BASIC_TEST); + ASSERT_TRUE(component); + ASSERT_TRUE(pendingMediaRequests().empty()); + + auto sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); +} + + +TEST_F(SGGraphicLoadingTest, Postloaded) +{ + loadDocument(BASIC_TEST); + ASSERT_TRUE(component); + ASSERT_EQ(1, pendingMediaRequests().size()); + + auto sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 1, 1))))); + + addMedia("http://bluebox", BLUE_BOX); + ASSERT_TRUE(pendingMediaRequests().empty()); + + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .dirty(sg::Layer::kFlagRedrawContent | sg::Layer::kFlagSizeChanged) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); +} + + +TEST_F(SGGraphicLoadingTest, Change) +{ + loadDocument(BASIC_TEST); + ASSERT_TRUE(component); + ASSERT_EQ(1, pendingMediaRequests().size()); + + // The initial VectorGraph is looking for "http://bluebox", which hasn't been received + auto sg = root->getSceneGraph(); + ASSERT_TRUE( + CheckSceneGraph(sg, IsLayer(Rect(0, 0, 200, 200)).child(IsLayer(Rect(0, 0, 1, 1))))); + + // Change the source to "http://redbox", add it, and verify that the VG inflates correctly + executeCommand("SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "http://redbox"}}, + true); + ASSERT_EQ(2, pendingMediaRequests().size()); + + addMedia("http://redbox", RED_BOX); + ASSERT_EQ(1, pendingMediaRequests().size()); + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .dirty(sg::Layer::kFlagRedrawContent | sg::Layer::kFlagSizeChanged) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))); + + // Add in "http://bluebox" + addMedia("http://bluebox", BLUE_BOX); + ASSERT_TRUE(pendingMediaRequests().empty()); + + // Now change back to "http://bluebox". That should fire immediately + executeCommand("SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "http://bluebox"}}, + true); + ASSERT_TRUE(pendingMediaRequests().empty()); + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .dirty(sg::Layer::kFlagRedrawContent) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); + + // Finally, swap it to empty + executeCommand( + "SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "http://missing_box"}}, true); + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)).dirty(sg::Layer::kFlagRedrawContent)))); +} + + + +static const char * LOCAL_TEST = R"apl( +{ + "type": "APL", + "version": "1.6", + "graphics": { + "yellowBox": { + "type": "AVG", + "version": "1.2", + "width": 200, + "height": 200, + "items": { + "type": "path", + "fill": "yellow", + "pathData": "h200 v200 h-200 z" + } + } + }, + "mainTemplate": { + "items": { + "type": "VectorGraphic", + "id": "TestVG", + "width": 200, + "height": 200, + "source": "http://bluebox" + } + } +} +)apl"; + + +TEST_F(SGGraphicLoadingTest, LocalGraphic) +{ + loadDocument(LOCAL_TEST); + ASSERT_TRUE(component); + ASSERT_EQ(1, pendingMediaRequests().size()); + + // The initial VectorGraph is looking for "http://bluebox", which hasn't been received + auto sg = root->getSceneGraph(); + ASSERT_TRUE( + CheckSceneGraph(sg, IsLayer(Rect(0, 0, 200, 200)).child(IsLayer(Rect(0, 0, 1, 1))))); + + // Change the source to "yellowBox", add it, and verify that the VG inflates correctly + executeCommand("SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "yellowBox"}}, + true); + ASSERT_EQ(1, pendingMediaRequests().size()); // Immediate graphic load + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .dirty(sg::Layer::kFlagRedrawContent | sg::Layer::kFlagSizeChanged) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::YELLOW)))))))); + + // Empty it + executeCommand( + "SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "http://bluebox"}}, true); + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)).dirty(sg::Layer::kFlagRedrawContent)))); + + // Set it back + executeCommand("SetValue", + {{"componentId", "TestVG"}, {"property", "source"}, {"value", "yellowBox"}}, + true); + ASSERT_EQ(1, pendingMediaRequests().size()); // Immediate graphic load + sg = root->getSceneGraph(); + ASSERT_TRUE(CheckSceneGraph( + sg, IsLayer(Rect(0, 0, 200, 200)) + .child(IsLayer(Rect(0, 0, 200, 200)) + .dirty(sg::Layer::kFlagRedrawContent) + .content(IsTransformNode().child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 200, 0, 200, 200, 0, 200})) + .pathOp(IsFillOp(IsColorPaint(Color::YELLOW)))))))); +} diff --git a/unit/scenegraph/unittest_sg_layer.cpp b/unit/scenegraph/unittest_sg_layer.cpp new file mode 100644 index 0000000..c834bbe --- /dev/null +++ b/unit/scenegraph/unittest_sg_layer.cpp @@ -0,0 +1,267 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/builder.h" + +using namespace apl; + +class SGLayerTest : public ::testing::Test { +}; + +TEST_F(SGLayerTest, Basic) +{ + auto layer = sg::layer("Test", Rect(10,20,200,300), 0.5f, Transform2D::scale(2.0f)); + + ASSERT_EQ("Test", layer->getName()); + + ASSERT_FALSE(layer->anyFlagSet()); + ASSERT_EQ(layer->debugFlagString(), ""); + ASSERT_EQ(layer->debugInteractionString(), ""); + + ASSERT_EQ(0, layer->children().size()); + ASSERT_FALSE(layer->content()); + + ASSERT_EQ(Rect(10,20,200,300), layer->getBounds()); + ASSERT_FALSE(layer->getOutline()); + ASSERT_FALSE(layer->getChildClip()); + ASSERT_EQ(0.5f, layer->getOpacity()); + ASSERT_EQ(Transform2D::scale(2.0f), layer->getTransform()); + ASSERT_EQ(Point(0,0), layer->getChildOffset()); + ASSERT_FALSE(layer->getShadow()); + ASSERT_FALSE(layer->getAccessibility()); + ASSERT_FALSE(layer->visible()); + ASSERT_EQ(layer->toDebugString(), "Layer"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(layer->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "name": "Test", + "opacity": 0.5, + "bounds": [ 10, 20, 200, 300 ], + "transform": [ 2, 0, 0, 2, 0, 0], + "childOffset": [ 0, 0 ], + "interaction": 0 + } + )apl"))); +} + +TEST_F(SGLayerTest, Rich) +{ + auto layer = sg::layer("Test", Rect(10, 20, 200, 300), 0.5f, Transform2D::scale(2.0f)); + layer->setContent( + sg::draw(sg::path(Rect(10, 10, 10, 10)), sg::fill(sg::paint(Color(Color::GREEN))))); + + ASSERT_TRUE(layer->visible()); + ASSERT_EQ(layer->debugFlagString(), "CONTENT"); + + ASSERT_TRUE(layer->setOpacity(1.0f)); + ASSERT_EQ(1.0f, layer->getOpacity()); + ASSERT_EQ(layer->debugFlagString(), "OPACITY CONTENT"); + + ASSERT_TRUE(layer->setBounds(Rect(10,20,100,100))); + ASSERT_EQ(layer->getBounds(), Rect(10,20,100,100)); + ASSERT_EQ(layer->debugFlagString(), "OPACITY SIZE CONTENT"); + + layer->clearFlags(); + ASSERT_EQ(0, layer->getAndClearFlags()); + + ASSERT_TRUE(layer->setBounds(Rect(0,0,100,100))); + ASSERT_EQ(layer->getBounds(), Rect(0,0,100,100)); + ASSERT_EQ(layer->debugFlagString(), "POSITION"); + + // Set a new transform same as the old transform + ASSERT_FALSE(layer->setTransform(Transform2D::scale(2.0f))); + ASSERT_EQ(layer->debugFlagString(), "POSITION"); + + // Actually change the transform + ASSERT_TRUE(layer->setTransform(Transform2D())); + ASSERT_EQ(Transform2D(), layer->getTransform()); + ASSERT_EQ(layer->debugFlagString(), "POSITION TRANSFORM"); + + layer->setChildOffset(Point(20,20)); + ASSERT_EQ(Point(20,20), layer->getChildOffset()); + ASSERT_EQ(layer->debugFlagString(), "POSITION TRANSFORM CHILD_OFFSET"); + + layer->setOutline(sg::path(Rect(0,0,100,100), 20)); + ASSERT_TRUE(layer->getOutline()); // Just check if it has been set + ASSERT_EQ(layer->debugFlagString(), "POSITION TRANSFORM CHILD_OFFSET OUTLINE"); + + layer->setShadow(sg::shadow(Color(Color::BLACK), Point(4,4), 10.0f)); + ASSERT_TRUE(layer->getShadow()); // Just check if it has been set + ASSERT_EQ(layer->debugFlagString(), "POSITION TRANSFORM CHILD_OFFSET OUTLINE SHADOW"); + + ASSERT_EQ(layer->getAndClearFlags(), + sg::Layer::kFlagPositionChanged | + sg::Layer::kFlagTransformChanged | + sg::Layer::kFlagChildOffsetChanged | + sg::Layer::kFlagOutlineChanged | + sg::Layer::kFlagRedrawShadow); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(layer->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "name": "Test", + "opacity": 1.0, + "bounds": [ 0, 0, 100, 100 ], + "transform": [ 1, 0, 0, 1, 0, 0], + "childOffset": [ 20, 20 ], + "outline": { + "type": "roundedRectPath", + "rect": [ 0, 0, 100, 100 ], + "radii": [ 20, 20, 20, 20 ] + }, + "shadow": { + "color": "#000000ff", + "offset": [ 4, 4 ], + "radius": 10 + }, + "content": [ + { + "type": "draw", + "path": { + "type": "rectPath", + "rect": [ 10, 10, 10, 10 ] + }, + "op": [ + { + "type": "fill", + "fillType": "even-odd", + "paint": { + "type": "colorPaint", + "color": "#008000ff", + "opacity": 1.0 + } + } + ] + } + ], + "interaction": 0 + } + )apl"))); +} + +TEST_F(SGLayerTest, Interaction) +{ + auto layer = sg::layer("Test", Rect(10, 20, 200, 300), 0.0f, Transform2D()); + + // Setting the interaction is used during startup and doesn't set any changed flags + layer->setInteraction(sg::Layer::kInteractionDisabled | sg::Layer::kInteractionChecked); + + ASSERT_EQ(layer->getInteraction(), sg::Layer::kInteractionDisabled | sg::Layer::kInteractionChecked); + ASSERT_EQ("disabled checked", layer->debugInteractionString()); + ASSERT_FALSE(layer->getAndClearFlags()); + + // Now update the interaction to add a few options + layer->updateInteraction(sg::Layer::kInteractionPressable, true); + ASSERT_EQ("disabled checked pressable", layer->debugInteractionString()); + ASSERT_EQ(sg::Layer::kFlagInteractionChanged, layer->getAndClearFlags()); + + layer->updateInteraction(sg::Layer::kInteractionDisabled, false); + layer->updateInteraction(sg::Layer::kInteractionScrollHorizontal | sg::Layer::kInteractionScrollVertical, true); + ASSERT_EQ("checked pressable scrollHorizontal scrollVertical", layer->debugInteractionString()); + ASSERT_EQ(sg::Layer::kFlagInteractionChanged, layer->getAndClearFlags()); + + // Verify that the last step cleared the flags + ASSERT_FALSE(layer->getAndClearFlags()); +} + +TEST_F(SGLayerTest, Children) +{ + auto layer = sg::layer("Test", Rect(0, 0, 100, 100), 1.0f, Transform2D()); + auto child1 = sg::layer("Child1", Rect(20, 20, 60, 10), 1.0f, Transform2D()); + auto child2 = sg::layer("Child2", Rect(20, 50, 60, 10), 1.0f, Transform2D()); + + ASSERT_EQ("Test", layer->getName()); + + ASSERT_FALSE(layer->anyFlagSet()); + ASSERT_FALSE(layer->visible()); + ASSERT_EQ(layer->debugFlagString(), ""); + ASSERT_EQ(layer->debugInteractionString(), ""); + ASSERT_EQ(0, layer->children().size()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(layer->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "name": "Test", + "opacity": 1.0, + "bounds": [ 0, 0, 100, 100 ], + "transform": [ 1, 0, 0, 1, 0, 0], + "childOffset": [ 0, 0 ], + "interaction": 0 + } + )apl"))); + + // Add one child + layer->appendChild(child1); + ASSERT_EQ(sg::Layer::kFlagChildrenChanged, layer->getAndClearFlags()); + ASSERT_EQ(1, layer->children().size()); + ASSERT_FALSE(layer->visible()); // Nothing is visible yet + + // Add another child + layer->appendChild(child2); + ASSERT_EQ(sg::Layer::kFlagChildrenChanged, layer->getAndClearFlags()); + ASSERT_EQ(2, layer->children().size()); + ASSERT_FALSE(layer->visible()); // Nothing is visible yet + + // Make this child visible + child2->setShadow(sg::shadow(Color(Color::BLACK), Point(2, 2), 5.0f)); + ASSERT_EQ(0, layer->getAndClearFlags()); + ASSERT_EQ(sg::Layer::kFlagRedrawShadow, child2->getAndClearFlags()); + ASSERT_TRUE(child2->visible()); + ASSERT_TRUE(layer->visible()); + + ASSERT_TRUE(IsEqual(layer->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "name": "Test", + "opacity": 1.0, + "bounds": [ 0, 0, 100, 100 ], + "transform": [ 1, 0, 0, 1, 0, 0], + "childOffset": [ 0, 0 ], + "interaction": 0, + "children": [ + { + "name": "Child1", + "opacity": 1.0, + "bounds": [ 20, 20, 60, 10 ], + "transform": [ 1, 0, 0, 1, 0, 0], + "childOffset": [ 0, 0 ], + "interaction": 0 + }, + { + "name": "Child2", + "opacity": 1.0, + "bounds": [ 20, 50, 60, 10 ], + "transform": [ 1, 0, 0, 1, 0, 0], + "childOffset": [ 0, 0 ], + "interaction": 0, + "shadow": { + "color": "#000000ff", + "offset": [2, 2], + "radius": 5 + } + } + ] + } + )apl"))); + +} \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_line_highlighting.cpp b/unit/scenegraph/unittest_sg_line_highlighting.cpp index bcbffc1..1685c58 100644 --- a/unit/scenegraph/unittest_sg_line_highlighting.cpp +++ b/unit/scenegraph/unittest_sg_line_highlighting.cpp @@ -180,16 +180,18 @@ TEST_F(AudioHighlightTest, BasicSceneGraph) // We've got the first speech mark, so we have Karaoke-Target state sg = root->getSceneGraph(); - ASSERT_TRUE(CheckSceneGraph( - sg, IsLayer(Rect{0, 0, 100, 100}, "...Text") - .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child( - IsTextNode().text("Fuzzy duck").range({0,0}).pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child( - IsTextNode().text("Fuzzy duck").range({1,1}).pathOp(IsFillOp(IsColorPaint(Color::RED))) - ) - ))); + ASSERT_TRUE( + CheckSceneGraph(sg, IsLayer(Rect{0, 0, 100, 100}, "...Text") + .dirty(sg::Layer::kFlagRedrawContent) + .content(IsTransformNode().child( + IsTextNode() + .text("Fuzzy duck") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Fuzzy duck") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))); // ======= Advance to the next speech mark ======== advanceTime(500); @@ -200,13 +202,15 @@ TEST_F(AudioHighlightTest, BasicSceneGraph) ASSERT_TRUE(CheckSceneGraph( sg, IsLayer(Rect{0, 0, 100, 100}, "...Text") .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child( - IsTextNode().text("Fuzzy duck").range({0,0}).pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .child( - IsTextNode().text("Fuzzy duck").range({1,1}).pathOp(IsFillOp(IsColorPaint(Color::GREEN))) - ) - ))); + .content(IsTransformNode().child( + IsTextNode() + .text("Fuzzy duck") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTextNode() + .text("Fuzzy duck") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))))))); // ======= Advance to the end of audio playback ======== advanceTime(500); @@ -310,7 +314,12 @@ TEST_F(AudioHighlightTest, Scrolling) { .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); // Execute SpeakItem with line highlighting. Align the line to "first" - executeCommand("SpeakItem", {{"componentId", "TEXT"}, {"sequencer", "MAGIC"}, {"highlightMode", "line"}, {"align", "first"}}, false); + executeCommand("SpeakItem", + {{"componentId", "TEXT"}, + {"sequencer", "MAGIC"}, + {"highlightMode", "line"}, + {"align", "first"}}, + false); ASSERT_TRUE(CheckPlayer("http://foo.com", TestAudioPlayer::kPreroll)); ASSERT_FALSE(factory->hasEvent()); @@ -360,17 +369,17 @@ TEST_F(AudioHighlightTest, Scrolling) { .vertical() .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + ))))); // ========== The second karaoke word hits. Starts scrolling Line2 =========== advanceTime(200); @@ -378,52 +387,54 @@ TEST_F(AudioHighlightTest, Scrolling) { // The second line turns GREEN sg = root->getSceneGraph(); ASSERT_TRUE(CheckSceneGraph( - sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") - .vertical() - .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") - .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 1}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({2, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + sg, + IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") + .vertical() + .child( + IsLayer(Rect{0, 0, 100, 100}, "...Text") + .dirty(sg::Layer::kFlagRedrawContent) + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({2, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))))); // ========== Advance past the initial scrolling but before the next word =========== advanceTime(100); sg = root->getSceneGraph(); ASSERT_TRUE(CheckSceneGraph( - sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") - .vertical() - .dirty(sg::Layer::kFlagChildOffsetChanged) - .childOffset(Point(0,20)) - .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 1}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({2, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + sg, + IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") + .vertical() + .dirty(sg::Layer::kFlagChildOffsetChanged) + .childOffset(Point(0, 20)) + .child( + IsLayer(Rect{0, 0, 100, 100}, "...Text") + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({2, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))))); // ========== Run off until all playback is done =========== - for (int i = 0 ; i < 2000 ; i += 100) + for (int i = 0; i < 2000; i += 100) advanceTime(100); // The player has finished @@ -437,18 +448,17 @@ TEST_F(AudioHighlightTest, Scrolling) { sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") .vertical() .dirty(sg::Layer::kFlagChildOffsetChanged) - .childOffset(Point(0,40)) + .childOffset(Point(0, 40)) .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))) - )))); - + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); } -TEST_F(AudioHighlightTest, ScrollingWithPreserve) { +TEST_F(AudioHighlightTest, ScrollingWithPreserve) +{ config->set(RootProperty::kScrollCommandDuration, 50); factory->addFakeContent({{"http://foo.com", @@ -478,7 +488,12 @@ TEST_F(AudioHighlightTest, ScrollingWithPreserve) { .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); // Execute SpeakItem with line highlighting. Align the line to "first" - executeCommand("SpeakItem", {{"componentId", "TEXT"}, {"sequencer", "MAGIC"}, {"highlightMode", "line"}, {"align", "first"}}, false); + executeCommand("SpeakItem", + {{"componentId", "TEXT"}, + {"sequencer", "MAGIC"}, + {"highlightMode", "line"}, + {"align", "first"}}, + false); ASSERT_TRUE(CheckPlayer("http://foo.com", TestAudioPlayer::kPreroll)); ASSERT_FALSE(factory->hasEvent()); @@ -528,17 +543,17 @@ TEST_F(AudioHighlightTest, ScrollingWithPreserve) { .vertical() .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + ))))); // ========== The second karaoke word hits. Starts scrolling Line2 =========== advanceTime(200); @@ -546,60 +561,62 @@ TEST_F(AudioHighlightTest, ScrollingWithPreserve) { // The second line turns GREEN sg = root->getSceneGraph(); ASSERT_TRUE(CheckSceneGraph( - sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") - .vertical() - .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") - .dirty(sg::Layer::kFlagRedrawContent) - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 1}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({2, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + sg, + IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") + .vertical() + .child( + IsLayer(Rect{0, 0, 100, 100}, "...Text") + .dirty(sg::Layer::kFlagRedrawContent) + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({2, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))))); // ========== Advance past the initial scrolling but before the next word =========== advanceTime(100); sg = root->getSceneGraph(); ASSERT_TRUE(CheckSceneGraph( - sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") - .vertical() - .dirty(sg::Layer::kFlagChildOffsetChanged) - .childOffset(Point(0,20)) - .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({0, 0}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({1, 1}) - .pathOp(IsFillOp(IsColorPaint(Color::GREEN)))) - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .range({2, 4}) - .pathOp(IsFillOp(IsColorPaint(Color::RED)))) - )))); + sg, + IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") + .vertical() + .dirty(sg::Layer::kFlagChildOffsetChanged) + .childOffset(Point(0, 20)) + .child( + IsLayer(Rect{0, 0, 100, 100}, "...Text") + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({0, 0}) + .pathOp(IsFillOp(IsColorPaint(Color::RED))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({1, 1}) + .pathOp(IsFillOp(IsColorPaint(Color::GREEN))) + .next(IsTextNode() + .text("Line1Line2Line3Line4Line5") + .range({2, 4}) + .pathOp(IsFillOp(IsColorPaint(Color::RED)))))))))); auto playerTimer = factory->getPlayers().at(0).lock()->getTimeoutId(); loop->freeze(playerTimer); - configChange(ConfigurationChange(1000,1000)); + configChange(ConfigurationChange(1000, 1000)); processReinflate(); loop->rehydrate(playerTimer); // ========== Run off until all playback is done =========== - for (int i = 0 ; i < 2000 ; i += 100) + for (int i = 0; i < 2000; i += 100) advanceTime(100); // The player has finished @@ -612,14 +629,12 @@ TEST_F(AudioHighlightTest, ScrollingWithPreserve) { ASSERT_TRUE(CheckSceneGraph( sg, IsLayer(Rect{0, 0, 100, 60}, "...ScrollView") .vertical() - .childOffset(Point(0,40)) + .childOffset(Point(0, 40)) .child(IsLayer(Rect{0, 0, 100, 100}, "...Text") - .content(IsTransformNode() - .child(IsTextNode() - .text("Line1Line2Line3Line4Line5") - .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))) - )))); - + .content(IsTransformNode().child( + IsTextNode() + .text("Line1Line2Line3Line4Line5") + .pathOp(IsFillOp(IsColorPaint(Color::BLUE)))))))); } static const char *SPEECH_MARK_HANDLER = R"apl({ diff --git a/unit/scenegraph/unittest_sg_node.cpp b/unit/scenegraph/unittest_sg_node.cpp new file mode 100644 index 0000000..b7daa7e --- /dev/null +++ b/unit/scenegraph/unittest_sg_node.cpp @@ -0,0 +1,473 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0/ +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +* +*/ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/builder.h" +#include "apl/scenegraph/node.h" +#include "apl/scenegraph/textpropertiescache.h" + +using namespace apl; + +class SGNodeTest : public ::testing::Test { +public: + std::shared_ptr measure = std::make_shared(); +}; + + +TEST_F(SGNodeTest, DrawNode) +{ + auto path = sg::path(Rect(0, 10, 20, 30)); + auto op = sg::fill(sg::paint(Color(Color::BLUE))); + auto node = sg::draw(path, op); + ASSERT_EQ(node->toDebugString(), "DrawNode"); + ASSERT_TRUE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "draw", + "path": { + "type": "rectPath", + "rect": [0.0,10.0,20.0,30.0] + }, + "op": [ + { + "paint": { + "opacity": 1.0, + "type": "colorPaint", + "color": "#0000ffff" + }, + "type": "fill", + "fillType": "even-odd" + } + ] + } + )apl"))); + + ASSERT_TRUE(sg::DrawNode::is_type(node)); + auto draw = sg::DrawNode::cast(node); + ASSERT_TRUE(draw); + + ASSERT_FALSE(draw->setPath(path)); + ASSERT_TRUE(draw->setPath(sg::path(Rect(0, 10, 20, 31)))); + + ASSERT_TRUE(op->paint->setOpacity(0.0f)); + ASSERT_FALSE(draw->visible()); + + ASSERT_FALSE(draw->setOp(op)); + ASSERT_TRUE(draw->setOp(sg::fill(sg::paint(Color(Color::RED))))); +} + + +TEST_F(SGNodeTest, TextNode) +{ + sg::TextPropertiesCache cache; + auto chunk = sg::TextChunk::create(StyledText::createRaw("hello, world")); + auto properties = + sg::TextProperties::create(cache, {"Arial"}, 12, FontStyle::kFontStyleNormal, 500); + auto textLayout = measure->layout(chunk, properties, + 100, // Width + MeasureMode::AtMost, + 100, // Height + MeasureMode::Exactly); + auto paint = sg::paint(Color(Color::RED)); + auto op = sg::fill(paint); + auto node = sg::text(textLayout, op, Range(0,5)); + + ASSERT_EQ("TextNode size=96.000000x100.000000 range=Range<0,5> text=hello, world", node->toDebugString()); + ASSERT_TRUE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "text", + "op": [ + { + "paint": { + "opacity": 1.0, + "type": "colorPaint", + "color": "#ff0000ff" + }, + "type": "fill", + "fillType": "even-odd"} + ], + "range":{ + "lowerBound":0, + "upperBound":5 + }, + "layout": null + } + )apl"))); + + ASSERT_TRUE(sg::TextNode::is_type(node)); + auto text = sg::TextNode::cast(node); + ASSERT_TRUE(text); + + ASSERT_FALSE(text->setTextLayout(textLayout)); + ASSERT_FALSE(text->setRange(Range(0,5))); + ASSERT_TRUE(text->setRange(Range(0,4))); + + ASSERT_TRUE(op->paint->setOpacity(0.0f)); + ASSERT_FALSE(node->visible()); + + ASSERT_FALSE(text->setOp(op)); + ASSERT_TRUE(text->setOp(sg::fill(sg::paint(Color(Color::TEAL))))); +} + + +TEST_F(SGNodeTest, TransformNode) +{ + auto node = sg::transform(); + ASSERT_EQ("TransformNode transform=Transform2D<1.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000>", node->toDebugString()); + ASSERT_FALSE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type":"transform", + "transform": [1,0,0,1,0,0] + } + )apl"))); + + ASSERT_TRUE(sg::TransformNode::is_type(node)); + auto transform = sg::TransformNode::cast(node); + ASSERT_TRUE(transform); + + ASSERT_FALSE(transform->setTransform(Transform2D())); + ASSERT_TRUE(transform->setTransform(Transform2D::scale(2))); +} + + +TEST_F(SGNodeTest, ClipNode) +{ + auto path = sg::path(Rect(0,0,20,20)); + auto child = sg::transform(); + auto node = sg::clip(path, child); + + ASSERT_EQ("ClipNode", node->toDebugString()); + ASSERT_FALSE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type":"clip", + "path": { + "type": "rectPath", + "rect": [0, 0, 20, 20] + }, + "children":[ + { + "type":"transform", + "transform":[1,0,0,1,0,0] + } + ] + } + )apl"))); + + ASSERT_TRUE(sg::ClipNode::is_type(node)); + auto clip = sg::ClipNode::cast(node); + ASSERT_TRUE(clip); + + ASSERT_FALSE(clip->setPath(path)); + ASSERT_TRUE(clip->setPath(sg::path(Rect(0,0,20,21)))); +} + + +TEST_F(SGNodeTest, OpacityNode) +{ + auto child = sg::transform(); + auto node = sg::opacity(0.5f, child); + + ASSERT_EQ("OpacityNode opacity=0.500000", node->toDebugString()); + ASSERT_FALSE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type":"opacity", + "opacity": 0.5, + "children":[ + { + "type":"transform", + "transform":[1,0,0,1,0,0] + } + ] + } + )apl"))); + + ASSERT_TRUE(sg::OpacityNode::is_type(node)); + auto opacity = sg::OpacityNode::cast(node); + ASSERT_TRUE(opacity); + + ASSERT_FALSE(opacity->setOpacity(0.5f)); + ASSERT_TRUE(opacity->setOpacity(1.0f)); +} + + +TEST_F(SGNodeTest, ImageNode) +{ + auto paint = sg::paint(Color(Color::RED)); + auto filter = sg::solid(paint); + auto node = sg::image(filter, Rect(0, 0, 100, 100), Rect(0, 0, 1, 1)); + + ASSERT_EQ("ImageNode target=Rect<100x100+0+0> source=Rect<1x1+0+0>", node->toDebugString()); + ASSERT_TRUE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type":"image", + "target": [0, 0, 100, 100], + "source": [0, 0, 1, 1], + "image":{ + "type":"solidFilter", + "paint":{ + "opacity":1, + "type":"colorPaint", + "color":"#ff0000ff" + } + } + } + )apl"))); + + ASSERT_TRUE(sg::ImageNode::is_type(node)); + auto image = sg::ImageNode::cast(node); + ASSERT_TRUE(image); + + ASSERT_FALSE(image->setTarget(Rect(0, 0, 100, 100))); + ASSERT_TRUE(image->setTarget(Rect(0, 0, 100, 101))); + + ASSERT_FALSE(image->setSource(Rect(0, 0, 1, 1))); + ASSERT_TRUE(image->setSource(Rect(0, 0, 2, 1))); + + ASSERT_FALSE(image->setImage(filter)); + ASSERT_TRUE(image->setImage(sg::solid(sg::paint(Color(Color::TRANSPARENT))))); + ASSERT_FALSE(node->visible()); +} + + +class TrivialMediaPlayer : public MediaPlayer { +public: + TrivialMediaPlayer(MediaPlayerCallback callback, const std::string& name) + : MediaPlayer(callback), + mName(name) {} + + void release() override {} + void halt() override {} + void setTrackList(std::vector tracks) override {} + void play(ActionRef actionRef) override {} + void pause() override {} + void next() override {} + void previous() override {} + void rewind() override {} + void seek( int offset ) override {} + void setTrackIndex( int trackIndex ) override {} + void setAudioTrack( AudioTrack audioTrack ) override {} + + rapidjson::Value serialize(rapidjson::Document::AllocatorType& allocator) const override { + auto result = rapidjson::Value(rapidjson::kObjectType); + result.AddMember("name", rapidjson::Value(mName.c_str(), allocator).Move(), allocator); + return result; + } + +private: + std::string mName; +}; + + +TEST_F(SGNodeTest, VideoNode) +{ + auto player = std::make_shared([](MediaPlayerEventType eventType, + const MediaState& state){}, "Foobar"); + auto node = sg::video(player, Rect(0, 0, 100, 100), VideoScale::kVideoScaleBestFill); + + ASSERT_EQ("VideoNode target=Rect<100x100+0+0> PLAYER", node->toDebugString()); + ASSERT_TRUE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "video", + "target": [0, 0, 100, 100], + "scale": "best-fill", + "player":{ + "name": "Foobar" + } + } + )apl"))); + + ASSERT_TRUE(sg::VideoNode::is_type(node)); + auto video = sg::VideoNode::cast(node); + ASSERT_TRUE(video); + + ASSERT_FALSE(video->setTarget(Rect(0, 0, 100, 100))); + ASSERT_TRUE(video->setTarget(Rect(0, 0, 100, 101))); + + ASSERT_FALSE(video->setMediaPlayer(player)); + + player = std::make_shared([](MediaPlayerEventType eventType, + const MediaState& state){}, "New player"); + ASSERT_TRUE(video->setMediaPlayer(player)); + ASSERT_TRUE(node->visible()); + + ASSERT_FALSE(video->setScale(VideoScale::kVideoScaleBestFill)); + ASSERT_TRUE(video->setScale(VideoScale::kVideoScaleBestFit)); +} + + +TEST_F(SGNodeTest, ShadowNode) +{ + auto child = sg::transform(); + auto shadow = sg::shadow(Color(Color::FUCHSIA), Point(5,5), 3.0f); + auto node = sg::shadowNode(shadow, child); + + ASSERT_EQ("ShadowNode", node->toDebugString()); + ASSERT_FALSE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type":"shadow", + "shadow":{ + "color":"#ff00ffff", + "offset":[5,5], + "radius":3.0 + }, + "children":[ + { + "type":"transform", + "transform":[1,0,0,1,0,0] + } + ] + } + )apl"))); + + ASSERT_TRUE(sg::ShadowNode::is_type(node)); + auto shadowNode = sg::ShadowNode::cast(node); + ASSERT_TRUE(shadowNode); + + ASSERT_FALSE(shadowNode->setShadow(shadow)); + ASSERT_TRUE(shadowNode->setShadow(sg::shadow(Color(Color::YELLOW), Point(5,5), 3.0f))); +} + + +class TrivialEditText : public sg::EditText { +public: + TrivialEditText() : EditText([]() {}, [](const std::string& text) {}, [](bool isFocused) {}) {} + + void release() override {} + void setFocus(bool hasFocus) override {} +}; + +class TrivialEditTextBox : public sg::EditTextBox { + Size getSize() const override { return Size(100, 20); } + float getBaseline() const override { return 14.0f; } +}; + +TEST_F(SGNodeTest, EditTextNode) +{ + sg::TextPropertiesCache cache; + + auto editText = std::make_shared(); + auto editTextBox = std::make_shared(); + auto properties = + sg::TextProperties::create(cache, {"Arial"}, 12, FontStyle::kFontStyleNormal, 500); + + auto editTextConfig = sg::EditTextConfig::create( + Color(Color::RED), + Color(Color::BLUE), + KeyboardType::kKeyboardTypeEmailAddress, + "klingon", + 23, + false, // Secure input + SubmitKeyType::kSubmitKeyTypeGo, + "a-zA-Z@.", + false, // Select on focus + KeyboardBehaviorOnFocus::kBehaviorOnFocusSystemDefault, + properties + ); + + auto node = sg::editText(editText, editTextBox, editTextConfig, "Hello, world!"); + + ASSERT_EQ("EditTextNode text=Hello, world! color=#ff0000ff", node->toDebugString()); + ASSERT_TRUE(node->visible()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(node->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "edit", + "box": { + "size": [100,20], + "baseline": 14 + }, + "config": { + "textColor": "#ff0000ff", + "highlightColor": "#0000ffff", + "keyboardType": "emailAddress", + "keyboardBehaviorOnFocus": "systemDefault", + "language": "klingon", + "maxLength": 23, + "secureInput": false, + "selectOnFocus": false, + "submitKeyType": "go", + "validCharacters": "a-zA-Z@.", + "textProperties": { + "fontFamily": ["Arial"], + "fontSize": 12, + "fontStyle": "normal", + "fontWeight": 500, + "letterSpacing": 0, + "lineHeight": 1.25, + "maxLines": 0, + "textAlign": "auto", + "textAlignVertical": "auto" + } + }, + "text": "Hello, world!" + } + )apl"))); + + ASSERT_TRUE(sg::EditTextNode::is_type(node)); + auto edit = sg::EditTextNode::cast(node); + ASSERT_TRUE(edit); + + ASSERT_FALSE(edit->setEditText(editText)); + ASSERT_TRUE(edit->setEditText(std::make_shared())); + + ASSERT_FALSE(edit->setEditTextBox(editTextBox)); + ASSERT_TRUE(edit->setEditTextBox(std::make_shared())); + + ASSERT_FALSE(edit->setEditTextConfig(editTextConfig)); + ASSERT_TRUE(edit->setEditTextConfig( sg::EditTextConfig::create( + Color(Color::RED), + Color(Color::BLUE), + KeyboardType::kKeyboardTypeEmailAddress, + "klingon", + 23, + false, // Secure input + SubmitKeyType::kSubmitKeyTypeGo, + "a-zA-Z@.", + false, // Select on focus + KeyboardBehaviorOnFocus::kBehaviorOnFocusSystemDefault, + properties + ))); + + ASSERT_FALSE(edit->setText("Hello, world!")); + ASSERT_TRUE(edit->setText("Goodbye...")); +} + diff --git a/unit/scenegraph/unittest_sg_nodebounds.cpp b/unit/scenegraph/unittest_sg_nodebounds.cpp new file mode 100644 index 0000000..9d66d4b --- /dev/null +++ b/unit/scenegraph/unittest_sg_nodebounds.cpp @@ -0,0 +1,269 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" +#include "apl/scenegraph/builder.h" +#include "apl/scenegraph/textpropertiescache.h" + +using namespace apl; + +class SGNodeBoundsTest : public DocumentWrapper { +public: + std::shared_ptr measure = std::make_shared(); +}; + +class SGNodeBoundsTestMediaObject : public MediaObject { +public: + static MediaObjectPtr createImage(Size size) { + auto result = std::make_shared(); + result->mType = EventMediaType::kEventMediaTypeImage; + result->mSize = size; + return result; + } + + std::string url() const override { return "TestImage"; } + State state() const override { return mState; } + EventMediaType type() const override { return mType; } + Size size() const override { return mSize; } + int errorCode() const override { return 0; } + std::string errorDescription() const override { return ""; } + const HeaderArray& headers() const override { return mHeaders; } + + CallbackID addCallback(MediaObjectCallback callback) override { return 0; } + void removeCallback(CallbackID callbackId) override {} + + Size mSize; + State mState = MediaObject::State::kReady; + EventMediaType mType = EventMediaType::kEventMediaTypeImage; + HeaderArray mHeaders; +}; + +class SGNodeBoundsTestMediaPlayer : public MediaPlayer { +public: + static MediaPlayerPtr create() { + return std::make_shared(); + } + + SGNodeBoundsTestMediaPlayer() : MediaPlayer([](MediaPlayerEventType, const MediaState&){}) {}; + void release() override {} + void halt() override {} + void setTrackList(std::vector tracks) override {} + void play(ActionRef actionRef) override {} + void pause() override {} + void next() override {} + void previous() override {} + void rewind() override {} + void seek(int offset) override {} + void setTrackIndex(int trackIndex) override {} + void setAudioTrack(AudioTrack audioTrack) override {} +}; + +TEST_F(SGNodeBoundsTest, DrawNode) +{ + auto paint = sg::paint(Color(Color::BLACK)); + + auto node = sg::draw(sg::path("L10,20 80,-20"), sg::fill(paint)); + ASSERT_TRUE(IsEqual(Rect(0, -20, 80, 40), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(0, -10, 40, 20), node->boundingBox(Transform2D::scale(0.5)))); + + // Empty path + node = sg::draw(sg::path("M10,10"), sg::fill(paint)); + ASSERT_TRUE(IsEqual(Rect(), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(), node->boundingBox(Transform2D::scale(20)))); + + // Path with a stroke width + node = sg::draw(sg::path("L10,20 80,-20"), + sg::stroke(paint).strokeWidth(4).lineJoin(apl::kGraphicLineJoinRound).get()); + ASSERT_TRUE(IsEqual(Rect(-2, -22, 84, 44), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(-1, -11, 42, 22), node->boundingBox(Transform2D::scale(0.5)))); + + // A series of drawing operations - use the one with the maximum width + auto op = sg::fill(paint); + auto op2 = sg::stroke(paint).strokeWidth(4).lineJoin(apl::kGraphicLineJoinRound).get(); + auto op3 = sg::stroke(paint).strokeWidth(6).lineJoin(apl::kGraphicLineJoinRound).get(); + op2->nextSibling = op3; + op->nextSibling = op2; + node = sg::draw(sg::path("L10,20 80,-20"), op); + ASSERT_TRUE(IsEqual(Rect(-3, -23, 86, 46), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(-6, -46, 172, 92), node->boundingBox(Transform2D::scale(2)))); +} + +TEST_F(SGNodeBoundsTest, TextNode) +{ + sg::TextPropertiesCache cache; + auto chunk = sg::TextChunk::create(StyledText::createRaw("hello, world")); + auto properties = + sg::TextProperties::create(cache, {"Arial"}, 12, FontStyle::kFontStyleNormal, 500); + auto textLayout = measure->layout(chunk, properties, + 100, // Width + MeasureMode::AtMost, + 100, // Height + MeasureMode::Exactly); + auto paint = sg::paint(Color(Color::RED)); + auto op = sg::fill(paint); + auto node = sg::text(textLayout, op, Range(0,1)); + + ASSERT_TRUE(IsEqual(Rect(0,0,96,24), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(10,10,96,24), node->boundingBox(Transform2D::translate(10,10)))); + ASSERT_TRUE(IsEqual(Rect(0,0,48,12), node->boundingBox(Transform2D::scale(0.5)))); + + sg::TextNode::cast(node)->setRange(Range(0,0)); + ASSERT_TRUE(IsEqual(Rect(0,0,96,12), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(10,10,96,12), node->boundingBox(Transform2D::translate(10,10)))); + ASSERT_TRUE(IsEqual(Rect(0,0,48,6), node->boundingBox(Transform2D::scale(0.5)))); + + op->nextSibling = sg::stroke(paint).strokeWidth(2).lineJoin(apl::kGraphicLineJoinRound).get(); + ASSERT_TRUE(IsEqual(Rect(-1,-1,98,14), node->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(9,9,98,14), node->boundingBox(Transform2D::translate(10,10)))); + ASSERT_TRUE(IsEqual(Rect(-0.5,-0.5,49,7), node->boundingBox(Transform2D::scale(0.5)))); +} + +TEST_F(SGNodeBoundsTest, ImageNode) +{ + auto node = sg::image(sg::filter(SGNodeBoundsTestMediaObject::createImage({200, 300})), + Rect{20, 20, 100, 100}, Rect{0, 0, 100, 100}); + ASSERT_TRUE(IsEqual(Rect(20, 20, 100, 100), node->boundingBox(Transform2D()))); + ASSERT_TRUE( + IsEqual(Rect(0, 0, 100, 100), node->boundingBox(Transform2D::translate(-20, -20)))); + ASSERT_TRUE(IsEqual(Rect(40, 40, 200, 200), node->boundingBox(Transform2D::scale(2)))); +} + +TEST_F(SGNodeBoundsTest, VideoNode) +{ + auto node = sg::video(SGNodeBoundsTestMediaPlayer::create(), Rect(20, 20, 100, 100), + VideoScale::kVideoScaleBestFill); + ASSERT_TRUE(IsEqual(Rect(20, 20, 100, 100), node->boundingBox(Transform2D()))); + ASSERT_TRUE( + IsEqual(Rect(0, 0, 100, 100), node->boundingBox(Transform2D::translate(-20, -20)))); + ASSERT_TRUE(IsEqual(Rect(40, 40, 200, 200), node->boundingBox(Transform2D::scale(2)))); +} + +class SampleEditText : public sg::EditText { +public: + SampleEditText() : EditText([]() {}, [](const std::string& text) {}, [](bool isFocused) {}) {} + + void release() override {} + void setFocus(bool hasFocus) override {} +}; + +class SampleEditTextBox : public sg::EditTextBox { + Size getSize() const override { return {100, 2}; } + float getBaseline() const override { return 14.0f; } +}; + +TEST_F(SGNodeBoundsTest, EditNode) +{ + auto editText = std::make_shared(); + auto editTextBox = std::make_shared(); + sg::TextPropertiesCache cache; + auto properties = + sg::TextProperties::create(cache, {"Arial"}, 12, FontStyle::kFontStyleNormal, 500); + + auto editTextConfig = sg::EditTextConfig::create( + Color(Color::RED), + Color(Color::BLUE), + KeyboardType::kKeyboardTypeEmailAddress, + "klingon", + 23, + false, // Secure input + SubmitKeyType::kSubmitKeyTypeGo, + "a-zA-Z@.", + false, // Select on focus + KeyboardBehaviorOnFocus::kBehaviorOnFocusSystemDefault, + properties + ); + + auto node = sg::editText(editText, editTextBox, editTextConfig, "Hello, world!"); + + ASSERT_TRUE(IsEqual(Rect(), node->boundingBox(Transform2D()))); +} + +TEST_F(SGNodeBoundsTest, CombiningNodes) +{ + auto paint = sg::paint(Color(Color::BLACK)); + auto node = sg::draw(sg::path("L10,20"), sg::fill(paint)); + + // Transforms + ASSERT_TRUE(IsEqual(Rect(0, 0, 10, 20), node->boundingBox(Transform2D()))); + auto transform = sg::transform(Transform2D::translate(5, 10), node); + ASSERT_TRUE(IsEqual(Rect(5, 10, 10, 20), transform->boundingBox(Transform2D()))); + transform = sg::transform(Transform2D::rotate(90), node); + ASSERT_TRUE(IsEqual(Rect(-20, 0, 20, 10), transform->boundingBox(Transform2D()))); + // Stack a transform + transform = sg::transform(Transform2D::scale(2), transform); + ASSERT_TRUE(IsEqual(Rect(-40, 0, 40, 20), transform->boundingBox(Transform2D()))); + + // Clip nodes + auto clip = sg::clip(sg::path(Rect{2, 3, 50, 5}), node); + ASSERT_TRUE(IsEqual(Rect(2, 3, 8, 5), clip->boundingBox(Transform2D()))); // Intersection + + // Opacity node + auto opacity = sg::opacity(0.0f, node); // Doesn't do anything + ASSERT_TRUE(IsEqual(Rect(0, 0, 10, 20), opacity->boundingBox(Transform2D()))); + + // Shadow node - missing + ASSERT_TRUE( + IsEqual(Rect(0, 0, 10, 20), sg::shadowNode(nullptr, node)->boundingBox(Transform2D()))); + + // Sharp shadows + auto sharp = sg::shadow(Color::BLACK, Point{5, 10}, 0.0f); + ASSERT_TRUE( + IsEqual(Rect(0, 0, 15, 30), sg::shadowNode(sharp, node)->boundingBox(Transform2D()))); + + sharp = sg::shadow(Color::BLACK, Point{-5, -10}, 0.0f); // Go the other direction + ASSERT_TRUE( + IsEqual(Rect(-5, -10, 15, 30), sg::shadowNode(sharp, node)->boundingBox(Transform2D()))); + + auto blurry = sg::shadow(Color::BLACK, Point{0, 0}, 4.0f); // Blurry, no shift + ASSERT_TRUE( + IsEqual(Rect(-4, -4, 18, 28), sg::shadowNode(blurry, node)->boundingBox(Transform2D()))); + + auto offset_blurry = sg::shadow(Color::BLACK, Point{3, 5}, 4.0f); + ASSERT_TRUE(IsEqual(Rect(-1, 0, 18, 29), + sg::shadowNode(offset_blurry, node)->boundingBox(Transform2D()))); + + offset_blurry = sg::shadow(Color::BLACK, Point{6, -3}, 4.0f); + ASSERT_TRUE(IsEqual(Rect(0, -7, 20, 28), + sg::shadowNode(offset_blurry, node)->boundingBox(Transform2D()))); +} + +TEST_F(SGNodeBoundsTest, NodeSiblings) +{ + auto paint = sg::paint(Color(Color::BLACK)); + + auto n1 = sg::draw(sg::path("l10,20"), sg::fill(paint)); + auto n2 = sg::draw(sg::path("M6,2 l15,25"), sg::fill(paint)); + auto n3 = sg::draw(sg::path("l10,50"), sg::fill(paint)); + auto n4 = sg::draw(sg::path("M-14,18 l15,15"), sg::fill(paint)); + + n1->setNext(n2); + n2->setNext(n3); + n3->setNext(n4); + + // Measuring the size of a node just gets that node itself and any children + ASSERT_TRUE(IsEqual(Rect(0, 0, 10, 20), n1->boundingBox(Transform2D()))); + + // Use the calculate method to include siblings + ASSERT_TRUE(IsEqual(Rect(-14, 0, 35, 50), sg::Node::calculateBoundingBox(n1, Transform2D()))); + + // Adding a parent node captures just the siblings in the chain. In this case, n3 & n4 + auto t = sg::transform(Transform2D::scale(0.5), n3); + ASSERT_TRUE(IsEqual(Rect(-7, 0, 12, 25), t->boundingBox(Transform2D()))); + ASSERT_TRUE(IsEqual(Rect(-14, 0, 24, 50), t->boundingBox(Transform2D::scale(2)))); +} + + diff --git a/unit/scenegraph/unittest_sg_paint.cpp b/unit/scenegraph/unittest_sg_paint.cpp new file mode 100644 index 0000000..09c89a2 --- /dev/null +++ b/unit/scenegraph/unittest_sg_paint.cpp @@ -0,0 +1,268 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/paint.h" +#include "apl/scenegraph/builder.h" + +using namespace apl; + +class SGPaintTest : public ::testing::Test {}; + +TEST_F(SGPaintTest, ColorPaint) +{ + auto paint = sg::paint(Color(Color::BLUE), 0.5f); + ASSERT_EQ(paint->toDebugString(), "ColorPaint color=#0000ffff opacity=0.500000"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(paint->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "colorPaint", + "color": "#0000ffff", + "opacity": 0.5 + } + )apl"))); + + ASSERT_TRUE(paint->setOpacity(1.0f)); + ASSERT_EQ(1.0f, paint->getOpacity()); + + ASSERT_FALSE(paint->setTransform(Transform2D())); + + // Check visibility + ASSERT_TRUE(paint->visible()); + ASSERT_TRUE(paint->setOpacity(0.0f)); + ASSERT_FALSE(paint->visible()); + + ASSERT_TRUE(paint->setOpacity(1.0f)); + ASSERT_TRUE(paint->visible()); + + ASSERT_TRUE(sg::ColorPaint::is_type(paint)); + ASSERT_TRUE(sg::ColorPaint::cast(paint)->setColor(Color::RED)); + ASSERT_EQ(sg::ColorPaint::cast(paint)->getColor(), Color::RED); + ASSERT_TRUE(paint->visible()); + + ASSERT_TRUE(sg::ColorPaint::cast(paint)->setColor(Color::TRANSPARENT)); + ASSERT_FALSE(paint->visible()); +} + +static const char *LINEAR_GRADIENT = R"apl( +{ + "type": "linear", + "colorRange": [ + "black", + "white" + ], + "inputRange": [ + 0, + 0.4 + ], + "angle": 90 +} +)apl"; + +TEST_F(SGPaintTest, StandardLinearGradientPaint) +{ + auto config = RootConfig::create(); + auto metrics = Metrics().size(1024,800).dpi(160).theme("dark"); + auto context = Context::createTestContext(metrics, *config); + + rapidjson::Document doc; + doc.Parse(LINEAR_GRADIENT); + + auto grad = Gradient::create(*context, Object(doc)); + auto paint = sg::paint(grad.get(), 0.5f, Transform2D::scale(2)); + + ASSERT_TRUE(sg::LinearGradientPaint::is_type(paint)); + auto linear = sg::LinearGradientPaint::cast(paint); + + ASSERT_EQ(linear->getStart(), Point(0,0.5)); + ASSERT_EQ(linear->getEnd(), Point(1,0.5)); + ASSERT_EQ(linear->getPoints(), std::vector({0, 0.4})); + ASSERT_EQ(linear->getColors(), std::vector({Color::BLACK, Color::WHITE})); + ASSERT_EQ(linear->getSpreadMethod(), Gradient::GradientSpreadMethod::PAD); + ASSERT_TRUE(linear->getUseBoundingBox()); + ASSERT_EQ(0.5f, linear->getOpacity()); + ASSERT_EQ(Transform2D::scale(2), linear->getTransform()); + ASSERT_TRUE(linear->visible()); + + ASSERT_EQ(linear->toDebugString(), "LinearGradientPaint " + "points=[0.000000,0.400000] " + "colors=[#000000ff,#ffffffff] " + "spread=0 " + "bb=yes " + "opacity=0.500000 " + "transform=Transform2D<2.000000, 0.000000, 0.000000, 2.000000, 0.000000, 0.000000> " + "start=0.000000,0.500000 " + "end=1.000000,0.500000"); + + ASSERT_TRUE(IsEqual(linear->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "linearGradient", + "opacity": 0.5, + "transform": [2.0,0.0,0.0,2.0,0.0,0.0], + "points": [0.0,0.4], + "colors": ["#000000ff","#ffffffff"], + "spreadMethod": "pad", + "usingBoundingBox": true, + "start": [0.0,0.5], + "end": [1.0,0.5] + } + )apl"))); + + ASSERT_TRUE(linear->setSpreadMethod(Gradient::GradientSpreadMethod::REFLECT)); + ASSERT_EQ(Gradient::GradientSpreadMethod::REFLECT, linear->getSpreadMethod()); + ASSERT_FALSE(linear->setSpreadMethod(Gradient::GradientSpreadMethod::REFLECT)); + + ASSERT_TRUE(linear->setUseBoundingBox(false)); + ASSERT_FALSE(linear->getUseBoundingBox()); + ASSERT_FALSE(linear->setUseBoundingBox(false)); +} + + +static const char *RADIAL_GRADIENT = R"apl( +{ + "type": "radial", + "colorRange": [ + "black", + "white" + ], + "inputRange": [ + 0, + 0.4 + ] +} +)apl"; + +TEST_F(SGPaintTest, StandardRadialGradientPaint) +{ + auto config = RootConfig::create(); + auto metrics = Metrics().size(1024,800).dpi(160).theme("dark"); + auto context = Context::createTestContext(metrics, *config); + + rapidjson::Document doc; + doc.Parse(RADIAL_GRADIENT); + + auto grad = Gradient::create(*context, Object(doc)); + auto paint = sg::paint(grad.get(), 0.5f, Transform2D::scale(2)); + + ASSERT_TRUE(sg::RadialGradientPaint::is_type(paint)); + auto radial = sg::RadialGradientPaint::cast(paint); + + ASSERT_EQ(radial->getCenter(), Point(0.5,0.5)); + ASSERT_FLOAT_EQ(radial->getRadius(), 0.7071); // Note: Not exactly correct, but what is used elsewhere + ASSERT_EQ(radial->getPoints(), std::vector({0, 0.4})); + ASSERT_EQ(radial->getColors(), std::vector({Color::BLACK, Color::WHITE})); + ASSERT_EQ(radial->getSpreadMethod(), Gradient::GradientSpreadMethod::PAD); + ASSERT_TRUE(radial->getUseBoundingBox()); + ASSERT_EQ(0.5f, radial->getOpacity()); + ASSERT_EQ(Transform2D::scale(2), radial->getTransform()); + ASSERT_TRUE(radial->visible()); + + ASSERT_EQ(radial->toDebugString(), "RadialGradientPaint " + "points=[0.000000,0.400000] " + "colors=[#000000ff,#ffffffff] " + "spread=0 " + "bb=yes " + "opacity=0.500000 " + "transform=Transform2D<2.000000, 0.000000, 0.000000, 2.000000, 0.000000, 0.000000> " + "center=0.500000,0.500000 " + "radius=0.707100"); + + ASSERT_TRUE(IsEqual(radial->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "radialGradient", + "opacity": 0.5, + "transform": [2.0,0.0,0.0,2.0,0.0,0.0], + "points": [0.0,0.4], + "colors": ["#000000ff","#ffffffff"], + "spreadMethod": "pad", + "usingBoundingBox": true, + "center": [0.5,0.5], + "radius": 0.707099974155426 + } + )apl"))); + + ASSERT_TRUE(radial->setSpreadMethod(Gradient::GradientSpreadMethod::REFLECT)); + ASSERT_EQ(Gradient::GradientSpreadMethod::REFLECT, radial->getSpreadMethod()); + ASSERT_FALSE(radial->setSpreadMethod(Gradient::GradientSpreadMethod::REFLECT)); + + ASSERT_TRUE(radial->setUseBoundingBox(false)); + ASSERT_FALSE(radial->getUseBoundingBox()); + ASSERT_FALSE(radial->setUseBoundingBox(false)); +} + +static const char *PATTERN = R"apl( +{ + "height": 10, + "width": 10, + "items": { + "type": "path", + "pathData": "M0,5 L5,0 L10,5 L5,10 z", + "fill": "blue" + } +} +)apl"; + +TEST_F(SGPaintTest, PatternPaint) { + auto config = RootConfig::create(); + auto metrics = Metrics().size(1024, 800).dpi(160).theme("dark"); + auto context = Context::createTestContext(metrics, *config); + + rapidjson::Document doc; + doc.Parse(PATTERN); + + auto graphic_pattern = GraphicPattern::create(*context, Object(doc)); + auto paint = sg::paint(graphic_pattern); + + ASSERT_TRUE(sg::PatternPaint::is_type(paint)); + auto pattern = sg::PatternPaint::cast(paint); + + ASSERT_EQ(pattern->toDebugString(), "PatternPaint size=10.000000x10.000000 opacity=1.000000"); + + ASSERT_TRUE(IsEqual(pattern->serialize(doc.GetAllocator()), StringToMapObject(R"apl( + { + "type": "patternPaint", + "opacity": 1.0, + "size": [10.0,10.0], + "content": [ + { + "type": "draw", + "path": { + "type": "generalPath", + "values": "MLLLZ", + "points": [0.0,5.0,5.0,0.0,10.0,5.0,5.0,10.0] + }, + "op": [ + { + "type": "fill", + "fillType": "even-odd", + "paint": { + "opacity": 1.0, + "type": "colorPaint", + "color": "#0000ffff" + } + } + ] + } + ] + } + )apl"))); + + ASSERT_FALSE(pattern->setSize(Size(10,10))); + ASSERT_TRUE(pattern->setSize(Size(20,20))); +} \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_path.cpp b/unit/scenegraph/unittest_sg_path.cpp index f7a26ad..fd55866 100644 --- a/unit/scenegraph/unittest_sg_path.cpp +++ b/unit/scenegraph/unittest_sg_path.cpp @@ -19,36 +19,95 @@ using namespace apl; -TEST(SGPathTest, Basic) +TEST(SGPathTest, Rectangle) { - // Rectangles - ASSERT_EQ(sg::path(Rect{0,20,100,100}), - sg::path(Rect{0,20,100,100})); - ASSERT_NE(sg::path(Rect{10,20,30,40}), - sg::path(Rect{10,20,30,60})); - - // Rounded Rectangles - ASSERT_EQ(sg::path(Rect{10,20,30,40}, 5), - sg::path(Rect{10,20,30,40}, 5)); - ASSERT_NE(sg::path(Rect{10,20,30,40}, 5), - sg::path(Rect{10,20,30,77}, 5)); - ASSERT_NE(sg::path(Rect{10,20,30,40}, 5), - sg::path(Rect{10,20,30,40}, 2)); + ASSERT_EQ(sg::path(Rect{0, 20, 100, 100})->toDebugString(), "RectPath Rect<100x100+0+20>"); + + ASSERT_EQ(sg::path(Rect{0, 20, 100, 100}), sg::path(Rect{0, 20, 100, 100})); + ASSERT_NE(sg::path(Rect{10, 20, 30, 40}), sg::path(Rect{10, 20, 30, 60})); + + ASSERT_FALSE(sg::path(Rect{10, 20, 10, 0})->empty()); + ASSERT_FALSE(sg::path(Rect{10, 20, 10, 10})->empty()); + + // Check other types for comparison + ASSERT_NE(sg::path(Rect{0,0,10,10}), sg::path(Rect{0,0,10,10}, 2)); +} + +TEST(SGPathTest, RoundedRect) +{ + ASSERT_EQ(sg::path(Rect{0, 20, 100, 100}, 5)->toDebugString(), + "RoundedRectPath Rect<100x100+0+20>:Radii<5.000000, 5.000000, 5.000000, 5.000000>"); + + ASSERT_EQ(sg::path(Rect{10, 20, 30, 40}, 5), sg::path(Rect{10, 20, 30, 40}, 5)); + ASSERT_NE(sg::path(Rect{10, 20, 30, 40}, 5), sg::path(Rect{10, 20, 30, 77}, 5)); + ASSERT_NE(sg::path(Rect{10, 20, 30, 40}, 5), sg::path(Rect{10, 20, 30, 40}, 2)); + + ASSERT_FALSE(sg::path(Rect{10, 20, 10, 0}, 5)->empty()); + ASSERT_FALSE(sg::path(Rect{10, 20, 10, 10}, 5)->empty()); // Rounded rectangles with complicated radii - ASSERT_EQ(sg::path(Rect{10,20,30,40}, Radii{1,2,3,4}), - sg::path(Rect{10,20,30,40}, Radii{1,2,3,4})); - ASSERT_NE(sg::path(Rect{10,20,30,40}, Radii{1,2,3,4}), - sg::path(Rect{10,20,30,77}, Radii{1,2,3,4})); - ASSERT_NE(sg::path(Rect{10,20,30,40}, Radii{1,2,3,4}), - sg::path(Rect{10,20,30,40}, Radii{1,2,3,7})); + ASSERT_EQ(sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4}), + sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4})); + ASSERT_NE(sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4}), + sg::path(Rect{10, 20, 30, 77}, Radii{1, 2, 3, 4})); + ASSERT_NE(sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4}), + sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 7})); // Other variations of constructors with RoundedRect - ASSERT_EQ(sg::path(Rect{10,20,30,40}, Radii{1,2,3,4}), - sg::path(RoundedRect(Rect{10,20,30,40}, Radii{1,2,3,4}))); - ASSERT_EQ(sg::path(Rect{10,20,30,40}, 5), - sg::path(RoundedRect(Rect{10,20,30,40}, Radii{5,5,5,5}))); + ASSERT_EQ(sg::path(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4}), + sg::path(RoundedRect(Rect{10, 20, 30, 40}, Radii{1, 2, 3, 4}))); + ASSERT_EQ(sg::path(Rect{10, 20, 30, 40}, 5), + sg::path(RoundedRect(Rect{10, 20, 30, 40}, Radii{5, 5, 5, 5}))); +} + +TEST(SGPathTest, GeneralPath) +{ + ASSERT_EQ(sg::path("h20 v20 h-20 z")->toDebugString(), + "GeneralPath MLLLZ [0.000000,0.000000,20.000000,0.000000,20.000000,20.000000,0.000000,20.000000]"); - // String variations ASSERT_EQ(sg::path("M5,5 h20"), sg::path("M 5 5 L25,5")); + ASSERT_NE(sg::path("M5,5 h20"), sg::path("M 5 5 L25,6")); + + ASSERT_TRUE(sg::path("M10,10 m20,20 z")->empty()); + ASSERT_FALSE(sg::path("L10,10")->empty()); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(sg::path("M5,5 L10,10")->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "generalPath", + "values": "ML", + "points": [5.0,5.0,10.0,10.0] + } + )apl"))); +} + +TEST(SGPathTest, FramePath) +{ + ASSERT_EQ(sg::path(RoundedRect(Rect{0,0,10,10}, 4), 2)->toDebugString(), + "FramePath Rect<10x10+0+0>:Radii<4.000000, 4.000000, 4.000000, 4.000000> inset=2.000000"); + + ASSERT_FALSE(sg::path(RoundedRect(Rect{0,0,10,10}, 4), 2)->empty()); + ASSERT_FALSE(sg::path(RoundedRect(Rect{0,0,0,10}, 4), 2)->empty()); + + // Frame variations + ASSERT_EQ(sg::path(RoundedRect(Rect{10,20,30,40}, 4), 10), + sg::path(RoundedRect(Rect{10,20,30,40}, 4), 10)); + ASSERT_NE(sg::path(RoundedRect(Rect{5,20,30,40}, 4), 10), + sg::path(RoundedRect(Rect{10,20,30,40}, 4), 10)); + ASSERT_NE(sg::path(RoundedRect(Rect{10,20,30,40}, 5), 10), + sg::path(RoundedRect(Rect{10,20,30,40}, 4), 10)); + ASSERT_NE(sg::path(RoundedRect(Rect{10,20,30,40}, 4), 12), + sg::path(RoundedRect(Rect{10,20,30,40}, 4), 10)); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(sg::path(RoundedRect(Rect{0,0,10,10}, 4), 2)->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "framePath", + "rect": [0.0,0.0,10.0,10.0], + "radii": [4.0,4.0,4.0,4.0], + "inset": 2.0 + } + )apl"))); } \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_pathbounds.cpp b/unit/scenegraph/unittest_sg_pathbounds.cpp new file mode 100644 index 0000000..f1d37cc --- /dev/null +++ b/unit/scenegraph/unittest_sg_pathbounds.cpp @@ -0,0 +1,101 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" +#include "apl/scenegraph/builder.h" +#include "apl/scenegraph/pathparser.h" + +using namespace apl; + +class SGPathBoundsTest : public DocumentWrapper {}; + +struct PathBoundsTest { + std::string source; + Rect bounds; +}; + +::testing::AssertionResult +CheckScaleExpansion(Point expected, Transform2D transform, float epsilon=1e-6) +{ + return IsEqual(expected, transform.scaleExpansion(), epsilon); +} + +const std::vector BOUNDS_TESTS = { + {"", {}}, // Null set + {"M20,20 10,10", {}}, // Still a null set + {"h20", {0, 0, 20, 0}}, // Simple horizontal line + {"h20 v20", {0, 0, 20, 20}}, // Simple vertical + {"M10,10 h20", {10, 10, 20, 0}}, // Offset horizontal line + {"m-10,0 h20 M0,-10 v20", {-10, -10, 20, 20}}, // Plus-sign + + // Quadratic paths + {"Q10,10 20,0", {0,0,20,5}}, // The bottom of the quadratic is halfway down + {"Q10,40 20,0", {0,0,20,20}}, + {"Q10,10 0,20", {0,0,5,20}}, + {"Q40,10 0,20", {0,0,20,20}}, + {"Q10,0 10,10", {0,0,10,10}}, + + // Cubic paths + {"C10,20 20,20 30,0", {0,0,30,15}}, // The bottom is 3/4 of the way up + {"C20,10 20,20 0,30", {0,0,15,30}}, + {"C10,20 20,-20 30,0", {0,-5.77350235,30,2*5.77350235}}, // Two roots + + // Path closure (no effect) + {"L10,0 v30 z", {0,0,10,30}}, +}; + +TEST_F(SGPathBoundsTest, GeneralPath) +{ + for (const auto& m : BOUNDS_TESTS) { + auto path = sg::parsePathString(m.source); + ASSERT_TRUE(IsEqual(path->boundingBox(Transform2D()), m.bounds)) << m.source; + } +} + +TEST_F(SGPathBoundsTest, OtherPaths) +{ + // All other path types are fundamentally rectangles + ASSERT_TRUE(IsEqual(sg::path(Rect{20,30,40,50})->boundingBox(Transform2D()), Rect{20,30,40,50})); + ASSERT_TRUE(IsEqual(sg::path(Rect{20,30,40,50}, 20)->boundingBox(Transform2D()), Rect{20,30,40,50})); + ASSERT_TRUE(IsEqual(sg::path(RoundedRect({20,30,40,50},4), 10)->boundingBox(Transform2D()), Rect{20,30,40,50})); +} + +TEST_F(SGPathBoundsTest, TransformScaleExpansion) +{ + ASSERT_TRUE(CheckScaleExpansion(Point(1,1), Transform2D())); + ASSERT_TRUE(CheckScaleExpansion(Point(1.414214, 1.414214), Transform2D::rotate(45))); + ASSERT_TRUE(CheckScaleExpansion(Point(2,2), Transform2D::scale(2))); + ASSERT_TRUE(CheckScaleExpansion(Point(2,0.5), Transform2D::scale(2,0.5))); + ASSERT_TRUE(CheckScaleExpansion(Point(1,1), Transform2D::translate(23.5, 17))); + + // Order matters. Rotating and then scaling is different from scaling and then rotating. + ASSERT_TRUE(CheckScaleExpansion(Point(2,0.5),Transform2D::scale(2,0.5) * Transform2D::rotate(90))); + ASSERT_TRUE(CheckScaleExpansion(Point(0.5,2),Transform2D::rotate(90)*Transform2D::scale(2,0.5))); +} + +TEST_F(SGPathBoundsTest, StrokePathMaxWidth) +{ + auto paint = sg::paint(Color(Color::BLACK)); + ASSERT_EQ(0, sg::fill(paint).get()->maxWidth()); + + auto op = sg::stroke(paint).strokeWidth(4.0).lineJoin(apl::kGraphicLineJoinRound).get(); + ASSERT_EQ(4.0, op->maxWidth()); + + op = sg::stroke(paint).strokeWidth(4.0).lineJoin(apl::kGraphicLineJoinMiter).miterLimit(6).get(); + ASSERT_EQ(24.0, op->maxWidth()); +} + diff --git a/unit/scenegraph/unittest_sg_pathop.cpp b/unit/scenegraph/unittest_sg_pathop.cpp new file mode 100644 index 0000000..27b314c --- /dev/null +++ b/unit/scenegraph/unittest_sg_pathop.cpp @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" +#include "apl/scenegraph/builder.h" + +#include + +using namespace apl; + +class SGPathOpTest : public ::testing::Test {}; + +static const std::regex STROKE("Stroke width=([0-9.]+) " + "miterLimit=([0-9.]+) " + "pathLen=([0-9.]+) " + "dashOffset=([0-9.]+) " + "lineCap=(\\w+) " + "lineJoin=(\\w+) " + "dashes=\\[(.*?)\\]"); + +static const std::regex DASHES("([0-9.]+)?(,([0-9.]+))*"); + + +TEST_F(SGPathOpTest, Fill) +{ + auto op = sg::fill(sg::paint(Color(Color::BLACK))); + + ASSERT_EQ(op->toDebugString(), "Fill"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(op->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "fill", + "fillType": "even-odd", + "paint": { + "type": "colorPaint", + "opacity": 1, + "color": "#000000ff" + } + } + )apl"))); +} + +TEST_F(SGPathOpTest, Stroke) +{ + auto op = sg::stroke(sg::paint(Color(Color::BLACK))).get(); + + ASSERT_EQ(op->toDebugString(), "Stroke width=1.000000 miterLimit=4.000000 " + "pathLen=0.000000 dashOffset=0.000000 " + "lineCap=butt lineJoin=miter dashes=[]"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(op->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "stroke", + "width": 1, + "miterLimit": 4, + "pathLength": 0, + "dashOffset": 0, + "lineCap": "butt", + "lineJoin": "miter", + "paint": { + "type": "colorPaint", + "opacity": 1, + "color": "#000000ff" + } + } + )apl"))); +} + + +TEST_F(SGPathOpTest, Fancy) +{ + auto op = sg::stroke(sg::paint(Color(Color::BLACK))) + .strokeWidth(10) + .miterLimit(8) + .dashOffset(2) + .dashes(ObjectArray{1,3}) + .lineCap(GraphicLineCap::kGraphicLineCapRound) + .lineJoin(GraphicLineJoin::kGraphicLineJoinBevel) + .pathLength(100) + .get(); + + ASSERT_EQ(op->toDebugString(), "Stroke width=10.000000 miterLimit=8.000000 " + "pathLen=100.000000 dashOffset=2.000000 " + "lineCap=round lineJoin=bevel dashes=[1.000000,3.000000]"); + + rapidjson::Document doc; + ASSERT_TRUE(IsEqual(op->serialize(doc.GetAllocator()), + StringToMapObject(R"apl( + { + "type": "stroke", + "width": 10, + "miterLimit": 8, + "pathLength": 100, + "dashOffset": 2, + "dashes": [ 1, 3], + "lineCap": "round", + "lineJoin": "bevel", + "paint": { + "type": "colorPaint", + "opacity": 1, + "color": "#000000ff" + } + } + )apl"))); +} \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_pathparser.cpp b/unit/scenegraph/unittest_sg_pathparser.cpp index 271e1c3..e010dd0 100644 --- a/unit/scenegraph/unittest_sg_pathparser.cpp +++ b/unit/scenegraph/unittest_sg_pathparser.cpp @@ -29,14 +29,65 @@ struct PathTextCase { }; static const std::vector PATH_TESTS = { - {"M10,10 L20,20", "ML", {10, 10, 20, 20}}, - {"M5,10 20,30", "", {}}, // Fails - no path generated - {"M5,10 m20 30", "", {}}, - {"m1 2 3 4 5 6", "", {}}, - {"M4,8 L10,12", "ML", {4, 8, 10, 12}}, - {"M4,8 l22 23", "ML", {4, 8, 26, 31}}, - {"M4,8 l22 23 -2 -2", "MLL", {4, 8, 26, 31, 24, 29}}, - {"H10 h20 v10 v20 30", "LLLLL", {10, 0, 30, 0, 30, 10, 30, 30, 30, 60}}, + {"M10,10 L20,20", "ML", {10, 10, 20, 20}}, + {"M5,10 20,30", "", {}}, // Fails - no path generated + {"M5,10 m20 30", "", {}}, + {"m1 2 3 4 5 6", "", {}}, + {"M4,8 L10,12", "ML", {4, 8, 10, 12}}, + {"M4,8 l22 23", "ML", {4, 8, 26, 31}}, + {"M4,8 l22 23 -2 -2", "MLL", {4, 8, 26, 31, 24, 29}}, + {"H10 h20 v10 v20 30", "MLLLLL", {0, 0, 10, 0, 30, 0, 30, 10, 30, 30, 30, 60}}, + {"V20 40", "MLL", {0, 0, 0, 20, 0, 40}}, + {"M10,10 C20,0 20,20 0,20", "MC", {10, 10, 20, 0, 20, 20, 0, 20}}, + {"M10,10 c10,-10 10,10 -10,10", "MC", {10, 10, 20, 0, 20, 20, 0, 20}}, + {"M0,100 S50,0 100,100 S150,200 200,100 250,0 300,100 350,200 400,100", "MCCCC", + {0, 100, // Move + 0, 100, 50, 0, 100, 100, // First control point is fixed to the move + 150, 200, 150, 200, 200, 100, // Carry over the control point + 250, 0, 250, 0, 300, 100, // Carry over the control point + 350, 200, 350, 200, 400, 100}}, // Carry over the control point + // Same as the last case, only relative smooth curve + {"M0,100 s50,-100 100,0 s50,100 100,0 50,-100 100,0 50,100 100,0", "MCCCC", + {0, 100, // Move + 0, 100, 50, 0, 100, 100, // First control point is fixed to the move + 150, 200, 150, 200, 200, 100, // Carry over the control point + 250, 0, 250, 0, 300, 100, // Carry over the control point + 350, 200, 350, 200, 400, 100}}, // Carry over the control point + // Quadratic Bezier curves; all four variations + {"M0,100 Q100,0 200,100 300,200 400,100", "MQQ", {0, 100, 100, 0, 200, 100, 300, 200, 400, 100}}, + {"M0,100 Q100,0 200,100 T400,100", "MQQ", {0, 100, 100, 0, 200, 100, 300, 200, 400, 100}}, + {"M0,100 q100,-100 200,0 100,100 200,0", "MQQ", {0, 100, 100, 0, 200, 100, 300, 200, 400, 100}}, + {"M0,100 q100,-100 200,0 t200,0", "MQQ", {0, 100, 100, 0, 200, 100, 300, 200, 400, 100}}, + {"M0,100 T200,0", "MQ", {0, 100, 0, 100, 200, 0}}, // Smooth curve without prior curve + {"M0,100 t200,0", "MQ", {0, 100, 0, 100, 200, 100}}, // Smooth curve without prior curve + // Elliptical arcs + {"M 300 200 A 100 100 0 0 1 500 200", "MCC", {300, 200, + 300, 144.771545, 344.771515, 100, 400, 100, + 455.228485, 100, 500, 144.771500, 500, 200}}, + {"M 300 200 a 100 100 0 0 1 200 0", "MCC", {300, 200, + 300, 144.771545, 344.771515, 100, 400, 100, + 455.228485, 100, 500, 144.771500, 500, 200}}, + {"A0,0 0 0 1 10,10", "ML", {0,0,10,10}}, // No radius -> straight line + {"A10,10 0 0 1 0,0", "", {}}, // No distance -> no arc + {"A10,10 0 0 1 10,10", "MC", {0,0,5.522847, 0, 10, 4.477152, 10, 10}}, // One quarter circle + {"A10,10 0 1 1 10,10", "MCCC", {0,0,0, -5.522846, 4.477152, -10, 10, -10, + 15.522847, -10, 20, -5.522851, 20, 0, + 20, 5.522845, 15.522850, 10, 10, 10}}, // The other three-quarters + {"A10,10 0 0 0 10,10", "MC", {0,0,0, 5.522847, 4.477152, 10, 10, 10}}, // Flip it + {"A10,10 0 1 0 10,10", "MCCC", {0,0,-5.522848, 0, -10, 4.477153, -10, 10, + -10, 15.522848, -5.522848, 20, 0, 20, + 5.522848, 20, 10, 15.522846, 10, 10}}, // Flipped three-quarters + {"M 300 200 a 10 10 0 0 1 200 0", "MCC", {300, 200, + 300, 144.771545, 344.771515, 100, 400, 100, + 455.228485, 100, 500, 144.771500, 500, 200}}, // Arc too small + {"A1000000, 10000000 0 0 1 1 1", "ML", {0, 0, 1, 1}}, // Flat angle; draw straight line + // Closure + {"M0,0 h10 v10 z", "MLLZ", { 0, 0, 10, 0, 10, 10 }}, + {"M0,0 h10 v10 Z", "MLLZ", { 0, 0, 10, 0, 10, 10 }}, + {"M0,0 h10 v10 Z Z Z", "MLLZ", { 0, 0, 10, 0, 10, 10 }}, + // Multiple moves followed by one line. The multiple moves collapse + {"M20,30 10,20 5,1 m20,20 3,3 M18,1 H5", "ML", { 18, 1, 5, 1}}, + {"L10,10 M20,20 30,30", "ML", {0,0,10,10}}, }; TEST_F(SGPathParserTest, Basic) @@ -48,6 +99,13 @@ TEST_F(SGPathParserTest, Basic) } } +TEST_F(SGPathParserTest, Error) +{ + auto path = sg::parsePathString("M10,10 L100,100 f13 L0,100"); + ASSERT_TRUE(path); // A Path object comes back...but it is empty + ASSERT_TRUE(path->empty()); +} + static const char * VECTOR = R"apl( { "type": "APL", @@ -102,7 +160,7 @@ TEST_F(SGPathParserTest, Path) .content( IsTransformNode() .transform(Transform2D::scale(2)) - .child(IsGenericNode().child( + .child( IsOpacityNode().child(IsTransformNode().child( IsClipNode() .path(IsGeneralPath( @@ -113,7 +171,7 @@ TEST_F(SGPathParserTest, Path) {40, 40, 360, 40, 360, 360, 40, 360})) .pathOp(IsFillOp(IsColorPaint(Color::RED))) .pathOp(IsStrokeOp(IsColorPaint(Color::BLUE), - 10))))))))))); + 10)))))))))); } static const char *PATTERN = R"apl( @@ -159,7 +217,8 @@ static const char *PATTERN = R"apl( } )apl"; -TEST_F(SGPathParserTest, Pattern) { +TEST_F(SGPathParserTest, Pattern) +{ loadDocument(PATTERN); ASSERT_TRUE(component); @@ -171,18 +230,17 @@ TEST_F(SGPathParserTest, Pattern) { IsLayer(Rect{0, 0, 800, 800}, "..VectorGraphic") .child( IsLayer(Rect{0, 0, 800, 800}) - .content(IsTransformNode() - .transform(Transform2D::scale(20)) - .child(IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath("MLLLZ", {0, 0, 40, 0, 40, 40, 0, 40})) - .pathOp(IsFillOp(IsPatternPaint( - {8, 8}, IsGenericNode().child( - IsDrawNode() - .path(IsGeneralPath( - "MLLLZ", {0, 4, 4, 0, 8, 4, 4, 8})) - .pathOp(IsFillOp(IsColorPaint( - Color::RED))))))))))))); + .content( + IsTransformNode() + .transform(Transform2D::scale(20)) + .child( + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 0, 40, 0, 40, 40, 0, 40})) + .pathOp(IsFillOp(IsPatternPaint( + {8, 8}, + IsDrawNode() + .path(IsGeneralPath("MLLLZ", {0, 4, 4, 0, 8, 4, 4, 8})) + .pathOp(IsFillOp(IsColorPaint(Color::RED))))))))))); } // TODO: Arc tests \ No newline at end of file diff --git a/unit/scenegraph/unittest_sg_text.cpp b/unit/scenegraph/unittest_sg_text.cpp index f0d27e9..8b53dd8 100644 --- a/unit/scenegraph/unittest_sg_text.cpp +++ b/unit/scenegraph/unittest_sg_text.cpp @@ -493,7 +493,6 @@ TEST_F(SGTextTest, Resize) { configChange(ConfigurationChange(200, 200)); root->clearPending(); sg = root->getSceneGraph(); - DumpSceneGraph(sg); ASSERT_TRUE( CheckSceneGraph(sg, IsLayer(Rect{0, 0, 200, 200}) .dirty(sg::Layer::kFlagSizeChanged | sg::Layer::kFlagRedrawContent) diff --git a/unit/scenegraph/unittest_sg_text_properties.cpp b/unit/scenegraph/unittest_sg_text_properties.cpp new file mode 100644 index 0000000..25b8d2c --- /dev/null +++ b/unit/scenegraph/unittest_sg_text_properties.cpp @@ -0,0 +1,107 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + * + */ + +#include "../testeventloop.h" +#include "test_sg.h" + +#include "apl/scenegraph/textpropertiescache.h" + +using namespace apl; + +class SGTextPropertiesTest : public ::testing::Test { +}; + +TEST_F(SGTextPropertiesTest, Basic) +{ + sg::TextPropertiesCache cache; + + auto tp = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900); + + rapidjson::Document doc; + auto value = tp->serialize(doc.GetAllocator()); + + ASSERT_TRUE(IsEqual(value, StringToMapObject(R"apl( + { + "fontFamily": ["Arial", "Helvetica"], + "fontSize": 22, + "fontStyle": "normal", + "fontWeight": 900, + "letterSpacing": 0, + "lineHeight": 1.25, + "maxLines": 0, + "textAlign": "auto", + "textAlignVertical": "auto" + } + )apl"))); + + ASSERT_EQ(1, cache.size()); +} + +TEST_F(SGTextPropertiesTest, Duplicate) +{ + sg::TextPropertiesCache cache; + + auto tp = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900); + + ASSERT_EQ(1, cache.size()); + + auto tp2 = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900, + 0, // Letter spacing + 1.25f // Line height + ); + + ASSERT_EQ(1, cache.size()); + ASSERT_EQ(*tp, *tp2); + ASSERT_EQ(tp.get(), tp2.get()); +} + +TEST_F(SGTextPropertiesTest, NotDuplicate) +{ + sg::TextPropertiesCache cache; + + auto tp = sg::TextProperties::create(cache, + {"Arial"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900); + + ASSERT_EQ(1, cache.size()); + + auto tp2 = sg::TextProperties::create(cache, + {"Arial", "Helvetica"}, + 22.0f, + FontStyle::kFontStyleNormal, + 900, + 0, // Letter spacing + 1.25f // Line height + ); + + ASSERT_EQ(2, cache.size()); + ASSERT_NE(*tp, *tp2); + ASSERT_NE(tp.get(), tp2.get()); +} \ No newline at end of file diff --git a/unit/testeventloop.h b/unit/testeventloop.h index 48dac07..b0ce896 100644 --- a/unit/testeventloop.h +++ b/unit/testeventloop.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "rapidjson/error/en.h" #include "rapidjson/pointer.h" @@ -485,7 +486,7 @@ class DocumentWrapper : public ActionWrapper { context = root->contextPtr(); ASSERT_TRUE(context); ASSERT_FALSE(context->getReinflationFlag()); - component = std::dynamic_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); } postInflate(); @@ -509,7 +510,7 @@ class DocumentWrapper : public ActionWrapper { context = root->contextPtr(); ASSERT_TRUE(context); ASSERT_TRUE(context->getReinflationFlag()); - component = std::dynamic_pointer_cast(root->topComponent()); + component = CoreComponent::cast(root->topComponent()); if (event.getActionRef().isPending()) { event.getActionRef().resolve(); @@ -625,7 +626,7 @@ class TestEventCommand : public CoreCommand { // Calculate the component map auto componentsMap = std::make_shared(); for (auto& compId : mValues.at(kCommandPropertyComponents).getArray()) { - auto comp = std::dynamic_pointer_cast(mContext->findComponentById(compId.getString())); + auto comp = CoreComponent::cast(mContext->findComponentById(compId.getString())); if (comp) { componentsMap->emplace(compId.getString(), comp->getValue()); } @@ -881,7 +882,7 @@ ::testing::AssertionResult CheckState(const ComponentPtr& component, Args... arg for (auto key : v) values[key] = true; - auto& core = std::static_pointer_cast(component)->getState(); + auto& core = CoreComponent::cast(component)->getState(); for (int i = 0 ; i < kStatePropertyCount ; i++) { auto key = static_cast(i); if (core.get(key) != values[i]) @@ -924,10 +925,10 @@ ::testing::AssertionResult TransformComponent(const RootContextPtr& root, const } inline -::testing::AssertionResult IsEqual(const Transform2D& lhs, const Transform2D& rhs) +::testing::AssertionResult IsEqual(const Transform2D& lhs, const Transform2D& rhs, float epsilon=0.0001) { for (int i = 0 ; i < 6 ; i++) - if (std::abs(lhs.get()[i] - rhs.get()[i]) > 0.0001) { + if (std::abs(lhs.get()[i] - rhs.get()[i]) > epsilon) { streamer s; s << "[" << lhs << "] != [" << rhs << "]"; return ::testing::AssertionFailure() << s.str(); @@ -936,8 +937,49 @@ ::testing::AssertionResult IsEqual(const Transform2D& lhs, const Transform2D& rh return ::testing::AssertionSuccess(); } -template -::testing::AssertionResult IsEqual(const std::vector& a, const std::vector& b) { +inline +::testing::AssertionResult IsEqual(const Point& lhs, const Point& rhs, float epsilon=0.0001) +{ + if (std::abs(lhs.getX() - rhs.getX()) > epsilon || + std::abs(lhs.getY() - rhs.getY()) > epsilon) + return ::testing::AssertionFailure() << lhs.toDebugString() << " != " << rhs.toDebugString(); + + return ::testing::AssertionSuccess(); +} + +inline +::testing::AssertionResult IsEqual(const Rect& lhs, const Rect& rhs, float epsilon=0.0001) +{ + if (std::abs(lhs.getX() - rhs.getX()) > epsilon || + std::abs(lhs.getY() - rhs.getY()) > epsilon || + std::abs(lhs.getWidth() - rhs.getWidth()) > epsilon || + std::abs(lhs.getHeight() - rhs.getHeight()) > epsilon) + return ::testing::AssertionFailure() << lhs.toDebugString() << " != " << rhs.toDebugString(); + + return ::testing::AssertionSuccess(); +} + +// This template compares two vectors of floating point numbers (doubles, floats, etc) +template::value, bool>::type = true> +::testing::AssertionResult IsEqual(const std::vector& a, const std::vector& b, float epsilon=1e-6) +{ + if (a.size() != b.size()) + return ::testing::AssertionFailure() << "Size mismatch a=" << a.size() << " b=" << b.size(); + + for (int i = 0; i < a.size(); i++) + if (std::abs(a.at(i)-b.at(i)) > epsilon) + return ::testing::AssertionFailure() + << "Element mismatch index=" << i << " a=" << a.at(i) << " b=" << b.at(i); + + return ::testing::AssertionSuccess(); +} + +// This template compares two vectors of items that support the '!=' operator +template::value, bool>::type = true> +::testing::AssertionResult IsEqual(const std::vector& a, const std::vector& b) +{ if (a.size() != b.size()) return ::testing::AssertionFailure() << "Size mismatch a=" << a.size() << " b=" << b.size(); @@ -1077,7 +1119,7 @@ ::testing::AssertionResult CheckDirtyVisualContext(const RootContextPtr& root, A std::vector v = {args...}; for (const auto& target : v) - if (!std::dynamic_pointer_cast(target)->isVisualContextDirty()) + if (!CoreComponent::cast(target)->isVisualContextDirty()) return ::testing::AssertionFailure() << "invalid visual context isDirty" ; return ::testing::AssertionSuccess(); @@ -1154,8 +1196,8 @@ MouseDown(const RootContextPtr& root, const CoreComponentPtr& comp, double x, do root->handlePointerEvent(PointerEvent(kPointerDown, point)); auto visitor = TouchableAtPosition(point); - std::dynamic_pointer_cast(root->topComponent())->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + CoreComponent::cast(root->topComponent())->raccept(visitor); + auto target = CoreComponent::cast(visitor.getResult()); if (target != comp) return ::testing::AssertionFailure() << "Down failed to hit target at " << x << "," << y; @@ -1170,8 +1212,8 @@ MouseUp(const RootContextPtr& root, const CoreComponentPtr& comp, double x, doub root->handlePointerEvent(PointerEvent(kPointerUp, point)); auto visitor = TouchableAtPosition(point); - std::dynamic_pointer_cast(root->topComponent())->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + CoreComponent::cast(root->topComponent())->raccept(visitor); + auto target = CoreComponent::cast(visitor.getResult()); if (target != comp) return ::testing::AssertionFailure() << "Up failed to hit target at " << x << "," << y; @@ -1186,8 +1228,8 @@ MouseDown(const RootContextPtr& root, double x, double y) { root->handlePointerEvent(PointerEvent(kPointerDown, point)); auto visitor = TouchableAtPosition(point); - std::dynamic_pointer_cast(root->topComponent())->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + CoreComponent::cast(root->topComponent())->raccept(visitor); + auto target = CoreComponent::cast(visitor.getResult()); if (!target) return ::testing::AssertionFailure() << "Down failed to hit target at " << x << "," << y; @@ -1202,8 +1244,8 @@ MouseUp(const RootContextPtr& root, double x, double y) { root->handlePointerEvent(PointerEvent(kPointerUp, point)); auto visitor = TouchableAtPosition(point); - std::dynamic_pointer_cast(root->topComponent())->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + CoreComponent::cast(root->topComponent())->raccept(visitor); + auto target = CoreComponent::cast(visitor.getResult()); if (!target) return ::testing::AssertionFailure() << "Up failed to hit target at " << x << "," << y; @@ -1218,8 +1260,8 @@ MouseMove(const RootContextPtr& root, double x, double y) { root->handlePointerEvent(PointerEvent(kPointerMove, point)); auto visitor = TouchableAtPosition(point); - std::dynamic_pointer_cast(root->topComponent())->raccept(visitor); - auto target = std::dynamic_pointer_cast(visitor.getResult()); + CoreComponent::cast(root->topComponent())->raccept(visitor); + auto target = CoreComponent::cast(visitor.getResult()); if (!target) return ::testing::AssertionFailure() << "Up failed to hit target at " << x << "," << y; @@ -1289,19 +1331,19 @@ compareTransformApprox(const Transform2D& left, const Transform2D& right, float inline ::testing::AssertionResult CheckTransform(const Transform2D& expected, const ComponentPtr& component) { - return compareTransformApprox(expected, component->getCalculated(kPropertyTransform).getTransform2D()); + return compareTransformApprox(expected, component->getCalculated(kPropertyTransform).get()); } inline ::testing::AssertionResult CheckTransformApprox(const Transform2D& expected, const ComponentPtr& component, float delta) { - return compareTransformApprox(expected, component->getCalculated(kPropertyTransform).getTransform2D(), delta); + return compareTransformApprox(expected, component->getCalculated(kPropertyTransform).get(), delta); } inline ::testing::AssertionResult expectBounds(ComponentPtr comp, float top, float left, float bottom, float right) { - auto bounds = comp->getCalculated(kPropertyBounds).getRect(); + auto bounds = comp->getCalculated(kPropertyBounds).get(); if (bounds.getTop() != top) return ::testing::AssertionFailure() << "bounds.getTop() does not equal top: " << bounds.getTop() << " != " << top; @@ -1320,7 +1362,7 @@ expectBounds(ComponentPtr comp, float top, float left, float bottom, float right inline ::testing::AssertionResult expectInnerBounds(ComponentPtr comp, float top, float left, float bottom, float right) { - auto bounds = comp->getCalculated(kPropertyInnerBounds).getRect(); + auto bounds = comp->getCalculated(kPropertyInnerBounds).get(); if (bounds.getTop() != top) return ::testing::AssertionFailure() << "bounds.getTop() does not equal top: " << bounds.getTop() << " != " << top; diff --git a/unit/time/unittest_sequencer.cpp b/unit/time/unittest_sequencer.cpp index c4fc512..4561dcb 100644 --- a/unit/time/unittest_sequencer.cpp +++ b/unit/time/unittest_sequencer.cpp @@ -979,7 +979,7 @@ TEST_F(SequencerTest, AnimateInParalell) ASSERT_TRUE(CheckDirty(component, kPropertyTransform)); - ASSERT_EQ(Transform2D::translateX(512), component->getCalculated(kPropertyTransform).getTransform2D()); + ASSERT_EQ(Transform2D::translateX(512), component->getCalculated(kPropertyTransform).get()); execute(SET_OPACITY, false); @@ -987,7 +987,7 @@ TEST_F(SequencerTest, AnimateInParalell) ASSERT_TRUE(CheckDirty(component, kPropertyOpacity, kPropertyTransform, kPropertyVisualHash)); - ASSERT_EQ(Transform2D::translateX(0), component->getCalculated(kPropertyTransform).getTransform2D()); + ASSERT_EQ(Transform2D::translateX(0), component->getCalculated(kPropertyTransform).get()); ASSERT_EQ(0.75, component->getCalculated(kPropertyOpacity).asNumber()); } diff --git a/unit/touch/unittest_gestures.cpp b/unit/touch/unittest_gestures.cpp index bdaa5e1..497dae7 100644 --- a/unit/touch/unittest_gestures.cpp +++ b/unit/touch/unittest_gestures.cpp @@ -131,7 +131,7 @@ TEST_F(GesturesTest, DoublePress) { loadDocument(DOUBLE_PRESS); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -162,7 +162,7 @@ TEST_F(GesturesTest, DoublePressThree) { loadDocument(DOUBLE_PRESS); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -192,7 +192,7 @@ TEST_F(GesturesTest, DoublePressTooLong) { loadDocument(DOUBLE_PRESS); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -300,7 +300,7 @@ TEST_F(GesturesTest, DoublePressDefinedTwice) { loadDocument(DOUBLE_PRESS_TWICE); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -555,7 +555,7 @@ TEST_F(GesturesTest, LongPress) { loadDocument(LONG_PRESS); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -688,10 +688,10 @@ TEST_F(GesturesTest, SwipeAwayUnfinished) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up before fulfilled. ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); @@ -739,10 +739,10 @@ TEST_F(GesturesTest, SwipeAwayUnfinishedMiddle) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up before fulfilled. ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(150,100), "onDown")); @@ -790,10 +790,10 @@ TEST_F(GesturesTest, SwipeAwayCancelled) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); ASSERT_TRUE(HandleAndCheckConsumedPointerEvent(PointerEventType::kPointerMove, Point(140,100), "onMove")); @@ -826,10 +826,10 @@ TEST_F(GesturesTest, SwipeAwayWrongDirection) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerMove, Point(200,110), "onMove")); @@ -844,7 +844,7 @@ TEST_F(GesturesTest, SwipeAwayLeftReveal) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -886,7 +886,7 @@ TEST_F(GesturesTest, SwipeAwayLeftReveal) ASSERT_TRUE(CheckEvent("onSwipeDone", "left")); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayLeftRevealTapOrScrollTimeout) @@ -920,10 +920,10 @@ TEST_F(GesturesTest, SwipeAwayLeftCover) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "cover", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); @@ -963,16 +963,16 @@ TEST_F(GesturesTest, SwipeAwayLeftCover) ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } void GesturesTest::swipeAwayLeftSlide() { - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); @@ -1017,7 +1017,7 @@ GesturesTest::swipeAwayLeftSlide() ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayLeftSlide) @@ -1036,10 +1036,10 @@ TEST_F(GesturesTest, SwipeAwayLeftRightLeftSlide) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); advanceTime(800); @@ -1085,17 +1085,17 @@ TEST_F(GesturesTest, SwipeAwayLeftRightLeftSlide) ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayLeftRightLeftSlideUnfinished) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); advanceTime(550); @@ -1148,10 +1148,10 @@ TEST_F(GesturesTest, SwipeAwayFlickLeftSlide) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); // Advance time to something in flick range @@ -1187,17 +1187,17 @@ TEST_F(GesturesTest, SwipeAwayFlickLeftSlide) ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayUnfinishedFlickLeftSlide) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); // Advance time to something not in flick range @@ -1241,10 +1241,10 @@ TEST_F(GesturesTest, SwipeAwayFlickTooFast) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); // This will actually give us 20000 dp/s, which would end up in 2 ms without a limit. @@ -1269,10 +1269,10 @@ TEST_F(GesturesTest, SwipeAwayLeftSlideNotEnoughDistance) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up before fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); @@ -1286,11 +1286,11 @@ TEST_F(GesturesTest, SwipeAwayLeftSlideNotEnoughDistance) void GesturesTest::swipeAwayRightSlide() { - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(100,100), "onDown")); @@ -1337,7 +1337,7 @@ GesturesTest::swipeAwayRightSlide() { ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayRightSlide) @@ -1356,10 +1356,10 @@ TEST_F(GesturesTest, SwipeAwayUpSlide) { loadDocument(SWIPE_AWAY, R"({ "direction": "up", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(100,200), "onDown")); @@ -1405,17 +1405,17 @@ TEST_F(GesturesTest, SwipeAwayUpSlide) ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateY(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayDownSlide) { loadDocument(SWIPE_AWAY, R"({ "direction": "down", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(100,100), "onDown")); @@ -1461,17 +1461,17 @@ TEST_F(GesturesTest, SwipeAwayDownSlide) ASSERT_TRUE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateY(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayOverSwipe) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "cover", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); @@ -1512,7 +1512,7 @@ TEST_F(GesturesTest, SwipeAwayLeftPointerMovementTooVertical) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1531,7 +1531,7 @@ TEST_F(GesturesTest, SwipeAwayRightPointerMovementTooVertical) { loadDocument(SWIPE_AWAY, R"({ "direction": "right", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1550,7 +1550,7 @@ TEST_F(GesturesTest, SwipeAwayUpPointerMovementTooHorizontal) { loadDocument(SWIPE_AWAY, R"({ "direction": "up", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1569,7 +1569,7 @@ TEST_F(GesturesTest, SwipeAwayDownPointerMovementTooHorizontal) { loadDocument(SWIPE_AWAY, R"({ "direction": "down", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1589,7 +1589,7 @@ TEST_F(GesturesTest, SwipeAwayMaxDurationEnforced) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 400, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1631,7 +1631,7 @@ TEST_F(GesturesTest, SwipeAwayMaxDurationEnforced) ASSERT_TRUE(CheckEvent("onSwipeDone", "left")); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayContext) @@ -1664,7 +1664,7 @@ TEST_F(GesturesTest, SwipeAwayContext) ASSERT_FALSE(child.HasMember("tags")); ////////////////// - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -1733,19 +1733,19 @@ TEST_F(GesturesTest, SwipeAwayContext) ////////////////// ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayLeftDisabled) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "slide", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); tw->setState(StateProperty::kStateDisabled, true); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); - ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(Rect(0, 0, 100, 100), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); // Up after fulfilled ASSERT_TRUE(HandlePointerEvent(root, PointerEventType::kPointerDown, Point(200,100), false)); @@ -1781,7 +1781,7 @@ TEST_F(GesturesTest, SwipeAwayLeftDisabled) ASSERT_FALSE(CheckDirty(tw->getChildAt(0), kPropertyTransform)); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } static const char *TOUCH_ALL = R"( @@ -1922,7 +1922,7 @@ TEST_F(GesturesTest, GestureCombo) { loadDocument(TOUCH_ALL); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -2027,7 +2027,7 @@ TEST_F(GesturesTest, SwipeAwayMiddle) { loadDocument(TOUCH_ALL); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Lorem ipsum dolor", text->getCalculated(kPropertyText).asString()); @@ -2309,7 +2309,7 @@ TEST_F(GesturesTest, DoublePressDisabledAVG) { loadDocument(ALL_AVG); - auto myGraphic = std::dynamic_pointer_cast(component->findComponentById("MyGraphic")); + auto myGraphic = CoreComponent::cast(component->findComponentById("MyGraphic")); myGraphic->setState(StateProperty::kStateDisabled, true); ASSERT_EQ(kComponentTypeVectorGraphic, component->getType()); @@ -2368,7 +2368,7 @@ TEST_F(GesturesTest, LongPressDisabledAVG) { loadDocument(ALL_AVG); - auto myGraphic = std::dynamic_pointer_cast(component->findComponentById("MyGraphic")); + auto myGraphic = CoreComponent::cast(component->findComponentById("MyGraphic")); myGraphic->setState(StateProperty::kStateDisabled, true); ASSERT_EQ(kComponentTypeVectorGraphic, component->getType()); @@ -2980,7 +2980,7 @@ TEST_F(GesturesTest, LongPressSingularTransformAfterStart) { TEST_F(GesturesTest, SwipeAwayScaled) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -3026,13 +3026,13 @@ TEST_F(GesturesTest, SwipeAwayScaled) ASSERT_TRUE(CheckEvent("onSwipeDone", "left")); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayRotated) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_EQ(1, tw->getChildCount()); ASSERT_EQ("texty", tw->getChildAt(0)->getId()); @@ -3078,12 +3078,12 @@ TEST_F(GesturesTest, SwipeAwayRotated) ASSERT_TRUE(CheckEvent("onSwipeDone", "left")); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwayTransformedDuringSwipe) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); advanceTime(100); @@ -3121,12 +3121,12 @@ TEST_F(GesturesTest, SwipeAwayTransformedDuringSwipe) { ASSERT_TRUE(CheckEvent("onSwipeDone", "left")); ASSERT_TRUE(CheckTransform(Transform2D::translateX(0), tw->getChildAt(0))); - ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).getRect(), tw->getChildAt(0)->getCalculated(kPropertyBounds).getRect()); + ASSERT_EQ(tw->getCalculated(kPropertyInnerBounds).get(), tw->getChildAt(0)->getCalculated(kPropertyBounds).get()); } TEST_F(GesturesTest, SwipeAwaySingularTransformDuringSwipe) { loadDocument(SWIPE_AWAY, R"({ "direction": "left", "mode": "reveal", "w": 100, "h": 100 })"); - auto tw = std::dynamic_pointer_cast(component->findComponentById("tw")); + auto tw = TouchWrapperComponent::cast(component->findComponentById("tw")); ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(200,100), "onDown")); advanceTime(100); @@ -3237,8 +3237,8 @@ static const char *SWIPE_RTL = R"( TEST_F(GesturesTest, SwipeAwayRTL) { loadDocument(SWIPE_RTL); - auto f1 = std::dynamic_pointer_cast(component->findComponentById("forwardSwipe")); - auto f2 = std::dynamic_pointer_cast(component->findComponentById("backwardSwipe")); + auto f1 = CoreComponent::cast(component->findComponentById("forwardSwipe")); + auto f2 = CoreComponent::cast(component->findComponentById("backwardSwipe")); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(20,40))); @@ -3272,8 +3272,8 @@ TEST_F(GesturesTest, SwipeAwayRTL) { TEST_F(GesturesTest, SwipeAwayWrongDirectionRTL) { loadDocument(SWIPE_RTL); - auto f1 = std::dynamic_pointer_cast(component->findComponentById("forwardSwipe")); - auto f2 = std::dynamic_pointer_cast(component->findComponentById("backwardSwipe")); + auto f1 = CoreComponent::cast(component->findComponentById("forwardSwipe")); + auto f2 = CoreComponent::cast(component->findComponentById("backwardSwipe")); // Up after fulfilled ASSERT_TRUE(HandleAndCheckPointerEvent(PointerEventType::kPointerDown, Point(20,40))); @@ -3368,7 +3368,7 @@ TEST_F(GesturesTest, Tap) { loadDocument(TAP); - auto tw = std::dynamic_pointer_cast(component); + auto tw = TouchWrapperComponent::cast(component); auto text = tw->getChildAt(0); ASSERT_EQ(kComponentTypeText, text->getType()); ASSERT_EQ("Not tapped", text->getCalculated(kPropertyText).asString()); diff --git a/unit/touch/unittest_pointer.cpp b/unit/touch/unittest_pointer.cpp index 527b5a2..6b6ba63 100644 --- a/unit/touch/unittest_pointer.cpp +++ b/unit/touch/unittest_pointer.cpp @@ -210,7 +210,7 @@ TEST_F(PointerTest, Overlapping) { ASSERT_TRUE(TransformComponent(root, "HidingContainer", "translateX", 200)); auto hiding = root->topComponent()->findComponentById("HidingContainer"); - auto transform = hiding->getCalculated(kPropertyTransform).getTransform2D(); + auto transform = hiding->getCalculated(kPropertyTransform).get(); ASSERT_EQ(Point(200,0), transform * Point(0,0)); // Poking the same point should hit the TouchWrapper @@ -255,7 +255,7 @@ static const char *SCROLLING_CONTAINER = TEST_F(PointerTest, ScrollView) { loadDocument(SCROLLING_CONTAINER); - auto touch = std::static_pointer_cast(component->findComponentById("MyTouch")); + auto touch = CoreComponent::cast(component->findComponentById("MyTouch")); // Verify you can hit the target at the starting location ASSERT_TRUE(MouseClick(root, touch, 25, 25)); diff --git a/unit/utils/unittest_stringfunctions.cpp b/unit/utils/unittest_stringfunctions.cpp index 07d41d7..df08ac8 100644 --- a/unit/utils/unittest_stringfunctions.cpp +++ b/unit/utils/unittest_stringfunctions.cpp @@ -191,6 +191,49 @@ TEST(StringFunctionsTest, ParseLongDoubleLiteral) } } +struct ParseSimpleTestCase { + std::string input; + long long expectedValue; + std::size_t expectedPosition; +}; + +static const std::vector PARSE_SIMPLE_TEST_CASES = { + // Finite decimal values + {"4", 4, 1}, + {"-4", -4, 2}, + {"4.0", 4, 1}, + {"4.", 4, 1}, + {"14.5", 14, 2}, + {"14.5", 14, 2}, + {"9999999999999999999999999", 0, 18446744073709551615u}, + + {"robert", 0, 18446744073709551615u}, +}; + +TEST(StringFunctionsTest, ParseIntegerLiteral) +{ + double max_delta = 1e-6; + for (const auto &testCase : PARSE_SIMPLE_TEST_CASES) { + std::size_t pos = std::numeric_limits::max(); + int parsed = sutil::stoi(testCase.input, &pos); + + ASSERT_NEAR(testCase.expectedValue, parsed, max_delta) << "Input: '" << testCase.input << "'"; + ASSERT_EQ(testCase.expectedPosition, pos) << "Input: '" << testCase.input << "'"; + } +} + +TEST(StringFunctionsTest, ParseLongLongLiteral) +{ + double max_delta = 1e-6; + for (const auto &testCase : PARSE_SIMPLE_TEST_CASES) { + std::size_t pos = std::numeric_limits::max(); + long long parsed = sutil::stoll(testCase.input, &pos); + + ASSERT_NEAR(testCase.expectedValue, parsed, max_delta) << "Input: '" << testCase.input << "'"; + ASSERT_EQ(testCase.expectedPosition, pos) << "Input: '" << testCase.input << "'"; + } +} + TEST(StringFunctionsTest, FormatFloat) { ASSERT_EQ("4.000000", sutil::to_string(4.0f));