diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f67778..3b4c4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## [1.7.0] + +This release adds support for version 1.7 of the APL specification. + +## Added + +- Added new position sticky for children containers +- Support specifying layout direction for components as well as the entire document +- Support for document level language +- Extended swipe away gesture to support forward and backward directions +- Added support for \ text markup +- Added start*, end* and padding variants to override left* and right* variants depending on layoutDirection +- Data-binding definitions now support Array and Maps +- Alpha feature: Improved extension message builder API +- Alpha feature: Extension client now supports modification of non-defined LiveMap members +- Alpha feature: APL Audio Player Extension. The Audio Player Extension extends APL capabilities providing a custom audio player extension + +## Changed + +- Data grammar now translates to byte code +- Performance improvements during the inflation process +- Component properties shadowColor, shadowHorizonalOffset, shadowRadius and shadowVerticalOffset are now also styled and dynamic +- Pager navigation and pageDirection properties are now also dynamic +- Updated reported APL version to 1.7 +- Other bug fixes +- Build improvements + ## [1.6.2] ### Changed @@ -26,7 +53,7 @@ This release adds support for version 1.6 of the APL specification. - Reinflation support for viewport size and orientation changes - Add handlePageMove property to Pager to allow custom transitions - Add pageDirection to support vertical pagers -- Added support for text markup +- Added support for \ text markup - New Math functions: Math.int and Math.float - New API to expose visible children of a component - Support bound values in layouts diff --git a/CMakeLists.txt b/CMakeLists.txt index d521f59..f1c2f86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# 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. diff --git a/Dockerfile b/Dockerfile index 0872a29..d479757 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# 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. diff --git a/README.md b/README.md index be99fc7..551586d 100644 --- a/README.md +++ b/README.md @@ -228,10 +228,10 @@ For more info on installing and configuring Docker, see: https://www.docker.com/get-started # Global configuration -## Telemetry -In order to compile core telemetry support use TELEMETRY cmake parameter: +## Tracing +In order to compile core tracing support use TRACING cmake parameter: ``` -$ cmake -DTELEMETRY=ON +$ cmake -DTRACING=ON ``` ## Memory debugging diff --git a/apl-dev-env.sh b/apl-dev-env.sh index f20950a..7dee238 100644 --- a/apl-dev-env.sh +++ b/apl-dev-env.sh @@ -1,4 +1,4 @@ -# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# 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. @@ -244,10 +244,12 @@ function apl-config-android { # Run cmake for Android mkdir -p android-build cd android-build echo "Running $CMAKE" - $CMAKE -DANDROID_ABI="x86" \ + $CMAKE -DCMAKE_BUILD_TYPE=Release \ + -DANDROID_ABI="armeabi-v7a" \ -DANDROID_PLATFORM=android-24 \ -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_FILE \ - -DAPL_JNI=ON .. + -DAPL_JNI=ON \ + -DBUILD_TESTS=ON .. ) } @@ -290,7 +292,7 @@ function apl-config-wasm { # Run cmake in the wasm build cd $APL_DIR mkdir -p wasm-build cd wasm-build - emcmake cmake -DEMSCRIPTEN=ON -DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=Debug -DEMSCRIPTEN_SOURCEMAPS=ON -DBUILD_TESTS=OFF .. + emcmake cmake -DEMSCRIPTEN=ON -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release -DEMSCRIPTEN_SOURCEMAPS=ON .. ) } diff --git a/aplcore/CMakeLists.txt b/aplcore/CMakeLists.txt index 4eaeb8d..e2a2c7c 100644 --- a/aplcore/CMakeLists.txt +++ b/aplcore/CMakeLists.txt @@ -71,6 +71,7 @@ add_subdirectory(src/extension) add_subdirectory(src/focus) add_subdirectory(src/graphic) add_subdirectory(src/livedata) +add_subdirectory(src/media) add_subdirectory(src/primitives) add_subdirectory(src/scaling) add_subdirectory(src/time) diff --git a/aplcore/include/apl/action/arrayaction.h b/aplcore/include/apl/action/arrayaction.h index 7890ef5..4dc9e2f 100644 --- a/aplcore/include/apl/action/arrayaction.h +++ b/aplcore/include/apl/action/arrayaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/action/documentaction.h b/aplcore/include/apl/action/documentaction.h index e00a36a..d2aa4c6 100644 --- a/aplcore/include/apl/action/documentaction.h +++ b/aplcore/include/apl/action/documentaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/action/extensioneventaction.h b/aplcore/include/apl/action/extensioneventaction.h index c6a9a65..9977c7d 100644 --- a/aplcore/include/apl/action/extensioneventaction.h +++ b/aplcore/include/apl/action/extensioneventaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/action/openurlaction.h b/aplcore/include/apl/action/openurlaction.h index 2192fc9..14ee31a 100644 --- a/aplcore/include/apl/action/openurlaction.h +++ b/aplcore/include/apl/action/openurlaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/action/scrolltoaction.h b/aplcore/include/apl/action/scrolltoaction.h index cbc53bf..f0042f3 100644 --- a/aplcore/include/apl/action/scrolltoaction.h +++ b/aplcore/include/apl/action/scrolltoaction.h @@ -44,7 +44,7 @@ class ScrollToAction : public AnimatedScrollAction { */ static std::shared_ptr make(const TimersPtr& timers, const std::shared_ptr& command, - const ComponentPtr& target = nullptr); + const CoreComponentPtr& target = nullptr); /** * Called from SpeakItem during line highlight mode. * @param timers Timer reference. @@ -56,7 +56,7 @@ class ScrollToAction : public AnimatedScrollAction { static std::shared_ptr make(const TimersPtr& timers, const std::shared_ptr& command, const Rect& subBounds, - const ComponentPtr& target = nullptr); + const CoreComponentPtr& target = nullptr); /** @@ -72,7 +72,7 @@ class ScrollToAction : public AnimatedScrollAction { const CommandScrollAlign& align, const Rect& subBounds, const ContextPtr& context, - const ComponentPtr& target = nullptr); + const CoreComponentPtr& target = nullptr); /** * Called in order to bring child into view, utilizing snap setting if exists. @@ -82,7 +82,7 @@ class ScrollToAction : public AnimatedScrollAction { * @return */ static std::shared_ptr makeUsingSnap(const TimersPtr& timers, - const ComponentPtr& target, + const CoreComponentPtr& target, apl_duration_t duration); ScrollToAction(const TimersPtr& timers, @@ -90,7 +90,7 @@ class ScrollToAction : public AnimatedScrollAction { const Rect& subBounds, const ContextPtr& context, bool scrollToSubBounds, - const ComponentPtr& target, + const CoreComponentPtr& target, const CoreComponentPtr& scrollableParent, apl_duration_t duration); @@ -100,7 +100,7 @@ class ScrollToAction : public AnimatedScrollAction { const Rect& subBounds, const ContextPtr& context, bool scrollToSubBounds, - const ComponentPtr& target = nullptr, + const CoreComponentPtr& target = nullptr, apl_duration_t duration = 0, bool useSnap = false); @@ -112,7 +112,7 @@ class ScrollToAction : public AnimatedScrollAction { CommandScrollAlign mAlign; Rect mSubBounds; bool mScrollToSubBounds; - ComponentPtr mTarget; + CoreComponentPtr mTarget; }; diff --git a/aplcore/include/apl/action/sequentialaction.h b/aplcore/include/apl/action/sequentialaction.h index b6cf685..9bea9f6 100644 --- a/aplcore/include/apl/action/sequentialaction.h +++ b/aplcore/include/apl/action/sequentialaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/action/setpageaction.h b/aplcore/include/apl/action/setpageaction.h index b3beaa3..ab2e143 100644 --- a/aplcore/include/apl/action/setpageaction.h +++ b/aplcore/include/apl/action/setpageaction.h @@ -37,14 +37,14 @@ class SetPageAction : public ResourceHoldingAction { SetPageAction(const TimersPtr& timers, const std::shared_ptr& command, - const ComponentPtr& target); + const CoreComponentPtr& target); private: void start(); private: std::shared_ptr mCommand; - ComponentPtr mTarget; + CoreComponentPtr mTarget; }; } // namespace apl diff --git a/aplcore/include/apl/action/speaklistaction.h b/aplcore/include/apl/action/speaklistaction.h index a222d79..8f7c887 100644 --- a/aplcore/include/apl/action/speaklistaction.h +++ b/aplcore/include/apl/action/speaklistaction.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/apl.h b/aplcore/include/apl/apl.h index 8143fe0..e0aa001 100644 --- a/aplcore/include/apl/apl.h +++ b/aplcore/include/apl/apl.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -49,6 +49,8 @@ #include "apl/graphic/graphicpattern.h" #include "apl/livedata/livearray.h" #include "apl/livedata/livemap.h" +#include "apl/media/mediamanager.h" +#include "apl/media/mediaobject.h" #include "apl/primitives/accessibilityaction.h" #include "apl/primitives/dimension.h" #include "apl/primitives/filter.h" @@ -62,7 +64,6 @@ #include "apl/utils/localemethods.h" #include "apl/utils/log.h" #include "apl/utils/session.h" -#include "apl/utils/telemetry.h" #ifdef ALEXAEXTENSIONS #include "apl/extension/extensionmediator.h" diff --git a/aplcore/include/apl/colorgrammar/colorfunctions.h b/aplcore/include/apl/colorgrammar/colorfunctions.h index aaa8442..1f770a5 100644 --- a/aplcore/include/apl/colorgrammar/colorfunctions.h +++ b/aplcore/include/apl/colorgrammar/colorfunctions.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/colorgrammar/colorgrammar.h b/aplcore/include/apl/colorgrammar/colorgrammar.h index 8bec5e9..e103ad8 100644 --- a/aplcore/include/apl/colorgrammar/colorgrammar.h +++ b/aplcore/include/apl/colorgrammar/colorgrammar.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/common.h b/aplcore/include/apl/common.h index 92d3ee3..be4b438 100644 --- a/aplcore/include/apl/common.h +++ b/aplcore/include/apl/common.h @@ -65,6 +65,8 @@ class GraphicPattern; class LiveArray; class LiveMap; class LiveObject; +class MediaManager; +class MediaObject; class Package; class RootConfig; class RootContext; @@ -93,6 +95,8 @@ using GraphicPatternPtr = std::shared_ptr; using LiveArrayPtr = std::shared_ptr; using LiveMapPtr = std::shared_ptr; using LiveObjectPtr = std::shared_ptr; +using MediaManagerPtr = std::shared_ptr; +using MediaObjectPtr = std::shared_ptr; using PackagePtr = std::shared_ptr; using RootConfigPtr = std::shared_ptr; using RootContextPtr = std::shared_ptr; diff --git a/aplcore/include/apl/component/actionablecomponent.h b/aplcore/include/apl/component/actionablecomponent.h index 27e8666..7d90ade 100644 --- a/aplcore/include/apl/component/actionablecomponent.h +++ b/aplcore/include/apl/component/actionablecomponent.h @@ -71,7 +71,7 @@ class ActionableComponent : public CoreComponent { const ComponentPropDefSet& propDefSet() const override; void release() override; - ActionableComponent(const ContextPtr& context, Properties&& properties, const std::string& path) : + ActionableComponent(const ContextPtr& context, Properties&& properties, const Path& path) : CoreComponent(context, std::move(properties), path), mGesturesDisabled(false) {} bool processGestures(const PointerEvent& event, apl_time_t timestamp) override; diff --git a/aplcore/include/apl/component/component.h b/aplcore/include/apl/component/component.h index 17276d3..36e3ea1 100644 --- a/aplcore/include/apl/component/component.h +++ b/aplcore/include/apl/component/component.h @@ -338,9 +338,9 @@ class Component : public Counter, /** * Call this to ensure that the component has a layout. This method must be used by * children of a sequence to before retrieving the layout bounds. - * @deprecated This method still works but not required to be used. + * @deprecated Should not be used. No-op. */ - virtual void ensureLayout(bool useDirtyFlag = false) = 0; + virtual void ensureLayout(bool useDirtyFlag = false) {} /** * The bounds of this component within an ancestor. @@ -370,7 +370,7 @@ class Component : public Counter, * @param point The requested point. * @return A valid point to scroll to. */ - virtual Point trimScroll(const Point& point) const { return Point(); } + virtual Point trimScroll(const Point& point) { return Point(); } /** * @return The valid directions that can be paged from the current page. This depends on the navigation setting. @@ -382,6 +382,16 @@ class Component : public Counter, */ virtual int pagePosition() const { return 0; } + /** + * @return true if component like Pager or Scrollable can move forward. + */ + virtual bool allowForward() const { return false; } + + /** + * @return true if component like Pager or Scrollable can move backwards. + */ + virtual bool allowBackwards() const { return false; } + /** * The component hierarchy signature is a unique text string that represents the type * of this component and all of the components below it in the hierarchy. This signature @@ -471,6 +481,16 @@ class Component : public Counter, */ ComponentPtr inflateChildAt(const rapidjson::Value& component, size_t index); + /** + * @return true if component could be focused with input focus. + */ + virtual bool isFocusable() const { return false; } + + /** + * @return true if component should participate be reported to the accessibility system. + */ + virtual bool isAccessible() const { return false; } + protected: Component(const ContextPtr& context, const std::string& id); diff --git a/aplcore/include/apl/component/componentpropdef.h b/aplcore/include/apl/component/componentpropdef.h index 8048207..9859531 100644 --- a/aplcore/include/apl/component/componentpropdef.h +++ b/aplcore/include/apl/component/componentpropdef.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/component/componentproperties.h b/aplcore/include/apl/component/componentproperties.h index 13b6a4b..a2b0333 100644 --- a/aplcore/include/apl/component/componentproperties.h +++ b/aplcore/include/apl/component/componentproperties.h @@ -63,7 +63,9 @@ enum TextAlign { kTextAlignAuto = 0, kTextAlignLeft = 1, kTextAlignCenter = 2, - kTextAlignRight = 3 + kTextAlignRight = 3, + kTextAlignStart = 4, + kTextAlignEnd = 5, }; /** @@ -134,7 +136,8 @@ enum Numbering { */ enum Position { kPositionAbsolute = 0, - kPositionRelative = 1 + kPositionRelative = 1, + kPositionSticky = 2 }; /** @@ -278,7 +281,9 @@ enum SwipeDirection { kSwipeDirectionUp, kSwipeDirectionLeft, kSwipeDirectionDown, - kSwipeDirectionRight + kSwipeDirectionRight, + kSwipeDirectionForward, + kSwipeDirectionBackward }; /** @@ -289,6 +294,16 @@ enum ScrollAnimation { kScrollAnimationSmoothInOut = 1 }; +/** + * Layout direction of a Component + */ +enum LayoutDirection { + /// Inherit is for input only, and will not be send out as output value. + kLayoutDirectionInherit = 0, + kLayoutDirectionLTR = 1, + kLayoutDirectionRTL = 2 +}; + /** * Component media state */ @@ -303,6 +318,14 @@ enum ComponentMediaState { kMediaStateError }; +/** + * Keyboard behavior when component gaining focus + */ +enum KeyboardBehaviorOnFocus { + kBehaviorOnFocusSystemDefault = 0, + kBehaviorOnFocusOpenKeyboard = 1 +}; + enum PropertyKey { // NOTE: ScrollDirection is placed early in the list so that it loads BEFORE height and width (see SequenceComponent.cpp) /// SequenceComponent scrolling direction (see #ScrollDirection) @@ -373,6 +396,8 @@ enum PropertyKey { kPropertyDisplay, /// FrameComponent | EditTextComponent drawn border width (output only) kPropertyDrawnBorderWidth, + /// ContainerComponent child absolute right position for LTR layout or left position for RTL layout + kPropertyEnd, /// Component array of opaque entity data kPropertyEntities, /// SequenceComponent fast scroll scaling setting @@ -427,8 +452,14 @@ enum PropertyKey { kPropertyItemsPerCourse, /// ContainerComponent flexbox content justification (see #FlexboxJustifyContent) kPropertyJustifyContent, + /// EditTextComponent the keyboard behavior on component gaining focus + kPropertyKeyboardBehaviorOnFocus, /// EditTextComponent keyboard type kPropertyKeyboardType, + /// Calculated Component layout direction (output only) + kPropertyLayoutDirection, + /// Component layout direction assigned + kPropertyLayoutDirectionAssigned, /// ContainerComponent child absolute left position kPropertyLeft, /// TextComponent letter spacing @@ -517,12 +548,16 @@ enum PropertyKey { kPropertyPadding, /// Component bottom padding kPropertyPaddingBottom, + /// Component layoutDirection aware end padding + kPropertyPaddingEnd, /// Component left padding kPropertyPaddingLeft, /// Component right padding kPropertyPaddingRight, /// Component top padding kPropertyPaddingTop, + /// Component layoutDirection aware start padding + kPropertyPaddingStart, /// Pager page direction kPropertyPageDirection, /// Pager virtual property for the ID of the current page @@ -563,7 +598,7 @@ enum PropertyKey { kPropertyShadowVerticalOffset, /// ContainerComponent child flexbox shrink property kPropertyShrink, - /// EditTextComponent specifies approximately howmany characters canbe displayed + /// EditTextComponent specifies approximately how many characters can be displayed kPropertySize, /// SequenceComponent snap location (see #Snap) kPropertySnap, @@ -573,14 +608,21 @@ enum PropertyKey { kPropertySpacing, /// Component opaque speech data kPropertySpeech, - // EditTextComponent label of the return key + /// ContainerComponent child absolute left position for LTR layout or right position for RTL layout + kPropertyStart, + /// EditTextComponent label of the return key kPropertySubmitKeyType, /// TextComponent | EditTextComponent assigned rich text kPropertyText, - /// TextComponent horizontal alignment (see #TextAlign) + /// Calculated TextComponent horizontal alignment (output only) kPropertyTextAlign, + /// TextComponent assigned horizontal alignment (see #TextAlign) + kPropertyTextAlignAssigned, /// TextComponent vertical alignment (see #TextAlignVertical) kPropertyTextAlignVertical, + /// A BCP-47 string (e.g., en-US) which affects the default font selection of Text or EditText components. + /// For example to select the japanese characters of the "Noto Sans CJK" font family set this to "ja-JP" + kPropertyLang, /// Integer property giving the total number of tracks in the video component. This property will be updated /// whenever a new set of tracks is assigned to the video component. kPropertyTrackCount, @@ -672,6 +714,8 @@ extern Bimap sSnapMap; extern Bimap sRoleMap; extern Bimap sSwipeDirectionMap; extern Bimap sScrollAnimationMap; +extern Bimap sLayoutDirectionMap; +extern Bimap sKeyboardBehaviorOnFocusMap; } // namespace apl diff --git a/aplcore/include/apl/component/containercomponent.h b/aplcore/include/apl/component/containercomponent.h index 32ed028..c211669 100644 --- a/aplcore/include/apl/component/containercomponent.h +++ b/aplcore/include/apl/component/containercomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -22,14 +22,14 @@ namespace apl { class ContainerComponent : public CoreComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - ContainerComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + ContainerComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeContainer; }; const ComponentPropDefSet* layoutPropDefSet() const override; - void processLayoutChanges(bool useDirtyFlag) override; + void processLayoutChanges(bool useDirtyFlag, bool first) override; protected: const ComponentPropDefSet& propDefSet() const override; diff --git a/aplcore/include/apl/component/corecomponent.h b/aplcore/include/apl/component/corecomponent.h index 1b3b672..2664153 100644 --- a/aplcore/include/apl/component/corecomponent.h +++ b/aplcore/include/apl/component/corecomponent.h @@ -37,6 +37,7 @@ class ComponentPropDefSet; class LayoutRebuilder; class Pointer; struct PointerEvent; +class StickyChildrenTree; extern const std::string VISUAL_CONTEXT_TYPE_MIXED; extern const std::string VISUAL_CONTEXT_TYPE_GRAPHIC; @@ -45,6 +46,7 @@ extern const std::string VISUAL_CONTEXT_TYPE_VIDEO; extern const std::string VISUAL_CONTEXT_TYPE_EMPTY; inline float nonNegative(float value) { return value < 0 ? 0 : value; } +inline float nonPositive(float value) { return value > 0 ? 0 : value; } using EventPropertyGetter = std::function; using EventPropertyMap = std::map; @@ -85,7 +87,7 @@ class CoreComponent : public Component, public: CoreComponent(const ContextPtr& context, Properties&& properties, - const std::string& path); + const Path& path); virtual ~CoreComponent() { YGNodeFree(mYGNodeRef); // TODO: Check to make sure we're deallocating correctly @@ -165,13 +167,7 @@ class CoreComponent : public Component, * * This only marks the current component as stale and not any of its children. */ - void markDisplayedChildrenStale(); - - // Documentation from component.h - size_t getDisplayedChildCount() const override; - - // Documentation from component.h - ComponentPtr getDisplayedChildAt(size_t drawIndex) const override; + void markDisplayedChildrenStale(bool useDirtyFlag); /** * Check if child of a components is in the list of displayed children. @@ -180,35 +176,21 @@ class CoreComponent : public Component, */ bool isDisplayedChild(const CoreComponent& child) const; - // Documentation from component.h - bool insertChild(const ComponentPtr& child, size_t index) override { - return canInsertChild() && insertChild(std::dynamic_pointer_cast(child), index, true); - } - - // Documentation from component.h - bool appendChild(const ComponentPtr& child) override { - return canInsertChild() && appendChild(child, true); - } - - // Documentation from component.h + /// Component overrides + size_t getDisplayedChildCount() const override; + ComponentPtr getDisplayedChildAt(size_t drawIndex) const override; + bool insertChild(const ComponentPtr& child, size_t index) override; + bool appendChild(const ComponentPtr& child) override; bool remove() override; - - // Documentation will be inherited bool canInsertChild() const override { // Child insertion is permitted if (a) there isn't a layout rebuilder and (b) there is space for a child. return !mRebuilder && ((singleChild() && mChildren.empty()) || multiChild()); } - - // Documentation will be inherited bool canRemoveChild() const override { // Child removal is permitted if (a) there isn't a layout rebuilder and (b) there is at least one child return !mRebuilder && !mChildren.empty(); } - - // Documentation will be inherited void update(UpdateType type, float value) override; - - // Documentation will be inherited void update(UpdateType type, const std::string& value) override; /** @@ -292,7 +274,13 @@ class CoreComponent : public Component, * This method will conduct a full layout pass if it is required, which is expensive, so avoid * calling this method unless you absolutely must guarantee that a specific component has been laid out. */ - void ensureLayout(bool useDirtyFlag) override; + void ensureLayoutInternal(bool useDirtyFlag); + + /** + * Guarantees that this component's child'has been laid out, so that layout bounds are fully + * calculated. + */ + virtual void ensureChildLayout(const CoreComponentPtr& child, bool useDirtyFlag); /** * @return True if the yoga node needs to run a layout pass. @@ -341,19 +329,24 @@ class CoreComponent : public Component, */ void fixPadding(); + /** + * Update the output layoutDirection + */ + void fixLayoutDirection(bool useDirtyFlag); + /** * Calculate component's relative visibility. * @param parentRealOpacity cumulative opacity value * @param parentVisibleRect component visible rect */ - float calculateVisibility(float parentRealOpacity, const Rect& parentVisibleRect); + float calculateVisibility(float parentRealOpacity, const Rect& parentVisibleRect) const; /** * Calculate component visible rect. * @param parentVisibleRect parents visible rect. * @return visible rectangle of component */ - Rect calculateVisibleRect(const Rect& parentVisibleRect); + Rect calculateVisibleRect(const Rect& parentVisibleRect) const; /** * Create the default event data-binding context for this component. @@ -393,7 +386,12 @@ class CoreComponent : public Component, rapidjson::Value serializeDirty(rapidjson::Document::AllocatorType& allocator) override; // Documentation from component.h - std::string provenance() const override { return mPath; }; + std::string provenance() const override { return mPath.toString(); }; + + /** + * Return path object used to generate provenance. + */ + Path getPathObject() const { return mPath; } /** * Retrieve component's visual context as a JSON object. @@ -440,6 +438,11 @@ class CoreComponent : public Component, */ virtual bool scrollable() const { return false; } + /** + * @return scrollables return the tree of children with position: sticky + */ + virtual std::shared_ptr getStickyTree() { return nullptr; } + /** * Execute any "onBlur" commands associated with this component. These commands * will be run in fast mode. @@ -507,9 +510,10 @@ class CoreComponent : public Component, /** * Walk the hierarchy updating child boundaries. - * @param useDirtyFlag + * @param useDirtyFlag true to notify runtime about changes with dirty properties + * @param first true for first layout for current template */ - virtual void processLayoutChanges(bool useDirtyFlag); + virtual void processLayoutChanges(bool useDirtyFlag, bool first); /** * After a layout has been completed, call this to execute any actions that may occur after a layout @@ -546,7 +550,7 @@ class CoreComponent : public Component, /** * @return true if component could be focused. */ - virtual bool isFocusable() const { return false; } + bool isFocusable() const override { return false; } /** * @return true if component can react to pointer events. @@ -558,6 +562,10 @@ class CoreComponent : public Component, */ virtual bool isActionable() const { return false; } + /// Documentation inherited from component.h + bool isAccessible() const override { + return isFocusable() || !getCalculated(apl::kPropertyAccessibilityLabel).empty(); + } /** * Get visible children of component and respective visibility values. @@ -570,7 +578,7 @@ class CoreComponent : public Component, /** * @return Type of visual context. */ - virtual std::string getVisualContextType(); + virtual std::string getVisualContextType() const; /** * Calculate visual layer. @@ -593,21 +601,21 @@ class CoreComponent : public Component, * @param parentRealOpacity parent component real opacity. * @return component cumulative opacity. */ - float calculateRealOpacity(float parentRealOpacity); + float calculateRealOpacity(float parentRealOpacity) const; /** * Calculate real opacity of component. * Note: it's recursive so better to utilize @see calculateRealOpacity(float parentRealOpacity) when possible. * @return component cumulative opacity. */ - float calculateRealOpacity(); + float calculateRealOpacity() const; /** * Calculate component visible rect. * Note: it's recursive so better to utilize @see calculateVisibleRect(const Rect& parentVisibleRect) when possible. * @return visible rectangle of component */ - Rect calculateVisibleRect(); + Rect calculateVisibleRect() const; /** * @param index index of child. @@ -615,6 +623,13 @@ class CoreComponent : public Component, */ virtual bool shouldAttachChildYogaNode(int index) const { return true; } + /** + * @param index index of child. + * @return True if component should be fully inflated. False if it should be left + * up to lazy inflation controlled by parent component. + */ + virtual bool shouldBeFullyInflated(int index) const { return true; } + /** * Checks to see if this Component inherits state from another Component. State * is inherited if compare Component is an ancestor, and inheritParentState = true for this Component @@ -678,6 +693,11 @@ class CoreComponent : public Component, */ YGNodeRef getNode() const { return mYGNodeRef; } + /** + * @return Direction in which component is laid out. + */ + YGDirection getLayoutDirection() const; + /** * Call this method to get shared ptr of CoreComponent * @return shared ptr of type CoreComponent. @@ -861,6 +881,16 @@ class CoreComponent : public Component, */ void setCalculated(PropertyKey key, const Object &value) { mCalculated.set(key, value); } + /** + * Get the offset applied to this component if it's position property is "sticky" + */ + const Point& getStickyOffset() const { return mStickyOffset; } + + /** + * Set the offset applied to this component if it's position property is "sticky" + */ + void setStickyOffset(Point stickyOffset) { mStickyOffset = stickyOffset; } + protected: // internal, do not call directly virtual bool insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag); @@ -884,6 +914,11 @@ class CoreComponent : public Component, return c; } + /** + * Allow derived classes to react to layout direction change + */ + virtual void handleLayoutDirectionChange(bool useDirtyFlag) {}; + /** * Execute the component key handlers if present. * @param type The key handler type (up/down). @@ -984,7 +1019,7 @@ class CoreComponent : public Component, std::vector mDisplayedChildren; // ordered list of children to be drawn CoreComponentPtr mParent; YGNodeRef mYGNodeRef; - std::string mPath; + Path mPath; std::shared_ptr mRebuilder; Size mLayoutSize; bool mDisplayedChildrenStale; @@ -994,7 +1029,7 @@ class CoreComponent : public Component, // the state of this component. Transform2D mGlobalToLocal; bool mGlobalToLocalIsStale; - + Point mStickyOffset; }; } // namespace apl diff --git a/aplcore/include/apl/component/edittextcomponent.h b/aplcore/include/apl/component/edittextcomponent.h index 2aad8b7..57f54d4 100644 --- a/aplcore/include/apl/component/edittextcomponent.h +++ b/aplcore/include/apl/component/edittextcomponent.h @@ -25,9 +25,9 @@ namespace apl { class EditTextComponent : public ActionableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); - EditTextComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + EditTextComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeEditText; }; @@ -47,6 +47,7 @@ class EditTextComponent : public ActionableComponent { const ComponentPropDefSet& propDefSet() const override; const EventPropertyMap& eventPropertyMap() const override; PointerCaptureStatus processPointerEvent(const PointerEvent& event, apl_time_t timestamp) override; + void executeOnFocus() override; private: CharacterRangesPtr mCharacterRangesPtr; diff --git a/aplcore/include/apl/component/framecomponent.h b/aplcore/include/apl/component/framecomponent.h index 6bb283a..145c633 100644 --- a/aplcore/include/apl/component/framecomponent.h +++ b/aplcore/include/apl/component/framecomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -22,8 +22,8 @@ namespace apl { class FrameComponent : public CoreComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - FrameComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + FrameComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeFrame; }; diff --git a/aplcore/include/apl/component/gridsequencecomponent.h b/aplcore/include/apl/component/gridsequencecomponent.h index 140a68b..30f3827 100644 --- a/aplcore/include/apl/component/gridsequencecomponent.h +++ b/aplcore/include/apl/component/gridsequencecomponent.h @@ -25,24 +25,25 @@ namespace apl { class GridSequenceComponent : public MultiChildScrollableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); GridSequenceComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : MultiChildScrollableComponent(context, std::move(properties), path), mItemsPerCourse(0), mCrossAxisDimensionIsAuto(false) {} ComponentType getType() const override { return kComponentTypeGridSequence; }; void initialize() override; - void processLayoutChanges(bool useDirtyFlag) override; + void processLayoutChanges(bool useDirtyFlag, bool first) override; protected: const ComponentPropDefSet& propDefSet() const override; const ComponentPropDefSet* layoutPropDefSet() const override; void layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, - bool useDirtyFlag) override; + bool useDirtyFlag, + bool first) override; void ensureChildAttached(const CoreComponentPtr& child, int targetIdx) override; const EventPropertyMap & eventPropertyMap() const override; diff --git a/aplcore/include/apl/component/imagecomponent.h b/aplcore/include/apl/component/imagecomponent.h index 4e33e4f..2119ef9 100644 --- a/aplcore/include/apl/component/imagecomponent.h +++ b/aplcore/include/apl/component/imagecomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -23,9 +23,9 @@ namespace apl { class ImageComponent : public CoreComponent, public MediaComponentTrait { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); - ImageComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + ImageComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeImage; } @@ -35,11 +35,15 @@ class ImageComponent : public CoreComponent, const EventPropertyMap& eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; - std::string getVisualContextType() override; + std::string getVisualContextType() const override; /// Media component trait overrides - std::set getSources() override; + std::vector getSources() override; + EventMediaType mediaType() const override { return kEventMediaTypeImage; } + + // Component trait overrides CoreComponentPtr getComponent() override { return shared_from_corecomponent(); } + }; } // namespace apl diff --git a/aplcore/include/apl/component/mediacomponenttrait.h b/aplcore/include/apl/component/mediacomponenttrait.h index 8cf8d05..92fdde6 100644 --- a/aplcore/include/apl/component/mediacomponenttrait.h +++ b/aplcore/include/apl/component/mediacomponenttrait.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -16,7 +16,8 @@ #ifndef _APL_MEDIA_COMPONENT_TRAIT_H #define _APL_MEDIA_COMPONENT_TRAIT_H -#include "componenttrait.h" +#include "apl/component/componenttrait.h" +#include "apl/engine/event.h" namespace apl { @@ -30,18 +31,6 @@ class MediaComponentTrait : public ComponentTrait { */ void postProcessLayoutChanges(); - /** - * Notify component about media loaded. - * @param source source that was loaded. - */ - virtual void pendingMediaLoaded(const std::string& source, int stillPending); - - /** - * Notify component about media that failed to be loaded. - * @param source source that failed to load. - */ - virtual void pendingMediaFailed(const std::string& source); - /// Internal media fetching utilities void resetMediaFetchState(); void ensureMediaRequested(); @@ -52,12 +41,29 @@ class MediaComponentTrait : public ComponentTrait { static const std::vector& propDefList(); protected: + /** + * @return vector of source URI's required by component. Note that order matters. + */ + virtual std::vector getSources() = 0; + /** + * Override this method in your subclass if you need a callback when a pending media object is returned. + * This will not be called if the media object was not pending. The override must call the superclass method. + * @param object The ready or failed media object + */ + virtual void pendingMediaReturned(const MediaObjectPtr& object); /** - * @return set of source URI's required by component. + * Override this method in your subclass. + * @return The type of media used by this component. */ - virtual std::set getSources() = 0; + virtual EventMediaType mediaType() const = 0; + +private: + void updateMediaState(); + +protected: + std::vector mMediaObjects; }; } // namespace apl diff --git a/aplcore/include/apl/component/multichildscrollablecomponent.h b/aplcore/include/apl/component/multichildscrollablecomponent.h index 8de52d2..c067568 100644 --- a/aplcore/include/apl/component/multichildscrollablecomponent.h +++ b/aplcore/include/apl/component/multichildscrollablecomponent.h @@ -36,11 +36,11 @@ enum ScrollableAlign { */ class MultiChildScrollableComponent : public ScrollableComponent { public: - MultiChildScrollableComponent(const ContextPtr& context, Properties&& properties, const std::string& path) : + MultiChildScrollableComponent(const ContextPtr& context, Properties&& properties, const Path& path) : ScrollableComponent(context, std::move(properties), path) {}; Object getValue() const override; bool multiChild() const override { return true; } - void processLayoutChanges(bool useDirtyFlag) override; + void processLayoutChanges(bool useDirtyFlag, bool first) override; void accept(Visitor& visitor) const override; void raccept(Visitor& visitor) const override; Point scrollPosition() const override; @@ -48,7 +48,7 @@ class MultiChildScrollableComponent : public ScrollableComponent { return getCalculated(kPropertyScrollDirection) == kScrollDirectionVertical ? kScrollTypeVertical : kScrollTypeHorizontal; } - Point trimScroll(const Point& point) const override; + Point trimScroll(const Point& point) override; /// Scrollable overrides bool isHorizontal() const override { return getCalculated(kPropertyScrollDirection) == kScrollDirectionHorizontal; } @@ -96,6 +96,9 @@ class MultiChildScrollableComponent : public ScrollableComponent { */ void setScrollPositionDirectlyByChild(const CoreComponentPtr& child, ScrollableAlign align, float offset ); + /// CoreComponent override. + void ensureChildLayout(const CoreComponentPtr& child, bool useDirtyFlag) override; + protected: /** * Finds the immediate child, if any, at the given position. @@ -119,10 +122,11 @@ class MultiChildScrollableComponent : public ScrollableComponent { bool insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) override; void removeChild(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); - void relayoutInPlace(bool useDirtyFlag); + virtual void layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, bool useDirtyFlag, bool first); + void relayoutInPlace(bool useDirtyFlag, bool first); float maxScroll() const override; bool shouldAttachChildYogaNode(int index) const override { return false; } + bool shouldBeFullyInflated(int index) const override; const EventPropertyMap & eventPropertyMap() const override; void handlePropertyChange(const ComponentPropDef& def, const Object& value) override; @@ -179,7 +183,8 @@ class MultiChildScrollableComponent : public ScrollableComponent { void attachYogaNodeIfRequired(const CoreComponentPtr& coreChild, int index) override; bool attachChild(const CoreComponentPtr& child, size_t index); - void runLayoutHeuristics(size_t anchorIdx, float childCache, float pageSize, bool useDirtyFlag); + void runLayoutHeuristics(size_t anchorIdx, float childCache, float pageSize, bool useDirtyFlag, bool first); + void fixScrollPosition(const Rect& oldAnchorRect, const Rect& anchorRect); private: Range mIndexesSeen; @@ -193,6 +198,7 @@ class MultiChildScrollableComponent : public ScrollableComponent { int mLastChildFullyInView = -1; int mLastChildInView = -1; + ActionPtr mDelayLayoutAction; }; } // namespace apl diff --git a/aplcore/include/apl/component/pagercomponent.h b/aplcore/include/apl/component/pagercomponent.h index 6a5d721..22b424d 100644 --- a/aplcore/include/apl/component/pagercomponent.h +++ b/aplcore/include/apl/component/pagercomponent.h @@ -27,8 +27,8 @@ class PageMoveHandler; class PagerComponent : public ActionableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - PagerComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + PagerComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypePager; }; @@ -41,13 +41,17 @@ class PagerComponent : public ActionableComponent { PageDirection pageDirection() const override; int pagePosition() const override { return getCalculated(kPropertyCurrentPage).asInt(); } bool getTags(rapidjson::Value& outMap, rapidjson::Document::AllocatorType& allocator) override; - void processLayoutChanges(bool useDirtyFlag) override; + 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; } bool isVertical() const override { return scrollType() == kScrollTypeVerticalPager; } bool canConsumeFocusDirectionEvent(FocusDirection direction, bool fromInside) override; CoreComponentPtr takeFocusFromChild(FocusDirection direction, const Rect& origin) override; + bool shouldBeFullyInflated(int index) const override { return false; } /** * Command page switch helper function. @@ -100,14 +104,17 @@ class PagerComponent : public ActionableComponent { void attachYogaNodeIfRequired(const CoreComponentPtr& coreChild, int index) override {}; std::map getChildrenVisibility(float realOpacity, const Rect &visibleRect) const override; - void attachCurrentAndReportLoaded(); + void attachPageAndReportLoaded(int page); ActionPtr executePageChangeEvent(bool fast); void setPage(int page); void setPageImmediate(int pageIndex); void handleSetPage(int index, PageDirection direction, const ActionRef& ref, bool skipDefaultAnimation); PageDirection focusDirectionToPage(FocusDirection direction); + void reportLoadedInternal(size_t index); + ActionPtr mCurrentAnimation; + ActionPtr mDelayLayoutAction; std::unique_ptr mPageMoveHandler; }; diff --git a/aplcore/include/apl/component/scrollablecomponent.h b/aplcore/include/apl/component/scrollablecomponent.h index fd5a271..f21ec3d 100644 --- a/aplcore/include/apl/component/scrollablecomponent.h +++ b/aplcore/include/apl/component/scrollablecomponent.h @@ -40,9 +40,10 @@ class ScrollableComponent : public ActionableComponent { bool canConsumeFocusDirectionEvent(FocusDirection direction, bool fromInside) override; CoreComponentPtr takeFocusFromChild(FocusDirection direction, const Rect& origin) override; + std::shared_ptr getStickyTree() override { return mStickyTree; } + protected: - ScrollableComponent(const ContextPtr& context, Properties&& properties, const std::string& path) : - ActionableComponent(context, std::move(properties), path) {}; + ScrollableComponent(const ContextPtr& context, Properties&& properties, const Path& path); /// Core component overrides void initialize() override; @@ -51,19 +52,6 @@ class ScrollableComponent : public ActionableComponent { bool scrollable() const override { return true; } const ComponentPropDefSet& propDefSet() const override; - /** - * Override this to calculate whether the scrollable can continue to scroll forward - * @return true if scrollable can continue to scroll forward, false otherwise. - */ - virtual bool allowForward() const = 0; - - /** - * Override this to calculate whether the scrollable can scroll backwards from its - * current position. - * @return true if scrollable can continue to scroll backwards, false otherwise. - */ - virtual bool allowBackwards() const = 0; - /** * Override this to calculate maximum available scroll position. * @return maximum available scroll position. @@ -85,6 +73,9 @@ class ScrollableComponent : public ActionableComponent { private: bool setScrollPositionInternal(float value); bool canScroll(FocusDirection direction); + + // A tree of the descendants of this scroll with position: sticky. + std::shared_ptr mStickyTree; }; } // namespace apl diff --git a/aplcore/include/apl/component/scrollviewcomponent.h b/aplcore/include/apl/component/scrollviewcomponent.h index 82c2c7d..9cb4e3b 100644 --- a/aplcore/include/apl/component/scrollviewcomponent.h +++ b/aplcore/include/apl/component/scrollviewcomponent.h @@ -22,15 +22,15 @@ namespace apl { class ScrollViewComponent : public ScrollableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - ScrollViewComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + ScrollViewComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeScrollView; }; Object getValue() const override; ScrollType scrollType() const override { return kScrollTypeVertical; } Point scrollPosition() const override; - Point trimScroll(const Point& point) const override; + Point trimScroll(const Point& point) override; bool isHorizontal() const override { return false; } bool isVertical() const override { return true; } diff --git a/aplcore/include/apl/component/sequencecomponent.h b/aplcore/include/apl/component/sequencecomponent.h index 40b84ed..6761aa2 100644 --- a/aplcore/include/apl/component/sequencecomponent.h +++ b/aplcore/include/apl/component/sequencecomponent.h @@ -22,8 +22,8 @@ namespace apl { class SequenceComponent : public MultiChildScrollableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - SequenceComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + SequenceComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeSequence; }; diff --git a/aplcore/include/apl/component/textcomponent.h b/aplcore/include/apl/component/textcomponent.h index 1f945d5..8413dae 100644 --- a/aplcore/include/apl/component/textcomponent.h +++ b/aplcore/include/apl/component/textcomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -23,8 +23,8 @@ namespace apl { class TextComponent : public CoreComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - TextComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + TextComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeText; }; @@ -34,11 +34,16 @@ class TextComponent : public CoreComponent { rapidjson::Value serializeMeasure(rapidjson::Document::AllocatorType& allocator) const; +private: + void updateTextAlign(bool useDirtyFlag); + protected: + void handleLayoutDirectionChange(bool useDirtyFlag) override { updateTextAlign(useDirtyFlag); }; + const EventPropertyMap & eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; void assignProperties(const ComponentPropDefSet& propDefSet) override; - std::string getVisualContextType() override; + std::string getVisualContextType() const override; }; diff --git a/aplcore/include/apl/component/textmeasurement.h b/aplcore/include/apl/component/textmeasurement.h index 416a151..311b9b0 100644 --- a/aplcore/include/apl/component/textmeasurement.h +++ b/aplcore/include/apl/component/textmeasurement.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -48,13 +48,13 @@ class TextMeasurement { static void install(const TextMeasurementPtr& textMeasurement); static const TextMeasurementPtr& instance(); - virtual ~TextMeasurement() {} + virtual ~TextMeasurement() = default; virtual LayoutSize measure( Component *component, - float width, - MeasureMode widthMode, - float height, - MeasureMode heightMode ) = 0; + float width, + MeasureMode widthMode, + float height, + MeasureMode heightMode ) = 0; virtual float baseline( Component *component, float width, diff --git a/aplcore/include/apl/component/touchablecomponent.h b/aplcore/include/apl/component/touchablecomponent.h index e6ca4ab..db0dadb 100644 --- a/aplcore/include/apl/component/touchablecomponent.h +++ b/aplcore/include/apl/component/touchablecomponent.h @@ -45,7 +45,7 @@ class TouchableComponent : public ActionableComponent { bool executeIntrinsicKeyHandlers(KeyHandlerType type, const Keyboard& keyboard) override; protected: - TouchableComponent(const ContextPtr &context, Properties &&properties, const std::string &path) : + TouchableComponent(const ContextPtr &context, Properties &&properties, const Path& path) : ActionableComponent(context, std::move(properties), path) {} const ComponentPropDefSet &propDefSet() const override; diff --git a/aplcore/include/apl/component/touchwrappercomponent.h b/aplcore/include/apl/component/touchwrappercomponent.h index a8a6ba7..d03fb2f 100644 --- a/aplcore/include/apl/component/touchwrappercomponent.h +++ b/aplcore/include/apl/component/touchwrappercomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -24,8 +24,8 @@ namespace apl { class TouchWrapperComponent : public TouchableComponent { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - TouchWrapperComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + TouchWrapperComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeTouchWrapper; }; diff --git a/aplcore/include/apl/component/vectorgraphiccomponent.h b/aplcore/include/apl/component/vectorgraphiccomponent.h index e5b27d4..f4b65b3 100644 --- a/aplcore/include/apl/component/vectorgraphiccomponent.h +++ b/aplcore/include/apl/component/vectorgraphiccomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -17,13 +17,15 @@ #define _APL_VECTOR_GRAPHIC_COMPONENT_H #include "apl/component/touchablecomponent.h" +#include "apl/component/mediacomponenttrait.h" namespace apl { -class VectorGraphicComponent : public TouchableComponent { +class VectorGraphicComponent : public TouchableComponent, + public MediaComponentTrait { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - VectorGraphicComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + 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; }; @@ -32,6 +34,7 @@ class VectorGraphicComponent : public TouchableComponent { bool updateGraphic(const GraphicContentPtr& json) override; void clearDirty() override; std::shared_ptr createTouchEventProperties(const Point &point) const override; + void postProcessLayoutChanges() override; bool isFocusable() const override; bool isActionable() const override; @@ -40,10 +43,18 @@ class VectorGraphicComponent : public TouchableComponent { protected: const EventPropertyMap& eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; - void processLayoutChanges(bool useDirtyFlag) override; - std::string getVisualContextType() override; + void processLayoutChanges(bool useDirtyFlag, bool first) override; + +protected: + std::string getVisualContextType() const override; bool setPropertyInternal(const std::string& key, const Object& value) override; + // Media component trait overrides + std::vector getSources() override; + EventMediaType mediaType() const override { return kEventMediaTypeVectorGraphic; } + + // Component trait overrides + CoreComponentPtr getComponent() override { return shared_from_corecomponent(); } }; diff --git a/aplcore/include/apl/component/videocomponent.h b/aplcore/include/apl/component/videocomponent.h index 238fc3e..d60e372 100644 --- a/aplcore/include/apl/component/videocomponent.h +++ b/aplcore/include/apl/component/videocomponent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -25,16 +25,14 @@ namespace apl { class VideoComponent : public CoreComponent, public MediaComponentTrait { public: - static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const std::string& path); - VideoComponent(const ContextPtr& context, Properties&& properties, const std::string& path); + static CoreComponentPtr create(const ContextPtr& context, Properties&& properties, const Path& path); + VideoComponent(const ContextPtr& context, Properties&& properties, const Path& path); ComponentType getType() const override { return kComponentTypeVideo; }; void updateMediaState(const MediaState& state, bool fromEvent) override; bool getTags(rapidjson::Value& outMap, rapidjson::Document::AllocatorType& allocator) override; - void pendingMediaLoaded(const std::string& source, int stillPending) override; - std::string getCurrentUrl() const; void postProcessLayoutChanges() override; @@ -43,11 +41,15 @@ class VideoComponent : public CoreComponent, const EventPropertyMap & eventPropertyMap() const override; const ComponentPropDefSet& propDefSet() const override; void addEventProperties(ObjectMap &event) const override; - std::string getVisualContextType() override; + std::string getVisualContextType() const override; void assignProperties(const ComponentPropDefSet &propDefSet) override; /// Media component trait overrides - std::set getSources() override; + std::vector getSources() override; + EventMediaType mediaType() const override { return kEventMediaTypeVideo; } + void pendingMediaReturned(const MediaObjectPtr& source) override; + + // Component trait overrides CoreComponentPtr getComponent() override { return shared_from_corecomponent(); } private: diff --git a/aplcore/include/apl/component/yogaproperties.h b/aplcore/include/apl/component/yogaproperties.h index 625d3ec..8c1b6fe 100644 --- a/aplcore/include/apl/component/yogaproperties.h +++ b/aplcore/include/apl/component/yogaproperties.h @@ -49,6 +49,7 @@ extern YGFlexDirection scrollDirectionLookup(ScrollDirection direction); extern void setScrollDirection(YGNodeRef nodeRef, const Object& value, const Context&); extern void setGridScrollDirection(YGNodeRef nodeRef, const Object& value, const Context&); extern void setDisplay(YGNodeRef nodeRef, const Object& value, const Context&); +extern void setLayoutDirection(YGNodeRef nodeRef, const Object& value, const Context&); template void setBorder(YGNodeRef nodeRef, const Object& value, const Context& context) { diff --git a/aplcore/include/apl/content/aplversion.h b/aplcore/include/apl/content/aplversion.h index fe5342a..2c16530 100644 --- a/aplcore/include/apl/content/aplversion.h +++ b/aplcore/include/apl/content/aplversion.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -31,14 +31,17 @@ class APLVersion { kAPLVersion14 = 0x1U << 4, /// Support version 1.4 kAPLVersion15 = 0x1U << 5, /// Support version 1.5 kAPLVersion16 = 0x1U << 6, /// Support version 1.6 + kAPLVersion17 = 0x1U << 7, /// Support version 1.7 kAPLVersion10to11 = kAPLVersion10 | kAPLVersion11, /// Convenience ranges from 1.0 to latest, kAPLVersion10to12 = kAPLVersion10to11 | kAPLVersion12, kAPLVersion10to13 = kAPLVersion10to12 | kAPLVersion13, kAPLVersion10to14 = kAPLVersion10to13 | kAPLVersion14, kAPLVersion10to15 = kAPLVersion10to14 | kAPLVersion15, kAPLVersion10to16 = kAPLVersion10to15 | kAPLVersion16, - kAPLVersionLatest = kAPLVersion10to16, /// Support the most recent engine version - kAPLVersionDefault = kAPLVersion10to16, /// Default value + kAPLVersion10to17 = kAPLVersion10to16 | kAPLVersion17, + kAPLVersionLatest = kAPLVersion10to17, /// Support the most recent engine version + kAPLVersionDefault = kAPLVersion10to17, /// Default value + kAPLVersionReported = kAPLVersion17, /// Default reported version kAPLVersionAny = 0xffffffff, /// Support any versions in the list }; @@ -51,10 +54,11 @@ class APLVersion { bool operator==(const APLVersion& rhs) const { return mValue == rhs.mValue; } bool operator!=(const APLVersion& rhs) const { return mValue != rhs.mValue; } + static std::string getDefaultReportedVersionString(); private: Value mValue; }; } // namespace apl -#endif // _APL_VERSION_H \ No newline at end of file +#endif // _APL_VERSION_H diff --git a/aplcore/include/apl/content/content.h b/aplcore/include/apl/content/content.h index b2f0a33..6d52f92 100644 --- a/aplcore/include/apl/content/content.h +++ b/aplcore/include/apl/content/content.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/directive.h b/aplcore/include/apl/content/directive.h index 24f5d5d..bf89fff 100644 --- a/aplcore/include/apl/content/directive.h +++ b/aplcore/include/apl/content/directive.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/extensioneventhandler.h b/aplcore/include/apl/content/extensioneventhandler.h index 13ffa8e..9f42107 100644 --- a/aplcore/include/apl/content/extensioneventhandler.h +++ b/aplcore/include/apl/content/extensioneventhandler.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/importref.h b/aplcore/include/apl/content/importref.h index 9e1962a..89119ff 100644 --- a/aplcore/include/apl/content/importref.h +++ b/aplcore/include/apl/content/importref.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/importrequest.h b/aplcore/include/apl/content/importrequest.h index 6ab70ca..76c963b 100644 --- a/aplcore/include/apl/content/importrequest.h +++ b/aplcore/include/apl/content/importrequest.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/jsondata.h b/aplcore/include/apl/content/jsondata.h index ffdbc9c..808252b 100644 --- a/aplcore/include/apl/content/jsondata.h +++ b/aplcore/include/apl/content/jsondata.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/metrics.h b/aplcore/include/apl/content/metrics.h index 229236b..783dbce 100644 --- a/aplcore/include/apl/content/metrics.h +++ b/aplcore/include/apl/content/metrics.h @@ -1,5 +1,5 @@ /* - * Copyright 2018, 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/package.h b/aplcore/include/apl/content/package.h index 779924e..f3e1799 100644 --- a/aplcore/include/apl/content/package.h +++ b/aplcore/include/apl/content/package.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/rootconfig.h b/aplcore/include/apl/content/rootconfig.h index d957b72..a11d7ab 100644 --- a/aplcore/include/apl/content/rootconfig.h +++ b/aplcore/include/apl/content/rootconfig.h @@ -35,10 +35,6 @@ #include #endif -#ifdef ALEXAEXTENSIONS -#include -#endif - namespace apl { class LiveDataObject; @@ -79,16 +75,8 @@ class RootConfig { }; enum ExperimentalFeature { - /// Core internal scrolling and paging. - kExperimentalFeatureHandleScrollingAndPagingInCore, - /// Core internal focus handling - kExperimentalFeatureHandleFocusInCore, /// Mark EditText component dirty if text updated kExperimentalFeatureMarkEditTextDirtyOnUpdate, - /// mark kPropertyChildrenChanged as dirty when the Component "displayed children" collection changes. - /// In addition to a child being inserted or removed, the displayed children will be changed - /// when a child transform, display, or opacity changes, or the component has a new layout. - kExperimentalFeatureNotifyChildrenChangedOnDisplayChange, /// Manage media request status in core and update dirty properties kExperimentalFeatureManageMediaRequests, /// Enable the experimental API for Alexa Extensions, in addition to existing extension api. @@ -168,6 +156,16 @@ class RootConfig { return *this; } + /** + * Specify the media manager used for loading images, videos, and vector graphics. + * @param mediaManager The media manager object. + * @return This object for chaining. + */ + RootConfig& mediaManager(const MediaManagerPtr& mediaManager) { + mMediaManager = mediaManager; + return *this; + } + /** * Specify the time manager. * @param timeManager The time manager @@ -706,20 +704,6 @@ class RootConfig { return set(RootProperty::kScreenReader, enabled); } - /** - * Enable core internal scrolling and paging. - * @experimental - * @deprecated Use enableExperimentalFeature(kHandleScrollingAndPagingInCore) - * @param flag true to enable, false by default. - * @return This object for chaining - */ - RootConfig& handleScrollAndPagingInCore(bool flag) { - if (flag) { - enableExperimentalFeature(kExperimentalFeatureHandleScrollingAndPagingInCore); - } - return *this; - } - /** * Set pointer inactivity timeout. Pointer considered stale after pointer was not updated for this time. * @deprecated Use set(RootProperty::kPointerInactivityTimeout, timeout) instead @@ -805,6 +789,11 @@ class RootConfig { */ TextMeasurementPtr getMeasure() const { return mTextMeasurement; } + /** + * @return The configured media manager object + */ + MediaManagerPtr getMediaManager() const { return mMediaManager; } + /** * @return The time manager object */ @@ -1242,6 +1231,7 @@ class RootConfig { ContextPtr mContext; TextMeasurementPtr mTextMeasurement; + MediaManagerPtr mMediaManager; std::shared_ptr mTimeManager; std::shared_ptr mLocaleMethods; std::map, std::pair> mDefaultComponentSize; diff --git a/aplcore/include/apl/content/rootproperties.h b/aplcore/include/apl/content/rootproperties.h index 2ae0a45..88702e3 100644 --- a/aplcore/include/apl/content/rootproperties.h +++ b/aplcore/include/apl/content/rootproperties.h @@ -53,6 +53,11 @@ enum RootProperty { kSequenceChildCache, /// Current UTC time in milliseconds since the epoch kUTCTime, + /// A BCP-47 string (e.g., en-US) which affects the default font selection of Text or EditText components. + /// For example to select the japanese characters of the "Noto Sans CJK" font family set this to "ja-JP" + kLang, + /// The document layout direction. Can be "RTL" or "LTR". Default is "LTR" (left to right) + kLayoutDirection, /// Local time zone adjustment in milliseconds kLocalTimeAdjustment, /// Double press timeout diff --git a/aplcore/include/apl/content/settings.h b/aplcore/include/apl/content/settings.h index 41b205b..2c242a2 100644 --- a/aplcore/include/apl/content/settings.h +++ b/aplcore/include/apl/content/settings.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/content/viewport.h b/aplcore/include/apl/content/viewport.h index 03fe8a7..cd58b78 100644 --- a/aplcore/include/apl/content/viewport.h +++ b/aplcore/include/apl/content/viewport.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/datagrammar/boundsymbol.h b/aplcore/include/apl/datagrammar/boundsymbol.h index f45af86..81af173 100644 --- a/aplcore/include/apl/datagrammar/boundsymbol.h +++ b/aplcore/include/apl/datagrammar/boundsymbol.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -25,12 +25,13 @@ namespace datagrammar { /** * A reference to a symbol in a specific context. Bound symbols are used in equations * to retrieve the current value of a symbol. They hold a weak pointer to the bound - * context to avoid referential loops. + * context to avoid referential loops. Bounds symboles are normallly only used for mutable + * values (immutable values should be directly referenced). */ class BoundSymbol : public ObjectData { public: - BoundSymbol(const ContextPtr& context, const std::string&& name) + BoundSymbol(const ContextPtr& context, std::string name) : mContext(context), mName(std::move(name)) {} @@ -43,6 +44,8 @@ class BoundSymbol : public ObjectData std::string toDebugString() const override; + bool operator==(const BoundSymbol& rhs) const; + friend streamer& operator<<(streamer&, const BoundSymbol&); private: diff --git a/aplcore/include/apl/datagrammar/bytecode.h b/aplcore/include/apl/datagrammar/bytecode.h new file mode 100644 index 0000000..051b315 --- /dev/null +++ b/aplcore/include/apl/datagrammar/bytecode.h @@ -0,0 +1,242 @@ +/** + * 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_BYTE_CODE_H +#define _APL_BYTE_CODE_H + +#include + +#include "apl/primitives/objectdata.h" + +namespace apl { +namespace datagrammar { + +using bciValueType = std::int32_t; +const unsigned OPCODE_BITS = 8; +const unsigned BCI_BITS = 24; +const int MAX_BCI_VALUE = (1 << (BCI_BITS-1)) - 1; +const int MIN_BCI_VALUE = -(1 << (BCI_BITS-1)); + +static_assert(MAX_BCI_VALUE == 8388607, "Incorrect MAX BCI value"); +static_assert(MIN_BCI_VALUE == -8388608, "Incorrect MIN BCI value"); + + +template +bool fitsInBCI(T value) { + static_assert(std::is_integral::value || std::is_floating_point::value, "Numeric type required"); + auto v = static_cast(value); + return value == v && v <= MAX_BCI_VALUE && v >= MIN_BCI_VALUE; +} + +template +bciValueType asBCI(T value) { + assert(fitsInBCI(value)); + return static_cast(value); +} + +/** + * Valid byte code commands. This list will grow over time. + * Do not write code that depends on the order of the commands. + */ +enum ByteCodeOpcode { + BC_OPCODE_NOP = 0, + BC_OPCODE_CALL_FUNCTION, // TOS = TOS_n(TOS_(n-1), ..., TOS) where n = value + BC_OPCODE_LOAD_CONSTANT, // TOS = ByteCodeConstant(value) + BC_OPCODE_LOAD_IMMEDIATE, // TOS = value + BC_OPCODE_LOAD_DATA, // TOS = data[value] + BC_OPCODE_LOAD_BOUND_SYMBOL, // TOS = TOS.eval() + BC_OPCODE_ATTRIBUTE_ACCESS, // TOS = TOS[data[value]] + BC_OPCODE_ARRAY_ACCESS, // TOS = TOS_1[TOS] + BC_OPCODE_UNARY_PLUS, // TOS = +TOS + BC_OPCODE_UNARY_MINUS, // TOS = -TOS + BC_OPCODE_UNARY_NOT, // TOS = !TOS + BC_OPCODE_BINARY_MULTIPLY, // TOS = TOS_1 * TOS + BC_OPCODE_BINARY_DIVIDE, // TOS = TOS_1 / TOS + BC_OPCODE_BINARY_REMAINDER, // TOS = TOS_1 % TOS + BC_OPCODE_BINARY_ADD, // TOS = TOS_1 + TOS + BC_OPCODE_BINARY_SUBTRACT, // TOS = TOS_1 - TOS + BC_OPCODE_COMPARE_OP, // TOS = Compare(ByteCodeComparison(value), TOS_1, TOS) + BC_OPCODE_JUMP, // pc += value + 1 + BC_OPCODE_JUMP_IF_FALSE_OR_POP, // If TOS is false, pc += value + 1 else pop + BC_OPCODE_JUMP_IF_TRUE_OR_POP, // If TOS is true, pc += value + 1 else pop + BC_OPCODE_JUMP_IF_NOT_NULL_OR_POP, // If TOS is not nul, pc += value + 1 else pop + BC_OPCODE_POP_JUMP_IF_FALSE, // If TOS is values, pc += value + 1. Pop in either case. + BC_OPCODE_MERGE_STRING, // TOS = TOS_n + ... + TOS where n = value - 1 + BC_OPCODE_APPEND_ARRAY, // TOS = TOS_1.append(TOS) + BC_OPCODE_APPEND_MAP, // TOS = TOS_2.append(TOS_1, TOS) +}; + +/** + * Sub-category of BC_OPCODE_COMPARE_OP + */ +enum ByteCodeComparison { + BC_COMPARE_LESS_THAN = 0, + BC_COMPARE_LESS_THAN_OR_EQUAL, + BC_COMPARE_EQUAL, + BC_COMPARE_NOT_EQUAL, + BC_COMPARE_GREATER_THAN, + BC_COMPARE_GREATER_THAN_OR_EQUAL +}; + +/** + * Pre-defined constants that don't need to be added to the operands vector. + */ +enum ByteCodeConstant { + BC_CONSTANT_NULL = 0, + BC_CONSTANT_FALSE, + BC_CONSTANT_TRUE, + BC_CONSTANT_EMPTY_STRING, + BC_CONSTANT_EMPTY_ARRAY, + BC_CONSTANT_EMPTY_MAP, +}; + +/** + * Convert a named constant into an appropriate Object + * @param value The named constant + * @return The appropriate Object + */ +inline Object getConstant(ByteCodeConstant value) { + switch (value) { + case BC_CONSTANT_NULL: + return Object::NULL_OBJECT(); + case BC_CONSTANT_FALSE: + return Object::FALSE_OBJECT(); + case BC_CONSTANT_TRUE: + return Object::TRUE_OBJECT(); + case BC_CONSTANT_EMPTY_STRING: + return Object(""); + case BC_CONSTANT_EMPTY_ARRAY: + return Object::EMPTY_MUTABLE_ARRAY(); + case BC_CONSTANT_EMPTY_MAP: + return Object::EMPTY_MUTABLE_MAP(); + default: + // Refer to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1766 + return Object::NULL_OBJECT(); + } +} + +/** + * Given a comparison operator and a value returned from three-way comparison operation, + * evaluate if the comparison worked. + * @param comparison The comparison operator. + * @param value Three possible values: less-than (-1), equal (0), and greater-than (1) + * @return True if the operator/value combination is true. + */ +inline bool CompareOp(ByteCodeComparison comparison, int value) { + switch (comparison) { + case BC_COMPARE_LESS_THAN: + return value == -1; + case BC_COMPARE_LESS_THAN_OR_EQUAL: + return value != 1; + case BC_COMPARE_EQUAL: + return value == 0; + case BC_COMPARE_NOT_EQUAL: + return value != 0; + case BC_COMPARE_GREATER_THAN: + return value == 1; + case BC_COMPARE_GREATER_THAN_OR_EQUAL: + return value != -1; + default: + // Refer to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1766 + return value == 0; + } +} + +/** + * A single byte code instruction contains an opcode and a value + */ +struct ByteCodeInstruction { + ByteCodeOpcode type : OPCODE_BITS; + bciValueType value : BCI_BITS; + + std::string toString() const; +}; + +static_assert(sizeof(ByteCodeInstruction) == 4, "Wrong size of ByteCodeInstruction"); + +/** + * Store an expression that has been compiled into byte code. + */ +class ByteCode : public ObjectData, public std::enable_shared_from_this +{ +public: + explicit ByteCode(const ContextPtr& context) + : mContext(context) + {} + + /** + * Evaluate this byte code in the associated context. + * @return The result of the evaluation. + */ + Object eval() const override; + + /** + * @return A simplified version of this byte code. This may be a constant expression. + */ + Object simplify(); + + /** + * Retrieve the list of mutable global symbols used by this byte code and suitable + * for building data-binding dependencies. This method also optimizes the underlying + * byte code for higher performance. + * @param symbols An empty symbols map to be populated. + */ + void symbols(SymbolReferenceMap& symbols); + + /** + * Decompile the byte code and write the disassembled code to the LOG. + */ + void dump() const; + + /** + * @return True if this byte code has been passed through the optimizer + */ + bool isOptimized() const { return mOptimized; } + + /** + * @return Lock and return the context referenced by this byte code + */ + ContextPtr getContext() const; + + /** + * Return a formatted de-compiled line + * @param pc The program counter location + * @return The de-compiled line as a string + */ + std::string instructionAsString(int pc) const; + + /** + * @return Number of instructions + */ + size_t instructionCount() const { return mInstructions.size(); } + + std::string toDebugString() const override { return "Compiled Byte Code"; } + + friend class ByteCodeAssembler; + friend class ByteCodeOptimizer; + friend class ByteCodeEvaluator; + +private: + std::weak_ptr mContext; + std::vector mInstructions; + std::vector mData; + + bool mOptimized = false; +}; + +} // namespace datagrammar +} // namespace apl + +#endif // _APL_BYTE_CODE_H diff --git a/aplcore/include/apl/datagrammar/bytecodeassembler.h b/aplcore/include/apl/datagrammar/bytecodeassembler.h new file mode 100644 index 0000000..167027e --- /dev/null +++ b/aplcore/include/apl/datagrammar/bytecodeassembler.h @@ -0,0 +1,169 @@ +/** + * 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_BYTE_CODE_ASSEMBLER_H +#define _APL_BYTE_CODE_ASSEMBLER_H + +#include "apl/datagrammar/bytecode.h" + +namespace apl { +namespace datagrammar { + +/** + * Ordering groups. Processing rules with the same order "collapse" into each other. + */ +enum ByteCodeOrder { + BC_ORDER_FIELD_OR_FUNCTION = 0, + BC_ORDER_UNARY, + BC_ORDER_MULTIPLICATIVE, + BC_ORDER_ADDITIVE, + BC_ORDER_COMPARISON, + BC_ORDER_EQUALITY, + BC_ORDER_LOGICAL_AND, + BC_ORDER_LOGICAL_OR, + BC_ORDER_NULLC, + BC_ORDER_TERNARY_IF, + BC_ORDER_TERNARY_ELSE, + BC_ORDER_GROUP, + BC_ORDER_FUNCTION, + BC_ORDER_COMMA, + BC_ORDER_DB, + BC_ORDER_INLINE_ARRAY, + BC_ORDER_INLINE_MAP, + BC_ORDER_STRING, + BC_ORDER_STRING_ELEMENT, + BC_ORDER_ATTRIBUTE, +}; + + + +/** + * A ByteCodeAssembler is passed to the PEGTL-based data grammar rules. As the rules + * are parsed, byte code is built up in the assembler. + */ +class ByteCodeAssembler { +public: + /** + * Parse a string for data-binding expressions and return the result. The result may + * be byte code or a simple object. + * @param context The data-binding context + * @param value The string to parse + * @return The calculated result + */ + static Object parse(const Context& context, const std::string& value); + +private: + Object retrieve() const; + + /*** Methods after this point are for use by the PEGTL parser ***/ + + // Load values + void loadOperand(const Object& value); + void loadConstant(ByteCodeConstant value); + void loadImmediate(bciValueType value); + void loadGlobal(const std::string& name); + + void pushAttributeName(const std::string &name); + void loadAttribute(); + + // Unary operators + void pushUnaryOperator(char ch); + void reduceUnary(); + + // Binary operators + void pushBinaryOperator(const std::string& op); + void reduceBinary(ByteCodeOrder order); + + // Parenthesis + void pushGroup(); + void popGroup(); + + // DB-group + void pushDBGroup(); + void popDBGroup(); + + // AND/OR/NULLC-statement + void pushAnd(); + void pushOr(); + void pushNullC(); + void reduceJumps(ByteCodeOrder order); + + // Ternary + void pushTernaryIf(); + void pushTernaryElse(); + void reduceOneJump(ByteCodeOrder order); + + // Array access + void pushArrayAccessStart(); + void pushArrayAccessEnd(); + + // Inline Array creation + void pushInlineArrayStart(); + void appendInlineArrayArgument(); + void pushInlineArrayEnd(); + + // Inline Map creation + void pushInlineMapStart(); + void appendInlineMapArgument(); + void pushInlineMapEnd(); + + // Functions + void pushFunctionStart(); + void pushComma(); + void pushFunctionEnd(); + + // Strings + void startString(); + void addString(const std::string& s); + void endString(); + + std::string toString() const; + + ContextPtr context() const { return mContext; } + + template friend struct action; + +public: + struct Operator { + ByteCodeOrder order; // Group order + ByteCodeOpcode command; // CommandType + int value; // Comparison or Constant value + }; + +private: + explicit ByteCodeAssembler(const Context& context); + +private: + struct CodeUnit { + explicit CodeUnit(const ContextPtr& context) + : byteCode(std::make_shared(context)) {} + + std::shared_ptr byteCode; + std::vector operators; // Operator stack + }; + + ContextPtr mContext; + CodeUnit mCode; + + // Convenience references so we don't keep dereferencing the ByteCode + std::vector* mInstructionRef; + std::vector* mDataRef; + std::vector* mOperatorsRef; +}; + +} // namespace datagrammar +} // namespace apl + +#endif // _APL_BYTE_CODE_ASSEMBLER_H diff --git a/aplcore/include/apl/datagrammar/bytecodeevaluator.h b/aplcore/include/apl/datagrammar/bytecodeevaluator.h new file mode 100644 index 0000000..4d92cf8 --- /dev/null +++ b/aplcore/include/apl/datagrammar/bytecodeevaluator.h @@ -0,0 +1,75 @@ +/** + * 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_BYTE_CODE_EVALUATOR_H +#define APL_BYTE_CODE_EVALUATOR_H + +#include +#include + +#include "apl/datagrammar/bytecode.h" + +namespace apl { +namespace datagrammar { + +/** + * Evaluation environment for byte code. This should only be allocated on the stack. + * The byte code reference must remain valid for the lifetime of the evaluator. + */ +class ByteCodeEvaluator { +public: + explicit ByteCodeEvaluator(const ByteCode& byteCode); + + /** + * Start or continue executing the byte code. + */ + void advance(); + + /** + * @return True if the byte code has finished executing + */ + bool isDone() const { return mState == DONE; } + + /** + * @return True if the byte code is an error state + */ + bool isError() const { return mState == ERROR; } + + /** + * Only valid after the byte code has run to completion. + * @return True if the byte code does not depend on any mutable methods or data. + */ + bool isConstant() const { assert(mState == DONE); return mIsConstant; } + + /** + * @return The result of executing the byte code. If the return type is void, this + * method will return null. + */ + Object getResult() const; + +private: + enum State { INIT, DONE, ERROR }; + + const ByteCode& mByteCode; + std::vector mStack; + int mProgramCounter = 0; + State mState = INIT; + bool mIsConstant = true; +}; + +} // namespace datagrammar +} // namespace apl + +#endif //APL_BYTE_CODE_EVALUATOR_H diff --git a/aplcore/include/apl/datagrammar/bytecodeoptimizer.h b/aplcore/include/apl/datagrammar/bytecodeoptimizer.h new file mode 100644 index 0000000..ca364fd --- /dev/null +++ b/aplcore/include/apl/datagrammar/bytecodeoptimizer.h @@ -0,0 +1,49 @@ +/** + * 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_BYTE_CODE_OPTIMIZER_H +#define _APL_BYTE_CODE_OPTIMIZER_H + +#include "apl/datagrammar/bytecodeassembler.h" + +namespace apl { +namespace datagrammar { + +/** + * This class optimizes byte code with constant folding, dead code removal, and + * context resolution. + */ +class ByteCodeOptimizer { +public: + /** + * Optimize the byte code. + * @param byteCode A reference to the byte code to optimize. + */ + static void optimize(ByteCode& byteCode); + +private: + ByteCodeOptimizer(ByteCode& byteCode); + + void simplifyOperations(); + void simplifyOperands(); + +private: + ByteCode& mByteCode; +}; + +} // namespace datagrammar +} // namespace apl + +#endif // _APL_BYTE_CODE_OPTIMIZER_H diff --git a/aplcore/include/apl/datagrammar/databindingerrors.h b/aplcore/include/apl/datagrammar/databindingerrors.h new file mode 100644 index 0000000..4df1d73 --- /dev/null +++ b/aplcore/include/apl/datagrammar/databindingerrors.h @@ -0,0 +1,184 @@ +/** + * 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_DATA_BINDING_ERRORS_H +#define _APL_DATA_BINDING_ERRORS_H + +#include + +#include "grammarerror.h" +#include "databindingrules.h" + +namespace apl { +namespace datagrammar { + +/** + * Standard PEGTL parsing error controller. This raises a parse_error exception + * if a problem occurs. The static "error_value" defined in this template converts + * from a templated action to a numbered error message. The "errorToString" method + * further converts the error message into a human-readable string. + * @tparam Rule + */ +template< typename Rule > +struct error_control : tao::pegtl::normal< Rule > { + static const GrammarError error_value; + + template + static void raise(const Input &in, States &&...) { + throw tao::pegtl::parse_error(errorToString(error_value), in); + } +}; + +/** + * Convenience routine for printing out the current character being processed + * by the PEGTL grammar. + * @tparam Input + * @param in + * @return A string showing the character (if printable) and the numeric value of the character. + */ +template< typename Input > std::string +get_current(const Input& in) +{ + streamer out; + + if( in.empty() ) { + out << ""; + } + else { + const auto c = in.peek_uint8(); + switch( c ) { + case 0: + out << " = "; + break; + case 9: + out << " = "; + break; + case 10: + out << " = "; + break; + case 13: + out << " = "; + break; + default: + if( isprint( c ) ) { + out << "'" << (char) c << "' = "; + } + } + out << "(char)" << unsigned( c ); + } + + return out.str(); +} + +// These are only enabled if DEBUG_DATA_BINDING is true +const bool TRACED_ERROR_CONTROL_SHOW_START = false; // Log starting blocks +const bool TRACED_ERROR_CONTROL_SHOW_SUCCESS = false; // Log successful blocks +const bool TRACED_ERROR_CONTROL_SHOW_FAILURE = false; // Log failed blocks + +/** + * Fancing PEGTL parsing error controller. This is enabled with DEBUG_DATA_BINDING. + * The messages are output as the PEGTL grammar is parsed. + * @tparam Rule + */ +template< typename Rule > +struct traced_error_control : error_control +{ + template + static void start(const Input& in, States&& ... /*unused*/ ) { + LOG_IF(TRACED_ERROR_CONTROL_SHOW_START) << pegtl::to_string(in.position()) + << " start " << pegtl::internal::demangle() + << "; current " << get_current(in); + } + + template + static void success(const Input& in, States&& ... /*unused*/ ) { + LOG_IF(TRACED_ERROR_CONTROL_SHOW_SUCCESS) << pegtl::to_string(in.position()) + << " success " << pegtl::internal::demangle() + << "; next " << get_current(in); + } + + template + static void failure(const Input& in, States&& ... /*unused*/ ) { + LOG_IF(TRACED_ERROR_CONTROL_SHOW_FAILURE) << pegtl::to_string(in.position()) + << " failure " << pegtl::internal::demangle(); + } + + template class Action, typename Iterator, typename Input, typename... States> + static auto apply(const Iterator& begin, const Input& in, States&& ... st) + -> decltype(pegtl::normal::template apply(begin, in, st...)) { + LOG(LogLevel::kDebug) << pegtl::to_string(in.position()) + << " apply " << pegtl::internal::demangle() + << " '" << std::string(in.begin() + begin.byte, in.current()) + << "' position=" << begin.byte; + return pegtl::normal::template apply(begin, in, st...); + } + + template class Action, typename Input, typename... States> + static auto apply0(const Input& in, States&& ... st) + -> decltype(pegtl::normal::template apply0(in, st...)) { + LOG(LogLevel::kDebug) << pegtl::to_string(in.position()) + << " apply " << pegtl::internal::demangle() + << " '" << std::string(in.begin() + in.byte, in.current()) + << "' position=" << in.byte; + return pegtl::normal::template apply0(in, st...); + } +}; + +template<> const GrammarError error_control>::error_value = INVALID_NUMBER_FORMAT; +template<> const GrammarError error_control::error_value = UNEXPECTED_TOKEN; +template<> const GrammarError error_control::error_value = UNEXPECTED_TOKEN_BEFORE_EOF; + +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_MULTIPLICATIVE; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_ADDITIVE; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_COMPARISON; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_EQUALITY; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_LOGICAL_AND; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_LOGICAL_OR; +template<> const GrammarError error_control::error_value = EXPECTED_OPERAND_AFTER_NULLC; + +template<> const GrammarError error_control::error_value = EXPECTED_EXPRESSION; + +template<> const GrammarError error_control::error_value = MALFORMED_ARRAY; +template<> const GrammarError error_control::error_value = UNTERMINATED_SS_STRING; +template<> const GrammarError error_control::error_value = UNTERMINATED_DS_STRING; +template<> const GrammarError error_control::error_value = EXPECTED_MAP_VALUE_ASSIGNMENT; +template<> const GrammarError error_control::error_value = EXPECTED_MAP_ASSIGNMENT; +template<> const GrammarError error_control::error_value = MALFORMED_MAP; +template<> const GrammarError error_control::error_value = MALFORMED_TERNARY_EXPRESSION; +template<> const GrammarError error_control::error_value = EXPECTED_POSTFIX_RIGHT_PAREN; + +// Untested items + +template<> const GrammarError error_control::error_value = static_cast(105); +template<> const GrammarError error_control::error_value = static_cast(106); +template<> const GrammarError error_control::error_value = static_cast(107); +template<> const GrammarError error_control::error_value = static_cast(111); +template<> const GrammarError error_control::error_value = static_cast(117); +template<> const GrammarError error_control::error_value = static_cast(118); +template<> const GrammarError error_control::error_value = static_cast(119); +template<> const GrammarError error_control::error_value = static_cast(120); +template<> const GrammarError error_control::error_value = static_cast(121); +template<> const GrammarError error_control::error_value = static_cast(122); +template<> const GrammarError error_control::error_value = static_cast(123); +template<> const GrammarError error_control::error_value = static_cast(127); +template<> const GrammarError error_control::error_value = static_cast(131); +template<> const GrammarError error_control>::error_value = static_cast(141); + +template<> const GrammarError error_control>::error_value = static_cast(204); + +} // namespace datagrammar +} // namespace apl + +#endif // _APL_DATA_BINDING_ERRORS_H diff --git a/aplcore/include/apl/datagrammar/databindinggrammar.h b/aplcore/include/apl/datagrammar/databindinggrammar.h index 58277b3..34cff26 100644 --- a/aplcore/include/apl/datagrammar/databindinggrammar.h +++ b/aplcore/include/apl/datagrammar/databindinggrammar.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -12,11 +12,24 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. * - * Data-binding grammar + * Data-binding grammar: BNF format + * + * exp ::= true | false | null | Number | String | dimension | resource | + * exp binop exp | unop exp | prefixexp | exp '?' exp ':' exp | '(' exp ')' + * dimension ::= Number dimunit + * dimunit ::= dp | px | vh | vw + * resource ::= '@' Name + * prefixexp ::= var | prefixexp '(' [explist] ')' + * var ::= Name | prefixexp '[' exp ']' | prefixexp '.' Name + * explist ::= {exp ','} exp + * binop ::= '+' | '-' | '*' | '/' | '%' | + * '<' | '>' | '<=' | '>=' | '==' | '!=' + * '&&' | '||' | '??' + * unop ::= '+' | '-' | '!' */ -#ifndef _APL_DATA_BINDING_GRAMMAR -#define _APL_DATA_BINDING_GRAMMAR +#ifndef _APL_DATA_BINDING_GRAMMAR_H +#define _APL_DATA_BINDING_GRAMMAR_H #include #include @@ -32,18 +45,17 @@ using namespace pegtl; * \cond ShowDataGrammar */ // Forward declarations -struct ternary; +struct expression; struct ds_string; struct ss_string; -struct ws : star {}; - +// ******* Symbols ******* struct sym_dbstart : string<'$', '{'> {}; struct sym_dbend : one<'}'> {}; struct sym_question : one<'?'> {}; struct sym_colon : one<':'> {}; -struct sym_term : one<'*', '/', '%'> {}; -struct sym_expr : one<'+', '-' > {}; +struct sym_multiplicative : one<'*', '/', '%'> {}; +struct sym_additive : one<'+', '-' > {}; struct sym_compare : sor< string<'<', '='>, string<'>', '='>, one<'<', '>'> > {}; struct sym_equal : sor< string<'=', '='>, string<'!', '='> > {}; struct sym_and : string<'&', '&'> {}; @@ -55,8 +67,9 @@ struct sym_decimal : one<'.'> {}; struct sym_attribute : one<'.'> {}; struct sym_array_access_start : one<'['> {}; struct sym_array_access_end : one<']'> {}; -struct sym_group_start : one<'('> {}; -struct sym_group_end : one<')'> {}; + +struct sep : space {}; +struct ws : star {}; struct str_false : string<'f', 'a', 'l', 's', 'e'> {}; struct str_null : string<'n', 'u', 'l', 'l'> {}; @@ -65,6 +78,7 @@ struct str_dp : string<'d', 'p'> {}; struct str_px : string<'p', 'x'> {}; struct str_vh : string<'v', 'h'> {}; struct str_vw : string<'v', 'w'> {}; +struct str_keyword : sor< str_false, str_null, str_true, str_dp, str_px, str_vh, str_vw>{}; template struct key : seq> {}; @@ -72,10 +86,7 @@ struct key : seq> {}; struct key_false : key {}; struct key_null : key {}; struct key_true : key {}; -struct key_dp : key {}; -struct key_px : key {}; -struct key_vh : key {}; -struct key_vw : key {}; +struct keyword : key {}; struct zero : if_must, not_at> {}; struct number_int : sor, star>> {}; @@ -83,60 +94,105 @@ struct number : sor>, // INTEGER seq>, // . DIGITS+ number_int> {}; // INTEGER -struct symbol : seq > {}; -struct resource : seq, identifier> {}; - -struct postfix_identifier: identifier {}; +struct symbol : seq< not_at, alpha, star > {}; + +// Inline Arrays (e.g., [1,2,3]) +struct array_comma : one<','> {}; +struct array_list : list_must {}; +struct array_body : pad_opt {}; +struct array_start : one<'['> {}; +struct array_end : one<']'> {}; +struct array : if_must {}; + +// Inline maps +struct map_start : one<'{'> {}; +struct map_comma : one<','> {}; +struct map_assign : one<':'> {}; +struct map_end : one<'}'> {}; +struct map_element : if_must, ws, map_assign, ws, expression> {}; +struct map_list : list_must {}; +struct map_body : pad_opt {}; +struct map : if_must {}; + +struct postfix_identifier : identifier {}; struct postfix_attribute : seq {}; -struct postfix_array_access : seq {}; +struct postfix_array_access : seq {}; +struct argument_list : list_must {}; struct postfix_left_paren : one<'('> {}; struct postfix_right_paren : one<')'> {}; -struct argument_list : list_must {}; -struct postfix_function : if_must, postfix_right_paren> {}; -struct postfix : sor {}; -struct postfix_expression : seq, star> {}; - -struct dimension_unit : sor {}; -struct dimension_or_number : seq> {}; -struct grouping : if_must {}; +struct postfix_function : if_must, postfix_right_paren> {}; +struct postfix : sor< postfix_attribute, + postfix_array_access, + postfix_function >{}; +struct plain_symbol : symbol {}; +struct resource : seq, identifier > {}; +struct postfix_expression : seq< sor, star< postfix >> {}; + +// TODO: Can I collapse this so we don't parse numbers twice? +struct dimension : seq, ws, sor > {}; +struct group_start : one<'('> {}; +struct group_end : one<')'> {}; +struct grouping : if_must {}; struct factor : sor< grouping, - dimension_or_number, key_true, key_false, key_null, + dimension, postfix_expression, + number, ss_string, ds_string> { }; -struct sfactor : seq, factor> {}; -struct term : list_must {}; -struct expression : list_must {}; -struct compare : list_must {}; -struct equate : list_must< compare, sym_equal, space >{}; -struct logical_and : list_must< equate, sym_and, space > {}; -struct logical_or : list_must< logical_and, sym_or, space >{}; -struct nullc : list_must< logical_or, sym_nullc, space >{}; -struct ternary_tail : seq< ternary, ws, sym_colon, ws, ternary >{}; -struct ternary : seq< nullc, +struct unary_expression : seq, factor> {}; +struct multiplicative_expression : list_must {}; +struct additive_expression : list_must {}; +struct comparison_expression : list_must {}; +struct equality_expression : list_must< comparison_expression, sym_equal, sep >{}; +struct logical_and_expression : list_must< equality_expression, sym_and, sep > {}; +struct logical_or_expression : list_must< logical_and_expression, sym_or, sep >{}; +struct nullc_expression : list_must< logical_or_expression, sym_nullc, sep >{}; +struct ternary_tail : seq< expression, ws, sym_colon, ws, expression >{}; +struct ternary_expression : seq< nullc_expression, opt< ws, if_must>> {}; -struct db : if_must, sym_dbend> {}; +struct expression : ternary_expression {}; + +struct db_empty : success {}; // No expression - by default we insert an empty string +struct db_body : pad_opt, sep> {}; +struct db : if_must {}; // TODO: This assumes UTF-8 encoding. We're relying on RapidJSON to return UTF-8 struct char_ : utf8::any {}; -struct ds_raw : until >, at >, must > {}; -struct ds_string : if_must, list, disable > > {}; - -struct ss_raw : until >, at >, must > {}; -struct ss_string : if_must, list, disable > > {}; +// Double-quoted string. E.g.: ${"foo"} +struct sym_double_quote : one<'"'> {}; +struct ds_char : char_ {}; +struct ds_raw : until, at >, must > {}; +struct ds_start : sym_double_quote {}; +struct ds_end : sym_double_quote {}; +struct ds_body : list {}; +struct ds_string : if_must {}; + +// Single-quoted string. E.g.: ${'foo'} +struct sym_single_quote : one<'\''> {}; +struct ss_char : char_ {}; +struct ss_raw : until, at >, must > {}; +struct ss_start : sym_single_quote {}; +struct ss_end : sym_single_quote {}; +struct ss_body : list {}; +struct ss_string : if_must {}; + +// NOTE: Probably can change this to until< at, char_ > {}; +// Open-string: E.g. "this is a ${1+3} generic string" +struct os_raw : until, at >, must > {}; +struct os_start : success {}; +struct os_string : seq> {}; + +struct grammar : must {}; -struct raw : until, at >, must > {}; -struct openstring : list {}; -struct grammar : must {}; /** * \endcond @@ -145,4 +201,4 @@ struct grammar : must {}; } // namespace datagrammar } // namespace apl -#endif // _APL_DATA_BINDING_GRAMMAR \ No newline at end of file +#endif // _APL_DATA_BINDING_GRAMMAR_H \ No newline at end of file diff --git a/aplcore/include/apl/datagrammar/databindingrules.h b/aplcore/include/apl/datagrammar/databindingrules.h index 4b5961b..2f4d414 100644 --- a/aplcore/include/apl/datagrammar/databindingrules.h +++ b/aplcore/include/apl/datagrammar/databindingrules.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -15,13 +15,13 @@ * Data-binding rules */ -#ifndef _APL_DATA_BINDING_GRAMMAR_H -#define _APL_DATA_BINDING_GRAMMAR_H +#ifndef _APL_DATA_BINDING_RULES_H +#define _APL_DATA_BINDING_RULES_H #include #include "databindinggrammar.h" -#include "databindingstack.h" -#include "node.h" +#include "bytecodeassembler.h" + #include "apl/primitives/object.h" @@ -39,6 +39,7 @@ using namespace pegtl; /** * \cond ShowDataGrammar */ + // ******************** ACTIONS ********************* template @@ -51,206 +52,209 @@ struct action : nothing< Rule > template<> struct action< number > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { + static void apply(const Input& in, ByteCodeAssembler& assembler) { double value = std::stod(in.string()); - stacks.push(Object(value)); + if (fitsInBCI(value)) + assembler.loadImmediate(asBCI(value)); + else + assembler.loadOperand(Object(value)); } }; template<> struct action< key_null > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Object::NULL_OBJECT()); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.loadConstant(BC_CONSTANT_NULL); } }; template<> struct action< key_true > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Object::TRUE_OBJECT()); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.loadConstant(BC_CONSTANT_TRUE); } }; template<> struct action< key_false > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Object::FALSE_OBJECT()); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.loadConstant(BC_CONSTANT_FALSE); } }; -// ************* Unary operations ************* -template<> struct action< star< sym_unary > > +// ************* Dimension ************* + +template<> struct action< dimension > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - std::string s = in.string(); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.loadOperand(Object(Dimension(*(assembler.context()), in.string()))); + } +}; - for (auto it=s.begin() ; it != s.end() ; it++) { - auto iter = sUnaryOperators.find(*it); - if (iter != sUnaryOperators.end()) - stacks.push(iter->second); - } +// ************* Unary operations ************* +template<> struct action< sym_unary > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.pushUnaryOperator(in.string()[0]); } }; -template<> struct action< sfactor > +template<> struct action< unary_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceUnary(OP_UNARY); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceUnary(); } }; // ************* Multiplication, division, modulus ************* -template<> struct action< sym_term > { +template<> struct action< sym_multiplicative > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - std::string s = in.string(); - stacks.push(sTermOperators.find(s)->second); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushBinaryOperator(in.string()); } }; -template<> struct action< term > +template<> struct action< multiplicative_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_TERM); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceBinary(BC_ORDER_MULTIPLICATIVE); } }; // ************* Addition, subtraction ************* -template<> struct action< sym_expr > { +template<> struct action< sym_additive > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - std::string s = in.string(); - stacks.push(sExpressionOperators.find(s)->second); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushBinaryOperator(in.string()); } }; -template<> struct action< expression > +template<> struct action< additive_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_EXPRESSION); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceBinary(BC_ORDER_ADDITIVE); } }; // ************* Comparison ************* template<> struct action< sym_compare > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - std::string s = in.string(); - stacks.push(sCompareOperators.find(s)->second); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushBinaryOperator(in.string()); } }; -template<> struct action< compare > +template<> struct action< comparison_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_COMPARISON); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceBinary(BC_ORDER_COMPARISON); } }; // ************* Equality ************* template<> struct action< sym_equal > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - std::string s = in.string(); - stacks.push(sEqualityOperators.find(s)->second); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushBinaryOperator(in.string()); } }; -template<> struct action< equate > +template<> struct action< equality_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_EQUALITY); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceBinary(BC_ORDER_EQUALITY); } }; // ************* Logical AND ************* template<> struct action< sym_and > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.push(AND_OPERATOR); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushAnd(); } }; -template<> struct action< logical_and > +template<> struct action< logical_and_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_LOGICAL_AND); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceJumps(BC_ORDER_LOGICAL_AND); } }; // ************* Logical OR ************* template<> struct action< sym_or > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.push(OR_OPERATOR); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushOr(); } }; -template<> struct action< logical_or > +template<> struct action< logical_or_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_LOGICAL_OR); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceJumps(BC_ORDER_LOGICAL_OR); } }; // ************* Null coalescence ************* template<> struct action< sym_nullc > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.push(NULLC_OPERATOR); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.pushNullC(); } }; -template<> struct action< nullc > +template<> struct action< nullc_expression > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceLR(OP_NULLC); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceJumps(BC_ORDER_NULLC); } }; // ************* Ternary ************* -template<> struct action < sym_colon > +template<> struct action < sym_question > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(TERNARY_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushTernaryIf(); } }; -template<> struct action < sym_question > +template<> struct action < sym_colon > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(TERNARY_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushTernaryElse(); } }; template<> struct action< ternary_tail > { template< typename Input > - static void apply(const Input& in, Stacks& stacks) { - stacks.reduceTernary(OP_TERNARY); + static void apply(const Input& in, ByteCodeAssembler& assembler) { + assembler.reduceOneJump(BC_ORDER_TERNARY_ELSE); } }; // ************* Starting parenthesis ************* -template<> struct action< sym_group_start > +template<> struct action< group_start > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(GROUP_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushGroup(); } }; @@ -258,8 +262,8 @@ template<> struct action< sym_group_start > template<> struct action< grouping > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.pop(GROUP_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.popGroup(); } }; @@ -267,205 +271,257 @@ template<> struct action< grouping > template<> struct action< resource > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - auto name = Object(in.string()); - stacks.push(Symbol(stacks.context(), std::vector{name}, "Resource")); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.loadGlobal(in.string()); // TODO: Should this be treated differently? } }; // ************* Symbol lookup ************* -template<> struct action< symbol > +template<> struct action< plain_symbol > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - auto name = Object(in.string()); - stacks.push(Symbol(stacks.context(), std::vector{name}, "Symbol")); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.loadGlobal(in.string()); } }; -// ************* Field access ************* -template<> struct action< postfix_identifier > +// ************* Array ************* +template<> struct action< array_start > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushInlineArrayStart(); + } +}; + +template<> struct action< array_end > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Object(in.string())); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushInlineArrayEnd(); } }; -template<> struct action< sym_attribute > +template<> struct action< array_comma > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(FIELD_ACCESS_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.appendInlineArrayArgument(); } }; -template<> struct action< postfix_attribute > +template<> struct action< array_list > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.reduceLR(OP_FIELD_OR_FUNCTION); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.appendInlineArrayArgument(); // Insert a fake comma } }; +// ************* Map *************** +template<> struct action< map_start > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushInlineMapStart(); + } +}; + +template<> struct action< map_end > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushInlineMapEnd(); + } +}; + +template<> struct action< map_comma > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.appendInlineMapArgument(); + } +}; + +template<> struct action< map_list > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.appendInlineMapArgument(); // Insert a fake comma + } +}; + +// ************* Field access ************* +template<> struct action< postfix_identifier > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.pushAttributeName(in.string()); + assembler.loadAttribute(); + } +}; // ************* Array access ************* template<> struct action< sym_array_access_start > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(ARRAY_ACCESS_OPERATOR); - stacks.open(); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushArrayAccessStart(); } }; template<> struct action< postfix_array_access > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.close(kCombineSingle); - stacks.reduceLR(OP_FIELD_OR_FUNCTION); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushArrayAccessEnd(); } }; // ************* Functions ************ -template<> struct action< postfix_left_paren > +template<> struct action { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(FUNCTION_OPERATOR); - stacks.open(); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushFunctionStart(); } }; -template<> struct action< postfix_function > +template<> struct action< sym_comma > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.close(kCombineVector); - stacks.reduceBinary(OP_FIELD_OR_FUNCTION); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushComma(); } }; -// *************** Data-binding group ************** - -template<> struct action< sym_dbstart > +template<> struct action< argument_list > { template< typename Input > - static void apply( const Input& in, Stacks& stacks ) { - stacks.push(DB_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushComma(); // Insert a fake comma } }; -template<> struct action< db > +template<> struct action< postfix_right_paren > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.pop(DB_OPERATOR); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushFunctionEnd(); } }; -// ************* Embedded String Handling ************** +// *************** Data-binding group ************** -template<> struct action< one<'"'> > +template<> struct action< sym_dbstart > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.open(); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.pushDBGroup(); } }; -template<> struct action< one<'\''> > +template<> struct action { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.open(); + static void apply( const Input& in, ByteCodeAssembler& assembler ) { + assembler.loadConstant(BC_CONSTANT_EMPTY_STRING); } }; -template<> struct action< ss_raw > +template<> struct action< db > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - auto s = in.string(); - if (s.length() > 0) - stacks.push(Object(s)); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.popDBGroup(); } }; -template<> struct action< ss_string > +// ************* Embedded String Handling ************** + +template<> struct action< ds_start > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.close(kCombineEmbeddedString); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.startString(); } }; -template<> struct action< key_dp > +template<> struct action< ss_start > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Dimension(stacks.popNumber())); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.startString(); } }; -template<> struct action< key_px > +template<> struct action< os_start > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Dimension(stacks.context().pxToDp(stacks.popNumber()))); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.startString(); } }; -template<> struct action< key_vh > +template<> struct action< ss_raw > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Dimension(stacks.context().vhToDp(stacks.popNumber()))); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + auto s = in.string(); + if (s.length() > 0) + assembler.addString(s); } }; -template<> struct action< key_vw > +template<> struct action< ds_raw > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.push(Dimension(stacks.context().vwToDp(stacks.popNumber()))); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + auto s = in.string(); + if (s.length() > 0) + assembler.addString(s); } }; -template<> struct action< ds_raw > +template<> struct action< os_raw > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { + static void apply( const Input& in, ByteCodeAssembler& assembler) { auto s = in.string(); if (s.length() > 0) - stacks.push(Object(s)); + assembler.addString(s); } }; -template<> struct action< ds_string > + +template<> struct action< os_string > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - stacks.close(kCombineEmbeddedString); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.endString(); } }; -template<> struct action< raw > +template<> struct action< ss_string > { template< typename Input > - static void apply( const Input& in, Stacks& stacks) { - auto s = in.string(); - if (s.length() > 0) - stacks.push(Object(s)); + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.endString(); } }; +template<> struct action< ds_string > +{ + template< typename Input > + static void apply( const Input& in, ByteCodeAssembler& assembler) { + assembler.endString(); + } +}; - /** - * \endcond - */ +/** + * \endcond + */ } // namespace datagrammar } // namespace apl -#endif // _APL_DATA_BINDING_GRAMMAR_H \ No newline at end of file +#endif // _APL_DATA_BINDING_RULES_H \ No newline at end of file diff --git a/aplcore/include/apl/datagrammar/databindingstack.h b/aplcore/include/apl/datagrammar/databindingstack.h index 1d1bf66..47430e7 100644 --- a/aplcore/include/apl/datagrammar/databindingstack.h +++ b/aplcore/include/apl/datagrammar/databindingstack.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/datagrammar/functions.h b/aplcore/include/apl/datagrammar/functions.h index a882d33..496de95 100644 --- a/aplcore/include/apl/datagrammar/functions.h +++ b/aplcore/include/apl/datagrammar/functions.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -26,38 +26,19 @@ class Object; namespace datagrammar { -// Pull these two out so we can identify them when extracting symbols from a Node. -Object ApplyArrayAccess(const std::vector& args); -Object ApplyFieldAccess(const std::vector& args); - -// Use these to construct an Object which may be a node or a calculated value -extern Object UnaryPlus(std::vector&& ); -extern Object UnaryMinus(std::vector&& ); -extern Object UnaryNot(std::vector&& ); - -extern Object Multiply(std::vector&& ); -extern Object Divide(std::vector&& ); -extern Object Remainder(std::vector&& ); -extern Object Add(std::vector&& ); -extern Object Subtract(std::vector&& ); - -extern Object LessThan(std::vector&& ); -extern Object GreaterThan(std::vector&& ); -extern Object LessEqual(std::vector&& ); -extern Object GreaterEqual(std::vector&& ); -extern Object Equal(std::vector&& ); -extern Object NotEqual(std::vector&& ); -extern Object And(std::vector&& ); -extern Object Or(std::vector&& ); -extern Object Nullc(std::vector&& ); -extern Object Ternary(std::vector&& ); - -extern Object Combine(std::vector&& ); -extern Object Symbol(const Context&, std::vector&&, const std::string& ); -extern Object FieldAccess(std::vector&& ); -extern Object ArrayAccess(std::vector&& ); -extern Object FunctionCall(std::vector&& ); - +extern Object CalculateUnaryPlus(const Object& arg); +extern Object CalculateUnaryMinus(const Object& arg); +extern Object CalculateUnaryNot(const Object& arg); +extern Object CalculateMultiply(const Object& a, const Object& b); +extern Object CalculateDivide(const Object& a, const Object& b); +extern Object CalculateRemainder(const Object& a, const Object& b); +extern Object CalculateAdd(const Object& a, const Object& b); +extern Object CalculateSubtract(const Object& a, const Object& b); +extern Object CalcFieldAccess(const Object& a, const Object& b); +extern Object CalcArrayAccess(const Object& a, const Object& b); + +extern int Compare(const Object& a, const Object& b); +extern Object MergeOp(const Object& a, const Object& b); } // datagrammar } // apl diff --git a/aplcore/include/apl/datagrammar/grammarerror.h b/aplcore/include/apl/datagrammar/grammarerror.h new file mode 100644 index 0000000..6f30ac2 --- /dev/null +++ b/aplcore/include/apl/datagrammar/grammarerror.h @@ -0,0 +1,50 @@ +/** + * 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_ERROR_H +#define _APL_GRAMMAR_ERROR_H + +#include + +namespace apl { + +enum GrammarError { + INVALID_NUMBER_FORMAT, + UNEXPECTED_TOKEN, + UNEXPECTED_TOKEN_BEFORE_EOF, + EXPECTED_OPERAND_AFTER_MULTIPLICATIVE, + EXPECTED_OPERAND_AFTER_ADDITIVE, + EXPECTED_OPERAND_AFTER_COMPARISON, + EXPECTED_OPERAND_AFTER_EQUALITY, + EXPECTED_OPERAND_AFTER_LOGICAL_AND, + EXPECTED_OPERAND_AFTER_LOGICAL_OR, + EXPECTED_OPERAND_AFTER_NULLC, + EXPECTED_EXPRESSION, + MALFORMED_ARRAY, + UNTERMINATED_SS_STRING, + UNTERMINATED_DS_STRING, + EXPECTED_MAP_ASSIGNMENT, + EXPECTED_MAP_VALUE_ASSIGNMENT, + MALFORMED_MAP, + MALFORMED_TERNARY_EXPRESSION, + EXPECTED_POSTFIX_RIGHT_PAREN, + _GRAMMAR_ERROR_COUNT // Mark end of the Grammar errors (see grammarerror.cpp) +}; + +extern std::string errorToString(GrammarError err); + +} // namespace apl + +#endif // _APL_GRAMMAR_ERROR_H diff --git a/aplcore/include/apl/datagrammar/node.h b/aplcore/include/apl/datagrammar/node.h deleted file mode 100644 index 49928e9..0000000 --- a/aplcore/include/apl/datagrammar/node.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2018 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. - * - * Node structure - */ - -#ifndef _APL_NODE_H -#define _APL_NODE_H - -#include -#include -#include -#include -#include - -#include "apl/primitives/objectdata.h" -#include "apl/utils/log.h" - -namespace apl { -namespace datagrammar { - -const bool DEBUG_NODE = false; - -/* - * Node class is used for expression evaluation - */ - -using OperatorFunc = Object (*)(const std::vector& args); - -class Node : public ObjectData -{ -public: - Node(OperatorFunc op, std::vector&& args, const std::string& name) - : mOp(op), mArgs(std::move(args)), mName(name) {} - - Object eval() const override - { - auto result = mOp(mArgs); - LOG_IF(DEBUG_NODE) << *this << " ---> " << result; - return result; - } - - std::string getSuffix() const; - - void push(const Object& ptr) - { - mArgs.push_back(ptr); - } - - void accept(Visitor& visitor) const override - { - visitor.push(); - for (auto it = mArgs.begin() ; !visitor.isAborted() && it != mArgs.end() ; it++) - it->accept(visitor); - visitor.pop(); - } - - const std::vector& args() const { return mArgs; } - std::string name() const { return mName; } - - std::string toDebugString() const override; - - friend streamer& operator<<(streamer&, const Node&); - -private: - OperatorFunc mOp; - std::vector mArgs; - std::string mName; -}; - -} // namespace datagrammar -} // namespace apl - -#endif // _APL_NODE_H \ No newline at end of file diff --git a/aplcore/include/apl/datasource/datasource.h b/aplcore/include/apl/datasource/datasource.h index bca282b..e91b148 100644 --- a/aplcore/include/apl/datasource/datasource.h +++ b/aplcore/include/apl/datasource/datasource.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -38,6 +38,7 @@ class DataSource : public LiveArrayObject { /// LiveArrayObject override void ensure(size_t idx) override; + bool isPaginating() const override { return true; } /** * Internal constructor, use create() function instead. @@ -45,13 +46,15 @@ class DataSource : public LiveArrayObject { DataSource( const LiveArrayPtr& liveArray, const ContextPtr& context, - const std::shared_ptr& connection, + const DataSourceConnectionPtr& connection, const std::string& name); std::string toDebugString() const override; + DataSourceConnectionPtr getDataSourceConnection() const override { return mSourceConnection; } + private: - std::shared_ptr mSourceConnection; + DataSourceConnectionPtr mSourceConnection; }; } // namespace apl diff --git a/aplcore/include/apl/datasource/datasourceconnection.h b/aplcore/include/apl/datasource/datasourceconnection.h index c17aca6..360ec45 100644 --- a/aplcore/include/apl/datasource/datasourceconnection.h +++ b/aplcore/include/apl/datasource/datasourceconnection.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -18,6 +18,8 @@ #include +#include "rapidjson/document.h" + #include "apl/common.h" #include "apl/utils/counter.h" @@ -47,6 +49,12 @@ class DataSourceConnection : public Counter { * @return LiveArray used as data storage for this connection. */ virtual std::shared_ptr getLiveArray() = 0; + + /** + * Retrieve datasource context as a JSON object. Should be called by RootContext->serializeDatasourceContext() + */ + virtual void serialize(rapidjson::Value& outMap, + rapidjson::Document::AllocatorType& allocator) = 0; }; } // namespace apl diff --git a/aplcore/include/apl/datasource/datasourceprovider.h b/aplcore/include/apl/datasource/datasourceprovider.h index 5651157..7f6c09f 100644 --- a/aplcore/include/apl/datasource/datasourceprovider.h +++ b/aplcore/include/apl/datasource/datasourceprovider.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/datasource/dynamicindexlistdatasourceprovider.h b/aplcore/include/apl/datasource/dynamicindexlistdatasourceprovider.h index acb4c56..7fc3b4b 100644 --- a/aplcore/include/apl/datasource/dynamicindexlistdatasourceprovider.h +++ b/aplcore/include/apl/datasource/dynamicindexlistdatasourceprovider.h @@ -164,6 +164,9 @@ class DynamicIndexListDataSourceConnection : public DynamicListDataSourceConnect */ bool changesAllowed() { return mMaxItems > 0; } + void serialize(rapidjson::Value& outMap, + rapidjson::Document::AllocatorType& allocator) override; + protected: /// Override to convert internal->source specific parameters. void fetch(size_t index, size_t count) override; @@ -205,7 +208,7 @@ class DynamicIndexListDataSourceProvider : public DynamicListDataSourceProvider, std::weak_ptr context, std::weak_ptr liveArray, const std::string& listId) override; - bool process(const Object& payload) override; + bool process(const Object& responseMap) override; /** * @internal diff --git a/aplcore/include/apl/datasource/dynamiclistdatasourceprovider.h b/aplcore/include/apl/datasource/dynamiclistdatasourceprovider.h index a3ce216..a4cca24 100644 --- a/aplcore/include/apl/datasource/dynamiclistdatasourceprovider.h +++ b/aplcore/include/apl/datasource/dynamiclistdatasourceprovider.h @@ -89,8 +89,18 @@ class DynamicListDataSourceConnection : public OffsetIndexDataSourceConnection, */ void advanceListVersion() { mListVersion++; } + /** + * @return context object + */ + std::shared_ptr getContext() { return mContext.lock(); } + protected: - void retryFetchRequest(const std::string& correlationToken); + /** + * Retry fetch request. + * @return true if request sent, false otherwise. + */ + bool retryFetchRequest(const std::string& correlationToken); + void sendFetchRequest(const ObjectMap& requestData); void clearTimeouts(const ContextPtr& context, const std::string& correlationToken); timeout_id scheduleUpdateExpiry(int version); @@ -168,7 +178,7 @@ class DynamicListDataSourceProvider : public DataSourceProvider { std::weak_ptr context, std::weak_ptr liveArray, const std::string& listId) = 0; - virtual bool process(const Object& payload) = 0; + virtual bool process(const Object& responseMap) = 0; DynamicListConfiguration mConfiguration; diff --git a/aplcore/include/apl/datasource/dynamictokenlistdatasourceprovider.h b/aplcore/include/apl/datasource/dynamictokenlistdatasourceprovider.h index 61fcc2c..8fc4617 100644 --- a/aplcore/include/apl/datasource/dynamictokenlistdatasourceprovider.h +++ b/aplcore/include/apl/datasource/dynamictokenlistdatasourceprovider.h @@ -71,6 +71,8 @@ class DynamicTokenListDataSourceConnection : public DynamicListDataSourceConnect */ void ensure(size_t index) override; + void serialize(rapidjson::Value &outMap, rapidjson::Document::AllocatorType &allocator) override; + protected: /// Override fetch not used in this class void fetch(size_t index, size_t count) override { return; }; @@ -101,7 +103,7 @@ class DynamicTokenListDataSourceProvider : public DynamicListDataSourceProvider, std::weak_ptr context, std::weak_ptr liveArray, const std::string& listId) override; - bool process(const Object& payload) override; + bool process(const Object& responseMap) override; private: bool processLazyLoadInternal(const DTLConnectionPtr& connection, const Object& responseMap); diff --git a/aplcore/include/apl/datasource/offsetindexdatasourceconnection.h b/aplcore/include/apl/datasource/offsetindexdatasourceconnection.h index bc4a1d9..f8e79f5 100644 --- a/aplcore/include/apl/datasource/offsetindexdatasourceconnection.h +++ b/aplcore/include/apl/datasource/offsetindexdatasourceconnection.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/arrayify.h b/aplcore/include/apl/engine/arrayify.h index f462aed..fb56052 100644 --- a/aplcore/include/apl/engine/arrayify.h +++ b/aplcore/include/apl/engine/arrayify.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/binding.h b/aplcore/include/apl/engine/binding.h index 8a01d10..0eab4ff 100644 --- a/aplcore/include/apl/engine/binding.h +++ b/aplcore/include/apl/engine/binding.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/builder.h b/aplcore/include/apl/engine/builder.h index b732c19..4d7a80d 100644 --- a/aplcore/include/apl/engine/builder.h +++ b/aplcore/include/apl/engine/builder.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -62,32 +62,39 @@ class Builder { void populateSingleChildLayout(const ContextPtr& context, const Object& item, const CoreComponentPtr& layout, - const Path& path); + const Path& path, + bool fullBuild); void populateLayoutComponent(const ContextPtr& context, const Object& item, const CoreComponentPtr& layout, - const Path& path); + const Path& path, + bool fullBuild); CoreComponentPtr expandLayout(const ContextPtr& context, Properties& properties, const rapidjson::Value& layout, const CoreComponentPtr& parent, - const Path& path); + const Path& path, + bool fullBuild); - void copyPreservedProperties(const CoreComponentPtr& component); + void copyPreservedBindings(const CoreComponentPtr& newComponent, const CoreComponentPtr& originalComponent); + + void copyPreservedProperties(const CoreComponentPtr& newComponent, const CoreComponentPtr& originalComponent); CoreComponentPtr expandSingleComponentFromArray(const ContextPtr& context, const std::vector& items, Properties&& properties, const CoreComponentPtr& parent, - const Path& path); + const Path& path, + bool fullBuild); CoreComponentPtr expandSingleComponent(const ContextPtr& context, const Object& item, Properties&& properties, const CoreComponentPtr& parent, - const Path& path); + const Path& path, + bool fullBuild); static void attachBindings(const ContextPtr& context, const Object& item); private: diff --git a/aplcore/include/apl/engine/componentdependant.h b/aplcore/include/apl/engine/componentdependant.h index 7607677..6516318 100644 --- a/aplcore/include/apl/engine/componentdependant.h +++ b/aplcore/include/apl/engine/componentdependant.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/context.h b/aplcore/include/apl/engine/context.h index c613923..b551268 100644 --- a/aplcore/include/apl/engine/context.h +++ b/aplcore/include/apl/engine/context.h @@ -24,6 +24,7 @@ #include #include "apl/common.h" +#include "apl/component/componentproperties.h" #include "apl/engine/contextobject.h" #include "apl/engine/jsonresource.h" #include "apl/engine/recalculatesource.h" @@ -44,15 +45,18 @@ class State; class Event; class Sequencer; class RootConfig; +class DataSourceConnection; + class FocusManager; class HoverManager; - class KeyboardManager; class LiveDataManager; class ExtensionManager; class LayoutManager; class MediaManager; +using DataSourceConnectionPtr = std::shared_ptr; + /* * The data-binding context holds information about the local environment, metrics, and resources. * Context objects should be heap-allocated with a shared pointer to their parent context. @@ -357,6 +361,16 @@ class Context : public RecalculateTarget, mMap.emplace(key, ContextObject(value).provenance(path)); } + /** + * Remove resource from the context. + * @param key The string key name + */ + void remove(const std::string& key) { + auto it = mMap.find(key); + if (it != mMap.end()) + mMap.erase(it); + } + /** * Return the provenance associated with this key. * @param key The string key name @@ -491,6 +505,16 @@ class Context : public RecalculateTarget, */ std::string getTheme() const; + /** + * @return The language as a BCP-47 string (e.g., en-US) + */ + std::string getLang() const; + + /** + * @return The layout direction + */ + LayoutDirection getLayoutDirection() const; + /** * @return The locale methods */ @@ -519,6 +543,16 @@ class Context : public RecalculateTarget, void setDirtyVisualContext(const ComponentPtr& ptr); bool isVisualContextDirty(const ComponentPtr& ptr); + /** + * Internal routine used by dynamic datasources to mark/unmark/test when the datasource context may have changed. + */ + void setDirtyDataSourceContext(const DataSourceConnectionPtr& ptr); + + /** + * @return List of pending onMount handlers for recently inflated components. + */ + WeakPtrSet& pendingOnMounts(); + void pushEvent(Event&& event); Sequencer& sequencer() const; diff --git a/aplcore/include/apl/engine/contextdependant.h b/aplcore/include/apl/engine/contextdependant.h index 6d6d2d6..3a675f0 100644 --- a/aplcore/include/apl/engine/contextdependant.h +++ b/aplcore/include/apl/engine/contextdependant.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/contextobject.h b/aplcore/include/apl/engine/contextobject.h index 564a48d..44b9ea2 100644 --- a/aplcore/include/apl/engine/contextobject.h +++ b/aplcore/include/apl/engine/contextobject.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/dependant.h b/aplcore/include/apl/engine/dependant.h index e546efc..fc6b880 100644 --- a/aplcore/include/apl/engine/dependant.h +++ b/aplcore/include/apl/engine/dependant.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/evaluate.h b/aplcore/include/apl/engine/evaluate.h index 35164ff..28b2b33 100644 --- a/aplcore/include/apl/engine/evaluate.h +++ b/aplcore/include/apl/engine/evaluate.h @@ -1,5 +1,5 @@ /** - * Copyright 2018, 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -22,34 +22,55 @@ namespace apl { +/** + * Parse a data-binding string and return the parsed expression. The returned object will + * be byte code if the string contained at least one data-binding expression and will be + * a plain string if no data-binding expressions were found + * @param context The data-binding context + * @param value The string value to evaluate + * @return The byte code or a string if no data-binding was found + */ +Object getDataBinding(const Context& context, const std::string& value); + /** * Parse a data-binding string and return the parsed expression. If the string contains * data-binding expressions referring to symbols not defined in the current context - * or symbols that have been marked as mutable, the returned object will be a Node - * containing the parse tree. + * or symbols that have been marked as mutable, the returned object will be byte code. + * If the parsed expression is constant, the returned object will be the appropriate type. + * * @param context The data-binding context * @param value The string value to evaluate - * @return The evaluated object or a Node object + * @return The evaluated object or byte code */ Object parseDataBinding(const Context& context, const std::string& value); /** - * Parse a data-binding recursively and return the parsed expressiontree. If the object contains any strings with - * data-binding expressions referring to symbols not defined in the current context or symbols that have been marked - * as mutable, the returned object will have corresponding Nodes containing the parse tree. + * Parse a data-binding recursively and return the parsed expression tree. If the object contains + * any strings with data-binding expressions referring to symbols not defined in the current context + * or symbols that have been marked as mutable, the returned object will be byte code. * @param context The data-binding context * @param object The object to evaluate - * @return The evaluated object or a Node object + * @return The evaluated object or byte code */ Object parseDataBindingRecursive(const Context& context, const Object& object); /** - * Evaluate an object applying data-binding + * Evaluate an object applying data-binding. The object or expression will be converted + * into byte code if necessary, evaluated, and resources will be substituted. + * * @param context The data-binding context. * @param object The object to evaluate. - * @return + * @return The result */ Object evaluate(const Context& context, const Object& object); + +/** + * Evaluate a string applying data-binding. The object or expression will be converted + * into byte code if necessary, evaluated, and resources will be substituted. + * @param context The data-binding context + * @param expression The string to evaluate + * @return The result + */ Object evaluate(const Context& context, const char *expression); /** diff --git a/aplcore/include/apl/engine/event.h b/aplcore/include/apl/engine/event.h index 4b189fd..8177a23 100644 --- a/aplcore/include/apl/engine/event.h +++ b/aplcore/include/apl/engine/event.h @@ -96,16 +96,6 @@ enum EventType { */ kEventTypeRequestFirstLineBounds, - /** - * Scroll a component into view. - * - * The component is the component to scroll. - * kEventPropertyPosition: The scroll position or page to change to. - * - * The server must resolve the ActionRef when the scroll is completed. - */ - kEventTypeScrollTo, - /** * Send an event to the server * @@ -117,17 +107,6 @@ enum EventType { */ kEventTypeSendEvent, - /** - * Change the page in a pager. - * - * The component is the pager. - * kEventPropertyPosition: The page to switch to (integer) - * kEventPropertyDirection: The direction to move. Either kEventDirectionForward or kEventDirectionBackward - * - * The server must resolve the ActionRef when the scroll is completed. - */ - kEventTypeSetPage, - /** * Speak a single component. * @@ -184,6 +163,7 @@ enum EventType { * @c ExperimentalFeature::kExperimentalFeatureManageMediaRequests is enabled. * * kEventPropertySource: the source URI of the requested media + * kEventPropertyMediaType: the type of media being requested * * Does not have an ActionRef * @@ -211,6 +191,7 @@ enum EventProperty { kEventPropertyExtension, kEventPropertyExtensionURI, kEventPropertyHighlightMode, + kEventPropertyMediaType, kEventPropertyName, kEventPropertyPosition, kEventPropertyReason, @@ -256,6 +237,12 @@ enum EventReason { kEventReasonExit = CommandReason::kCommandReasonExit }; +enum EventMediaType { + kEventMediaTypeImage, + kEventMediaTypeVideo, + kEventMediaTypeVectorGraphic +}; + extern Bimap sEventTypeBimap; extern Bimap sEventPropertyBimap; diff --git a/aplcore/include/apl/engine/eventsource.h b/aplcore/include/apl/engine/eventsource.h index 3cb1ebb..c71fbb3 100644 --- a/aplcore/include/apl/engine/eventsource.h +++ b/aplcore/include/apl/engine/eventsource.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/hovermanager.h b/aplcore/include/apl/engine/hovermanager.h index a4eea99..2864e18 100644 --- a/aplcore/include/apl/engine/hovermanager.h +++ b/aplcore/include/apl/engine/hovermanager.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/info.h b/aplcore/include/apl/engine/info.h index 1534bba..d8e0d0c 100644 --- a/aplcore/include/apl/engine/info.h +++ b/aplcore/include/apl/engine/info.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/jsonresource.h b/aplcore/include/apl/engine/jsonresource.h index 7752590..9a8af62 100644 --- a/aplcore/include/apl/engine/jsonresource.h +++ b/aplcore/include/apl/engine/jsonresource.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/keyboardmanager.h b/aplcore/include/apl/engine/keyboardmanager.h index 1f6828b..b11e110 100644 --- a/aplcore/include/apl/engine/keyboardmanager.h +++ b/aplcore/include/apl/engine/keyboardmanager.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/layoutmanager.h b/aplcore/include/apl/engine/layoutmanager.h index 9747acf..2eaedaf 100644 --- a/aplcore/include/apl/engine/layoutmanager.h +++ b/aplcore/include/apl/engine/layoutmanager.h @@ -85,8 +85,14 @@ class LayoutManager { /** * Layout all pending components * @param useDirtyFlag If true, updated properties will set a dirty flag in components + * @param first if true - it's a first layout for this document */ - void layout(bool useDirtyFlag); + void layout(bool useDirtyFlag, bool first = false); + + /** + * Flash any non-inflated components in hierarchy, where supported. + */ + void flushLazyInflation(); /** * Inform the layout manager of a configuration change. If the configuration change @@ -138,8 +144,14 @@ class LayoutManager { */ void addPostProcess(const CoreComponentPtr& component, PropertyKey key, const Object& value ); + /** + * Notify LayoutManager that additional processing pass required after layout. + */ + void needToReProcessLayoutChanges() { mNeedToReProcessLayoutChanges = true; } + private: - void layoutComponent(const CoreComponentPtr& component, bool useDirtyFlag); + void layoutComponent(const CoreComponentPtr& component, bool useDirtyFlag, bool first); + void flushLazyInflationInternal(const CoreComponentPtr& comp); private: using PPKey = std::pair; @@ -149,6 +161,7 @@ class LayoutManager { Size mConfiguredSize; bool mTerminated = false; bool mInLayout = false; // Guard against recursive calls to layout + bool mNeedToReProcessLayoutChanges = false; std::map mPostProcess; // Collection of elements to post-process }; diff --git a/aplcore/include/apl/engine/mediamanager.h b/aplcore/include/apl/engine/mediamanager.h deleted file mode 100644 index d42df69..0000000 --- a/aplcore/include/apl/engine/mediamanager.h +++ /dev/null @@ -1,87 +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. - */ - -#ifndef _APL_MEDIA_MANAGER_H -#define _APL_MEDIA_MANAGER_H - -#include -#include -#include -#include - -#include "apl/common.h" - -namespace apl { - -class RootContextData; -class ConfigurationChange; -using CoreComponentWPtr = std::weak_ptr; - -/** - * Media resources manager. Inflated components (based on viewport window) may request some media resources to be - * loaded. Manager will dedupe this requests and send them to runtime as an event. Runtime supposed to answer with call - * to RootContext::mediaLoaded with every source that was successfully loaded. - */ -class MediaManager { -public: - explicit MediaManager(const RootContextData& core); - - /** - * Register set of media sources behind the component. - * @param component component. - * @param sources set of sources required by this component. - * @result number of sources still required to be loaded. - */ - size_t registerComponentMedia(const CoreComponentPtr& component, const std::set& sources); - - /** - * @param source source to check. - * @return true if source loaded, false otherwise. - */ - bool isLoaded(const std::string& source) const { return mLoadedSources.count(source) > 0; } - - /** - * Go though current list of registered components and generate request to load all required sources. - */ - void processMediaRequests(); - - /** - * Notify manager about media source loaded. If this fulfills any of component requirements it will be notified - * accordingly. - * @param source source that was loaded. - */ - void mediaLoaded(const std::string& source); - - /** - * Notify manager about media source fail to load. - * @param source source that failed to load. - */ - void mediaLoadFailed(const std::string& source); - -private: - const RootContextData& mCore; - bool mComponentSetDirty = false; - - std::multimap> mComponentToSource; - std::map mPendingPerComponent; - std::set mRequestedSources; - std::set mLoadedSources; - - void noLongerPending(const std::string& source, bool failed); -}; - -} // namespace apl - -#endif // _APL_MEDIA_MANAGER_H diff --git a/aplcore/include/apl/engine/parameterarray.h b/aplcore/include/apl/engine/parameterarray.h index 0d979f2..752a5d4 100644 --- a/aplcore/include/apl/engine/parameterarray.h +++ b/aplcore/include/apl/engine/parameterarray.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/propdef.h b/aplcore/include/apl/engine/propdef.h index 6a1a5bd..9f10aeb 100644 --- a/aplcore/include/apl/engine/propdef.h +++ b/aplcore/include/apl/engine/propdef.h @@ -203,11 +203,11 @@ inline Object asDashArray(const Context& context, const Object& object) { } inline Object asStyledText(const Context& context, const Object& object) { - return StyledText::create(object); + return StyledText::create(context, object); } inline Object asFilteredText(const Context& context, const Object& object) { - return StyledText::create(object).getStyledText().getText(); + return StyledText::create(context, object).getStyledText().getText(); } inline Object asTransformOrArray(const Context& context, const Object& object) { diff --git a/aplcore/include/apl/engine/properties.h b/aplcore/include/apl/engine/properties.h index 7dc3ec5..13c2d04 100644 --- a/aplcore/include/apl/engine/properties.h +++ b/aplcore/include/apl/engine/properties.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/propertymap.h b/aplcore/include/apl/engine/propertymap.h index 5df6b80..0828dbe 100644 --- a/aplcore/include/apl/engine/propertymap.h +++ b/aplcore/include/apl/engine/propertymap.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/recalculatesource.h b/aplcore/include/apl/engine/recalculatesource.h index dcaad7c..0f9e6f5 100644 --- a/aplcore/include/apl/engine/recalculatesource.h +++ b/aplcore/include/apl/engine/recalculatesource.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/recalculatetarget.h b/aplcore/include/apl/engine/recalculatetarget.h index 10582a0..3f0a095 100644 --- a/aplcore/include/apl/engine/recalculatetarget.h +++ b/aplcore/include/apl/engine/recalculatetarget.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/rootcontext.h b/aplcore/include/apl/engine/rootcontext.h index ec23585..9a7bbf5 100644 --- a/aplcore/include/apl/engine/rootcontext.h +++ b/aplcore/include/apl/engine/rootcontext.h @@ -42,10 +42,6 @@ class RootContextData; class TimeManager; struct PointerEvent; -extern const char *ELAPSED_TIME; -extern const char *LOCAL_TIME; -extern const char *UTC_TIME; - /** * Represents a top-level APL document. * @@ -230,6 +226,24 @@ class RootContext : public std::enable_shared_from_this, */ rapidjson::Value serializeVisualContext(rapidjson::Document::AllocatorType& allocator); + /** + * Identifies when the datasource context may have changed. A call to serializeDatasourceContext resets this value to false. + * @return true if the datasource context has changed since the last call to serializeDatasourceContext, false otherwise. + */ + bool isDataSourceContextDirty() const; + + /** + * Clear the datasource context dirty flag + */ + void clearDataSourceContextDirty(); + + /** + * Retrieve datasource context as a JSON array object. This method also clears the + * datasource context dirty flag + * @param allocator Rapidjson allocator + * @return The serialized datasource context + */ + rapidjson::Value serializeDataSourceContext(rapidjson::Document::AllocatorType& allocator); /** * Execute an externally-driven command @@ -464,6 +478,7 @@ class RootContext : public std::enable_shared_from_this, ObjectMapPtr createDocumentEventProperties(const std::string& handler) const; void scheduleTickHandler(const Object& handler, double delay); void processTickHandlers(); + void clearPendingInternal(bool first) const; private: ContentPtr mContent; diff --git a/aplcore/include/apl/engine/rootcontextdata.h b/aplcore/include/apl/engine/rootcontextdata.h index d49efb9..4ea1732 100644 --- a/aplcore/include/apl/engine/rootcontextdata.h +++ b/aplcore/include/apl/engine/rootcontextdata.h @@ -24,21 +24,22 @@ #include "apl/content/metrics.h" #include "apl/content/rootconfig.h" #include "apl/content/settings.h" +#include "apl/datasource/datasourceconnection.h" #include "apl/engine/event.h" #include "apl/engine/hovermanager.h" #include "apl/engine/jsonresource.h" #include "apl/engine/keyboardmanager.h" #include "apl/engine/layoutmanager.h" -#include "apl/engine/mediamanager.h" #include "apl/engine/runtimestate.h" #include "apl/engine/styles.h" #include "apl/extension/extensionmanager.h" #include "apl/focus/focusmanager.h" #include "apl/livedata/livedatamanager.h" +#include "apl/media/mediamanager.h" +#include "apl/primitives/size.h" #include "apl/time/sequencer.h" #include "apl/touch/pointermanager.h" #include "apl/utils/counter.h" -#include "apl/primitives/size.h" namespace apl { @@ -88,7 +89,7 @@ class RootContextData : public Counter { LiveDataManager& dataManager() const { return *mDataManager; } ExtensionManager& extensionManager() const { return *mExtensionManager; } LayoutManager& layoutManager() const { return *mLayoutManager; } - MediaManager& mediaManager() const { return *mMediaManager; } + MediaManager& mediaManager() const { return *mConfig.getMediaManager(); } const YGConfigRef& ygconfig() const { return mYGConfigRef; } CoreComponentPtr top() const { return mTop; } @@ -97,6 +98,11 @@ class RootContextData : public Counter { const std::map& graphics() const { return mGraphics; } const SessionPtr& session() const { return mSession; } + RootContextData& lang(std::string lang) { mLang = lang; return *this; } + RootContextData& layoutDirection(LayoutDirection layoutDirection) { + mLayoutDirection = layoutDirection; return *this; + } + /** * @return The installed text measurement for this context. */ @@ -119,6 +125,11 @@ class RootContextData : public Counter { */ void releaseScreenLock() { mScreenLockCount--; } + /** + * @return List of pending onMount handlers for recently inflated components. + */ + WeakPtrSet& pendingOnMounts() { return mPendingOnMounts; } + public: int getPixelWidth() const { return mMetrics.getPixelHeight(); } int getPixelHeight() const { return mMetrics.getPixelHeight(); } @@ -129,11 +140,14 @@ class RootContextData : public Counter { std::string getTheme() const { return mRuntimeState.getTheme(); } std::string getRequestedAPLVersion() const { return mRuntimeState.getRequestedAPLVersion(); } + std::string getLang() const { return mLang; } + LayoutDirection getLayoutDirection() const { return mLayoutDirection; } bool getReinflationFlag() const { return mRuntimeState.getReinflation(); } std::queue events; std::set dirty; std::set dirtyVisualContext; + std::set dirtyDatasourceContext; private: RuntimeState mRuntimeState; @@ -150,7 +164,6 @@ class RootContextData : public Counter { std::unique_ptr mDataManager; std::unique_ptr mExtensionManager; std::unique_ptr mLayoutManager; - std::unique_ptr mMediaManager; YGConfigRef mYGConfigRef; TextMeasurementPtr mTextMeasurement; CoreComponentPtr mTop; // The top component @@ -158,6 +171,9 @@ class RootContextData : public Counter { int mScreenLockCount; SettingsPtr mSettings; SessionPtr mSession; + std::string mLang; + LayoutDirection mLayoutDirection; + WeakPtrSet mPendingOnMounts; }; diff --git a/aplcore/include/apl/engine/state.h b/aplcore/include/apl/engine/state.h index c22f6c3..47b77a8 100644 --- a/aplcore/include/apl/engine/state.h +++ b/aplcore/include/apl/engine/state.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/styledefinition.h b/aplcore/include/apl/engine/styledefinition.h index 8c59100..f38a91c 100644 --- a/aplcore/include/apl/engine/styledefinition.h +++ b/aplcore/include/apl/engine/styledefinition.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/engine/styleinstance.h b/aplcore/include/apl/engine/styleinstance.h index 8b716d0..4ae06f1 100644 --- a/aplcore/include/apl/engine/styleinstance.h +++ b/aplcore/include/apl/engine/styleinstance.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/extension/extensionclient.h b/aplcore/include/apl/extension/extensionclient.h index 2410447..88c2ae2 100644 --- a/aplcore/include/apl/extension/extensionclient.h +++ b/aplcore/include/apl/extension/extensionclient.h @@ -142,10 +142,15 @@ class ExtensionClient : public Counter, bool registrationMessageProcessed(); /** - * @return True if extension was sucessfully registered. False otherwise. + * @return True if extension was successfully registered. False otherwise. */ bool registered(); + /** + * @return The assigned connection token. + */ + std::string getConnectionToken() const; + /** * Process service message directed to this extension. * @param rootContext alive root context. diff --git a/aplcore/include/apl/extension/extensionmediator.h b/aplcore/include/apl/extension/extensionmediator.h index 45b6127..7b9b073 100644 --- a/aplcore/include/apl/extension/extensionmediator.h +++ b/aplcore/include/apl/extension/extensionmediator.h @@ -108,6 +108,21 @@ class ExtensionMediator: public std::enable_shared_from_this mClients.clear(); } + /** + * @return @c true if this mediator is enabled, @c false otherwise. + */ + bool isEnabled() const { return mEnabled; } + + /** + * Enables or disables this mediator. Disabled mediators will not process incoming messages. This is usefule when + * the document associated with the mediator is being backgrounded. + * + * Mediators are enabled when first created. + * + * @param enabled @c true if this mediator should become enabled, @c false if it should become disabled. + */ + void enable(bool enabled) { mEnabled = enabled; } + private: friend class RootContext; @@ -141,6 +156,8 @@ class ExtensionMediator: public std::enable_shared_from_this std::map> mClients; // executor to enqueue/sequence message processing alexaext::ExecutorPtr mMessageExecutor; + // Determines whether incoming messages from extensions should be processed. + bool mEnabled = true; }; } //namespace apl diff --git a/aplcore/include/apl/graphic/graphiccontent.h b/aplcore/include/apl/graphic/graphiccontent.h index c89b3a8..7ab2fc4 100644 --- a/aplcore/include/apl/graphic/graphiccontent.h +++ b/aplcore/include/apl/graphic/graphiccontent.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/graphic/graphicdependant.h b/aplcore/include/apl/graphic/graphicdependant.h index 3f9bbd1..519833c 100644 --- a/aplcore/include/apl/graphic/graphicdependant.h +++ b/aplcore/include/apl/graphic/graphicdependant.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/graphic/graphicproperties.h b/aplcore/include/apl/graphic/graphicproperties.h index 518dc6f..ef3ed71 100644 --- a/aplcore/include/apl/graphic/graphicproperties.h +++ b/aplcore/include/apl/graphic/graphicproperties.h @@ -27,6 +27,11 @@ enum GraphicScale { kGraphicScaleStretch = 3 }; +enum GraphicLayoutDirection { + kGraphicLayoutDirectionLTR = 0, + kGraphicLayoutDirectionRTL = 1 +}; + enum GraphicLineCap { kGraphicLineCapButt = 0, kGraphicLineCapRound = 1, @@ -60,6 +65,8 @@ enum GraphicPropertyKey { kGraphicPropertyFontWeight, kGraphicPropertyHeightActual, kGraphicPropertyHeightOriginal, + kGraphicPropertyLang, + kGraphicPropertyLayoutDirection, kGraphicPropertyLetterSpacing, kGraphicPropertyOpacity, kGraphicPropertyPathData, @@ -111,6 +118,7 @@ enum GraphicVersions { extern Bimap sGraphicScaleBimap; extern Bimap sGraphicPropertyBimap; +extern Bimap sGraphicLayoutDirectionBimap; extern Bimap sGraphicLineCapBimap; extern Bimap sGraphicLineJoinBimap; extern Bimap sGraphicTextAnchorBimap; diff --git a/aplcore/include/apl/livedata/layoutrebuilder.h b/aplcore/include/apl/livedata/layoutrebuilder.h index cae4871..95f05ba 100644 --- a/aplcore/include/apl/livedata/layoutrebuilder.h +++ b/aplcore/include/apl/livedata/layoutrebuilder.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -25,6 +25,7 @@ namespace apl { class LiveArrayObject; +using LiveArrayObjectPtr = std::shared_ptr; /** * A LayoutRebuilder updates and adjust the children of a Component if the "data" property @@ -40,6 +41,7 @@ class LayoutRebuilder : public std::enable_shared_from_this, * Construct a LayoutRebuilder and register it with the parent layout and LiveArray * @param context The data-binding context that children will be created with. * @param layout The parent layout Component + * @param old old top component for case of reinflation * @param array The DynamicArray that is controlled by the LiveArray * @param items The list of items defined in the Component * @param childPath The provenance path for the children @@ -48,8 +50,9 @@ class LayoutRebuilder : public std::enable_shared_from_this, */ static std::shared_ptr create(const ContextPtr& context, const CoreComponentPtr& layout, - const std::shared_ptr& array, - const std::vector& items, + const CoreComponentPtr& old, + const LiveArrayObjectPtr& array, + const ObjectArray& items, const Path& childPath, bool numbered); @@ -58,8 +61,9 @@ class LayoutRebuilder : public std::enable_shared_from_this, */ LayoutRebuilder(const ContextPtr& context, const CoreComponentPtr& layout, - const std::shared_ptr& array, - const std::vector& items, + const CoreComponentPtr& old, + const LiveArrayObjectPtr& array, + const ObjectArray& items, const Path& childPath, bool numbered); @@ -78,12 +82,28 @@ class LayoutRebuilder : public std::enable_shared_from_this, */ void rebuild(); + /** + * Inflate child of component associated with rebuilder if not fully inflated already. + * @param child child to inflate. + */ + void inflateIfRequired(const CoreComponentPtr& child); + /** * Notify rebuilder that particular data index is on screen. * @param idx */ void notifyItemOnScreen(int idx); + /** + * Notify rebuilder that the start edge of existing range was reached, + */ + void notifyStartEdgeReached(); + + /** + * Notify rebuilder that the end edge of existing range was reached, + */ + void notifyEndEdgeReached(); + /** * Must be called after construction to inform the LayoutRebuilder that the layout has a firstItem or lastItem * @param hasFirstItem True if the layout has a "firstItem" @@ -108,11 +128,20 @@ class LayoutRebuilder : public std::enable_shared_from_this, return mHasLastItem; } + /** + * @return array that backs this Rebuilder. May be nullptr if array is lost by any reason. + */ + std::shared_ptr getBackingArray() const { return mArray.lock(); } + +private: + ContextPtr buildBaseChildContext(const LiveArrayObjectPtr& array, size_t dataIndex, size_t insertIndex); + private: ContextPtr mContext; std::weak_ptr mLayout; + std::weak_ptr mOld; std::weak_ptr mArray; - const std::vector mItems; + const ObjectArray mItems; Path mChildPath; bool mNumbered; int mCallbackToken = -1; diff --git a/aplcore/include/apl/livedata/livearray.h b/aplcore/include/apl/livedata/livearray.h index 7f9a324..d8fa3bd 100644 --- a/aplcore/include/apl/livedata/livearray.h +++ b/aplcore/include/apl/livedata/livearray.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/livearraychange.h b/aplcore/include/apl/livedata/livearraychange.h index 19d2efa..c6eacb4 100644 --- a/aplcore/include/apl/livedata/livearraychange.h +++ b/aplcore/include/apl/livedata/livearraychange.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/livearrayobject.h b/aplcore/include/apl/livedata/livearrayobject.h index 5b24d7e..a48b72f 100644 --- a/aplcore/include/apl/livedata/livearrayobject.h +++ b/aplcore/include/apl/livedata/livearrayobject.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -59,17 +59,16 @@ class LiveArrayObject : public LiveDataObject, Counter { return std::static_pointer_cast(shared_from_this()); } - /** - * Overrides from Object::Data - */ + /// Overrides from ObjectData Object at(std::uint64_t index) const override; - virtual void ensure(size_t index) {} std::uint64_t size() const override; bool empty() const override; const std::vector& getArray() const override; void accept(Visitor& visitor) const override; std::string toDebugString() const override { return "LiveArrayObject"; } + virtual void ensure(size_t index) {} + /** * This is called from the LiveDataManager to flush all stored array changes and update the context */ @@ -88,6 +87,12 @@ class LiveArrayObject : public LiveDataObject, Counter { */ std::pair newToOld(ObjectArray::size_type index); + /** + * @return true if internal implementation may request items added or removed based on current binding state, false + * otherwise. + */ + virtual bool isPaginating() const { return false; } + private: void handleArrayMessage(const LiveArrayChange& change); diff --git a/aplcore/include/apl/livedata/livedatamanager.h b/aplcore/include/apl/livedata/livedatamanager.h index aed0ad2..bc645d7 100644 --- a/aplcore/include/apl/livedata/livedatamanager.h +++ b/aplcore/include/apl/livedata/livedatamanager.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/livedataobject.h b/aplcore/include/apl/livedata/livedataobject.h index 8c1c426..0dca3bc 100644 --- a/aplcore/include/apl/livedata/livedataobject.h +++ b/aplcore/include/apl/livedata/livedataobject.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -70,6 +70,11 @@ class LiveDataObject : public ObjectData, */ virtual std::shared_ptr asMap() { return nullptr; } + /** + * @return datasource connection pointer or nullptr if livedata object is not type of datasource + */ + virtual DataSourceConnectionPtr getDataSourceConnection() const { return nullptr; } + /** * Flush tracking changes */ diff --git a/aplcore/include/apl/livedata/livemap.h b/aplcore/include/apl/livedata/livemap.h index 72bac06..1582692 100644 --- a/aplcore/include/apl/livedata/livemap.h +++ b/aplcore/include/apl/livedata/livemap.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/livemapchange.h b/aplcore/include/apl/livedata/livemapchange.h index 84a5692..a768e82 100644 --- a/aplcore/include/apl/livedata/livemapchange.h +++ b/aplcore/include/apl/livedata/livemapchange.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/livemapobject.h b/aplcore/include/apl/livedata/livemapobject.h index 149ffc0..dacbbdf 100644 --- a/aplcore/include/apl/livedata/livemapobject.h +++ b/aplcore/include/apl/livedata/livemapobject.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/livedata/liveobject.h b/aplcore/include/apl/livedata/liveobject.h index 099afa1..ad7c816 100644 --- a/aplcore/include/apl/livedata/liveobject.h +++ b/aplcore/include/apl/livedata/liveobject.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/media/coremediamanager.h b/aplcore/include/apl/media/coremediamanager.h new file mode 100644 index 0000000..cb2af36 --- /dev/null +++ b/aplcore/include/apl/media/coremediamanager.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_CORE_MEDIA_MANAGER_H +#define _APL_CORE_MEDIA_MANAGER_H + +#include +#include +#include + +#include "apl/media/mediamanager.h" + +namespace apl { + +/** + * The core media manager pushes events onto the event queue when media objects are requested. + * + * This media manager is not thread safe and should not be used by multiple view hosts (create one per view host). + * This is the default media manager that will be instantiated in RootConfig if not overwritten. + */ +class CoreMediaManager : public MediaManager, public std::enable_shared_from_this { +public: + ~CoreMediaManager() override = default; + + MediaObjectPtr request(const std::string& url, EventMediaType type) override; + + void processMediaRequests(const ContextPtr& context) override; + + void mediaLoadComplete(const std::string& source, bool isReady) override; + + /** + * Remove a URL from the map of media objects. Note that this does not release any MediaObjects using that + * URL. It only removes it from the known map maintained by the core media manager. + * @param url The URL to remove. + */ + void removeMediaObject(const std::string& url); + +protected: + std::map> mObjectMap; + std::set, std::owner_less>> mPending; +}; + +} // namespace apl + +#endif // _APL_CORE_MEDIA_MANAGER_H diff --git a/aplcore/include/apl/media/mediamanager.h b/aplcore/include/apl/media/mediamanager.h new file mode 100644 index 0000000..f9ae655 --- /dev/null +++ b/aplcore/include/apl/media/mediamanager.h @@ -0,0 +1,65 @@ +/** + * 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_MEDIA_MANAGER_H +#define _APL_MEDIA_MANAGER_H + +#include + +#include "apl/common.h" +#include "apl/engine/event.h" + +namespace apl { + +/** + * Media resources manager. Inflated components (based on viewport window) may request some media resources to be + * loaded. Manager will dedupe this requests and send them to runtime as an event. Runtime supposed to answer with call + * to RootContext::mediaLoaded with every source that was successfully loaded. + * + * Please note that a media manager may be shared across multiple view hosts. Media managers shared across + * multiple view hosts should implement thread safety. + */ +class MediaManager { +public: + virtual ~MediaManager() = default; + + /** + * Request a media object + * @param url The source required + * @param type The type of media requested. This should be removed in the future. + * @return the media object + */ + virtual MediaObjectPtr request(const std::string& url, EventMediaType type) = 0; + + /** + * Go though current list of registered components and generate requests to load all required sources. + * This method is called from the main event loop. Override this method if your media manager needs + * to be called frequently. + * @param context The local data-binding context + */ + virtual void processMediaRequests(const ContextPtr& context) {}; + + /** + * Notify the manager about a media object which either loaded or failed to load. This method + * is not required. It is called by RootContext::mediaLoaded() and RootContext::mediaLoadFailed(). + * @param source The URL of the media object + * @param isReady True if success, false if failure + */ + virtual void mediaLoadComplete(const std::string& source, bool isReady) {}; +}; + +} // namespace apl + +#endif // _APL_MEDIA_MANAGER_H diff --git a/aplcore/include/apl/media/mediaobject.h b/aplcore/include/apl/media/mediaobject.h new file mode 100644 index 0000000..3f17b93 --- /dev/null +++ b/aplcore/include/apl/media/mediaobject.h @@ -0,0 +1,92 @@ +/** + * 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_MEDIA_OBJECT_H +#define _APL_MEDIA_OBJECT_H + +#include +#include + +#include "apl/common.h" +#include "apl/engine/event.h" +#include "apl/primitives/size.h" +#include "apl/utils/noncopyable.h" + +namespace apl { + +using MediaObjectCallback = std::function; + +/** + * An abstract base class for a media "blob" loaded from a view host which tracks the loading state + * of the media. The MediaManager returns a pointer to a subclass of MediaObject when media is + * requested. The media objects's internal state tracks if the load is pending, completed, or has failed. + * Callbacks may be attached to pending media objects to be informed when the load completes or fails. + * + * The MediaManager normally caches media objects by URL; requesting the same URL may return the same + * media object, but that behavior is not guaranteed. + * + * Releasing the shared pointer to the media object may also release the downloaded content. Components + * should hold onto the media object pointers as long as they are needed to render on-screen content. + */ +class MediaObject : NonCopyable, + public Counter { +public: + enum State { + kPending, /// The media object has not yet loaded. + kReady, /// The media object is loaded and may be displayed. + kError /// The media object failed to load and may not be displayed. + }; + + virtual ~MediaObject() = default; + + /** + * @return The URL used to load the media object + */ + virtual std::string url() const = 0; + + /** + * @return The current state of the media object. + */ + virtual State state() const = 0; + + /** + * @return The type of the media object. + */ + virtual EventMediaType type() const = 0; + + /** + * @return The size of the media object. Bitmap images and videos use pixels. Vector graphics + * return the unit-less size of the vector graphic. + */ + virtual Size size() const = 0; + + /** + * Add a callback to be executed when the MediaObject changes state from kPending to + * either kReady or kError. Multiple callbacks may be added, but the order in which + * the callbacks are executed is not guaranteed. Callbacks will be invoked on the + * main core engine thread. + * + * The callback will not be added if the MediaObject is not in the kPending state. + * + * @param callback The callback to be executed. + * @return True if the callback has been added (the MediaObject is in the kPending state). + */ + virtual bool addCallback(MediaObjectCallback callback) = 0; +}; + + +} // namespace apl + +#endif // _APL_MEDIA_OBJECT_H diff --git a/aplcore/include/apl/primitives/color.h b/aplcore/include/apl/primitives/color.h index b0f5443..c7057ef 100644 --- a/aplcore/include/apl/primitives/color.h +++ b/aplcore/include/apl/primitives/color.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/dimension.h b/aplcore/include/apl/primitives/dimension.h index edde079..7242fee 100644 --- a/aplcore/include/apl/primitives/dimension.h +++ b/aplcore/include/apl/primitives/dimension.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/filter.h b/aplcore/include/apl/primitives/filter.h index fc423cd..d3d33a2 100644 --- a/aplcore/include/apl/primitives/filter.h +++ b/aplcore/include/apl/primitives/filter.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/functions.h b/aplcore/include/apl/primitives/functions.h index f55756f..145f931 100644 --- a/aplcore/include/apl/primitives/functions.h +++ b/aplcore/include/apl/primitives/functions.h @@ -22,7 +22,7 @@ namespace apl { -using UserFunction = std::function&)>; +using UserFunction = std::function; extern void createStandardFunctions(Context& context); diff --git a/aplcore/include/apl/primitives/gradient.h b/aplcore/include/apl/primitives/gradient.h index fec5860..58f81b0 100644 --- a/aplcore/include/apl/primitives/gradient.h +++ b/aplcore/include/apl/primitives/gradient.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/keyboard.h b/aplcore/include/apl/primitives/keyboard.h index d1c9ea8..867f01f 100644 --- a/aplcore/include/apl/primitives/keyboard.h +++ b/aplcore/include/apl/primitives/keyboard.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/mediasource.h b/aplcore/include/apl/primitives/mediasource.h index 49c2f09..f93d540 100644 --- a/aplcore/include/apl/primitives/mediasource.h +++ b/aplcore/include/apl/primitives/mediasource.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/mediastate.h b/aplcore/include/apl/primitives/mediastate.h index 31d5cf7..b114a4f 100644 --- a/aplcore/include/apl/primitives/mediastate.h +++ b/aplcore/include/apl/primitives/mediastate.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/object.h b/aplcore/include/apl/primitives/object.h index 8f5a62d..77a2743 100644 --- a/aplcore/include/apl/primitives/object.h +++ b/aplcore/include/apl/primitives/object.h @@ -26,7 +26,6 @@ * String * Gradient * MediaSource - * Node * JSONObject * JSONArray * IOptArray @@ -59,8 +58,8 @@ namespace apl { namespace datagrammar { - class Node; class BoundSymbol; + class ByteCode; } class Object; @@ -96,7 +95,7 @@ using SharedVectorPtr = ObjectArrayPtr; /** * A single Object which can hold a variety of types. * Most objects are of type null, boolean, number, or string. They all fit within - * this basic object. Other possibilities include nodes (for expression evaluate), + * this basic object. Other possibilities include byte code (for expression evaluate), * maps (for context and for JSONObject) and arrays (vectors or JSONArray). * * To avoid dynamic casting, the base object has methods for manipulating all of these @@ -123,11 +122,11 @@ using SharedVectorPtr = ObjectArrayPtr; * - Styled Text * - 2D Transformations * - Bound Symbol + * - ByteCode * * The mutable types are: * - Vector graphic * - Generalized transformation - * - Node */ class Object { @@ -139,7 +138,7 @@ class Object kStringType, kArrayType, kMapType, - kNodeType, + kByteCodeType, kFunctionType, kAbsoluteDimensionType, kRelativeDimensionType, @@ -178,7 +177,7 @@ class Object Object(double d); Object(const char *s); Object(const std::string& s); - Object(const std::shared_ptr& n); + 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); @@ -244,10 +243,11 @@ class Object bool isArray() const { return mType == kArrayType; } bool isMap() const { return mType == kMapType || mType == kComponentType || mType == kContextType; } bool isTrueMap() const { return mType == kMapType; } - bool isNode() const { return mType == kNodeType; } bool isBoundSymbol() const { return mType == kBoundSymbolType; } - bool isEvaluable() const { return mType == kNodeType || 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; } @@ -304,8 +304,8 @@ class Object std::shared_ptr getFunction() const; std::shared_ptr getBoundSymbol() const; std::shared_ptr getLiveDataObject() const; - std::shared_ptr getNode() const; std::shared_ptr getAccessibilityAction() const; + std::shared_ptr getByteCode() const; const ObjectMap& getMap() const; ObjectMap& getMutableMap(); @@ -346,13 +346,13 @@ class Object // Mutable objects bool isMutable() const; - // NODE & BoundSymbol objects + // BoundSymbol, and compiled ByteCodeInstruction objects Object eval() const; - // NODE & BoundSymbol objects + // BoundSymbol, and compiled ByteCodeInstruction objects bool isPure() const; - // NODE & BoundSymbol: Add any symbols defined by this node to the "symbols" set + // BoundSymbol, ByteCode: Add any symbols defined by this node to the "symbols" set void symbols(SymbolReferenceMap& symbols) const; // FUNCTION & Easing objects diff --git a/aplcore/include/apl/primitives/objectbag.h b/aplcore/include/apl/primitives/objectbag.h index 9c6e946..a70bc83 100644 --- a/aplcore/include/apl/primitives/objectbag.h +++ b/aplcore/include/apl/primitives/objectbag.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/objectdata.h b/aplcore/include/apl/primitives/objectdata.h index e6ce3e4..feec8ca 100644 --- a/aplcore/include/apl/primitives/objectdata.h +++ b/aplcore/include/apl/primitives/objectdata.h @@ -92,7 +92,7 @@ class ObjectData : public NonCopyable { * @param args The arguments to pass. * @return The returned value or NULL. */ - virtual Object call(const ObjectArray &args) const { return Object::NULL_OBJECT(); } + virtual Object call(const ObjectArray& args) const { return Object::NULL_OBJECT(); } /** * Accept a visitor pattern to iterate over the object. diff --git a/aplcore/include/apl/primitives/point.h b/aplcore/include/apl/primitives/point.h index 32e8e89..a891ed5 100644 --- a/aplcore/include/apl/primitives/point.h +++ b/aplcore/include/apl/primitives/point.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/radii.h b/aplcore/include/apl/primitives/radii.h index 99d6624..5af64ac 100644 --- a/aplcore/include/apl/primitives/radii.h +++ b/aplcore/include/apl/primitives/radii.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/rect.h b/aplcore/include/apl/primitives/rect.h index 04c9b84..546a5f8 100644 --- a/aplcore/include/apl/primitives/rect.h +++ b/aplcore/include/apl/primitives/rect.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -146,6 +146,16 @@ class Rect */ Point getTopLeft() const { return {mX, mY}; } + /** + * @return The top-right corner as a point. + */ + Point getTopRight() const { return {mX + mWidth, mY}; } + + /** + * @return The bottom-left corner as a point + */ + Point getBottomLeft() const { return {mX, mY + mHeight}; } + /** * @return The bottom-right corner as a point */ diff --git a/aplcore/include/apl/primitives/styledtext.h b/aplcore/include/apl/primitives/styledtext.h index 4ff2c8f..0c7336d 100644 --- a/aplcore/include/apl/primitives/styledtext.h +++ b/aplcore/include/apl/primitives/styledtext.h @@ -48,23 +48,77 @@ class StyledText { kSpanTypeSuperscript = 6, kSpanTypeSubscript = 7, kSpanTypeNoBreak = 8, + kSpanTypeSpan = 9, }; /** - * Representation of text style span. + * Limited set of APL supported span attributes. */ - struct Span { + enum SpanAttributeName { + kSpanAttributeNameColor = 0, + kSpanAttributeNameFontSize = 1, + }; + + struct SpanAttribute { /** - * @param start style span starting index. - * @param type style type. + * Name of span attribute. */ - Span(size_t start, SpanType type) : type(type), start(start), end(start) {} + SpanAttributeName name; /** - * Copy constructor for emplace to vector to work with clang. - * @param obj object to copy. + * Name of span attribute. */ - Span(const Span& obj) : type(obj.type), start(obj.start), end(obj.end) {}; + Object value; + + /** + * Compare two span attributes for equality. + * @param rhs The other span attribute. + * @return True if they are equal. + */ + bool operator==(const SpanAttribute& rhs) const { + return name == rhs.name && value == rhs.value; + } + + /** + * Compare two span attributes for inequality. + * @param rhs The other span attribute. + * @return True if they are not equal. + */ + bool operator!=(const SpanAttribute& rhs) const { + return name != rhs.name || value != rhs.value; + } + }; + + /** + * Representation of text style span. + */ + struct Span { + /** + * @param start style span starting index. + * @param type style type. + * @param attributes style attributes. + */ + Span(size_t start, SpanType type, const std::map& attributeMap) : + type(type), + start(start), + end(start) { + if (attributeMap.empty()) return; + attributes = std::vector(); + for (const auto& kv : attributeMap) + attributes.push_back(SpanAttribute{kv.first, kv.second}); + }; + + /** + * @param start style span starting index. + * @param type style type. + */ + Span(size_t start, SpanType type) : type(type), start(start), end(start) {}; + + /** + * Copy constructor for emplace to vector to work with clang. + * @param obj object to copy. + */ + Span(const Span& obj) : type(obj.type), start(obj.start), end(obj.end), attributes(obj.attributes) {}; /** * Type of span. @@ -81,13 +135,18 @@ class StyledText { */ size_t end; + /** + * Span attributes. + */ + std::vector attributes; + /** * Compare two spans for equality. * @param rhs The other span. * @return True if they are equal. */ bool operator==(const Span& rhs) const { - return type == rhs.type && start == rhs.start && end == rhs.end; + return type == rhs.type && start == rhs.start && end == rhs.end && attributes == rhs.attributes; } /** @@ -96,7 +155,7 @@ class StyledText { * @return True if they are not equal */ bool operator!=(const Span& rhs) const { - return type != rhs.type || start != rhs.start || end != rhs.end; + return type != rhs.type || start != rhs.start || end != rhs.end || attributes != rhs.attributes; } }; @@ -120,6 +179,8 @@ class StyledText { SpanType getSpanType() const; + std::vector getSpanAttributes() const { return mSpanAttributes; }; + std::string getString() const { return mString; }; private: @@ -129,6 +190,7 @@ class StyledText { size_t codePointCount; std::stack mStack; SpanType mSpanType = (SpanType) START; + std::vector mSpanAttributes; std::string mString; int mCurrentStrPos = 0; int mSpanIndex = 0; @@ -136,10 +198,11 @@ class StyledText { /** * Build StyledText from object. + * @param context The data-binding context. * @param object The source of the text. * @return An object containing a StyledText or null. */ - static Object create(const Object& object); + static Object create(const Context& context, const Object& object); /** * Empty styled text object. Useful as default value. @@ -181,7 +244,7 @@ class StyledText { bool operator==(const StyledText& rhs) const { return mRawText == rhs.mRawText; } - StyledText(const std::string& raw); + StyledText(const Context& context, const std::string& raw); private: StyledText() {} diff --git a/aplcore/include/apl/primitives/styledtextstate.h b/aplcore/include/apl/primitives/styledtextstate.h index 72b25c4..c6fe419 100644 --- a/aplcore/include/apl/primitives/styledtextstate.h +++ b/aplcore/include/apl/primitives/styledtextstate.h @@ -16,6 +16,7 @@ #ifndef _APL_STYLED_TEXT_STATE_H #define _APL_STYLED_TEXT_STATE_H +#include "apl/engine/context.h" #include "apl/primitives/styledtext.h" #include @@ -29,7 +30,10 @@ namespace apl { */ class StyledTextState { public: - StyledTextState() = default; + /** + * @param context The data-binding context. + */ + StyledTextState(const Context& context) : mContext(context) {}; /** * Append text to raw text "container". @@ -42,6 +46,16 @@ class StyledTextState { */ void space(); + /** + * @param attributeName style attributeName. + */ + void attributeName(const std::string& attributeName); + + /** + * @param attributeValue style attributeName. + */ + void attributeValue(const std::string& attributeValue); + /** * @param tag style tag. */ @@ -95,10 +109,13 @@ class StyledTextState { std::string& getText() { return mText; } private: + const Context& mContext; std::stack mBuildStack; std::map mOpenedSpans; std::vector mSpans; std::string mText; + std::map mCurrentAttributeMap; + std::string mCurrentAttributeName; /** * All span offsets are codepoint offsets. Note that the number of bytes per codepoint depends on the string encoding used. */ @@ -134,6 +151,8 @@ class StyledTextState { return (a.type < b.type); } } spanComparator; + + void emplaceAttribute(const std::string& value); }; } // namespace apl diff --git a/aplcore/include/apl/primitives/symbolreferencemap.h b/aplcore/include/apl/primitives/symbolreferencemap.h index f921b60..3dc3e95 100644 --- a/aplcore/include/apl/primitives/symbolreferencemap.h +++ b/aplcore/include/apl/primitives/symbolreferencemap.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/timefunctions.h b/aplcore/include/apl/primitives/timefunctions.h index c13eaf7..3a0abe1 100644 --- a/aplcore/include/apl/primitives/timefunctions.h +++ b/aplcore/include/apl/primitives/timefunctions.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/timegrammar.h b/aplcore/include/apl/primitives/timegrammar.h index b870b8f..cb98438 100644 --- a/aplcore/include/apl/primitives/timegrammar.h +++ b/aplcore/include/apl/primitives/timegrammar.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/primitives/transform.h b/aplcore/include/apl/primitives/transform.h index 795295d..e3ce08d 100644 --- a/aplcore/include/apl/primitives/transform.h +++ b/aplcore/include/apl/primitives/transform.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/scaling/scalingcalculator.h b/aplcore/include/apl/scaling/scalingcalculator.h index 3d12b83..5d47d33 100644 --- a/aplcore/include/apl/scaling/scalingcalculator.h +++ b/aplcore/include/apl/scaling/scalingcalculator.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/time/sequencer.h b/aplcore/include/apl/time/sequencer.h index 6b60e6f..112cab2 100644 --- a/aplcore/include/apl/time/sequencer.h +++ b/aplcore/include/apl/time/sequencer.h @@ -19,7 +19,6 @@ #include "apl/utils/counter.h" #include "apl/action/action.h" #include "apl/engine/context.h" -#include "apl/component/corecomponent.h" #include "apl/command/command.h" #include "apl/time/executionresource.h" #include "apl/time/executionresourceholder.h" diff --git a/aplcore/include/apl/time/timers.h b/aplcore/include/apl/time/timers.h index c57bb2d..74e9f25 100644 --- a/aplcore/include/apl/time/timers.h +++ b/aplcore/include/apl/time/timers.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/touch/gesture.h b/aplcore/include/apl/touch/gesture.h index a17d71c..f5ec848 100644 --- a/aplcore/include/apl/touch/gesture.h +++ b/aplcore/include/apl/touch/gesture.h @@ -20,6 +20,7 @@ #include "apl/touch/pointerevent.h" #include "apl/touch/gesturestep.h" #include "apl/utils/bimap.h" +#include "apl/utils/counter.h" #include "apl/utils/noncopyable.h" namespace apl { @@ -45,7 +46,8 @@ extern Bimap sGestureTypeBimap; * one of them is triggered. After that triggered gesture considered active and will consume all events until * finished/reset. */ -class Gesture : public NonCopyable { +class Gesture : public Counter, + public NonCopyable { public: static std::shared_ptr create(const ActionablePtr& actionable, const Object& object); diff --git a/aplcore/include/apl/touch/gestures/pagerflinggesture.h b/aplcore/include/apl/touch/gestures/pagerflinggesture.h index 89dfb9e..fd9559e 100644 --- a/aplcore/include/apl/touch/gestures/pagerflinggesture.h +++ b/aplcore/include/apl/touch/gestures/pagerflinggesture.h @@ -54,6 +54,7 @@ class PagerFlingGesture : public FlingGesture, public std::enable_shared_from_th float mLastAnimationAmount; // Last amount that was successfully applied to animation transition float mAmount; std::shared_ptr mResourceHolder; + LayoutDirection mLayoutDirection; }; } // namespace apl diff --git a/aplcore/include/apl/touch/gestures/swipeawaygesture.h b/aplcore/include/apl/touch/gestures/swipeawaygesture.h index 3b8c9c8..67b1894 100644 --- a/aplcore/include/apl/touch/gestures/swipeawaygesture.h +++ b/aplcore/include/apl/touch/gestures/swipeawaygesture.h @@ -64,6 +64,7 @@ class SwipeAwayGesture : public FlingGesture, public std::enable_shared_from_thi SwipeAwayActionType mAction; SwipeDirection mDirection; + SwipeDirection mDirectionAssigned; Object mOnSwipeMove; Object mOnSwipeDone; Object mItems; diff --git a/aplcore/include/apl/utils/pagemovehandler.h b/aplcore/include/apl/touch/utils/pagemovehandler.h similarity index 92% rename from aplcore/include/apl/utils/pagemovehandler.h rename to aplcore/include/apl/touch/utils/pagemovehandler.h index 677fb3a..fbe30c4 100644 --- a/aplcore/include/apl/utils/pagemovehandler.h +++ b/aplcore/include/apl/touch/utils/pagemovehandler.h @@ -67,10 +67,12 @@ class PageMoveHandler { Object&& commands, PageMoveDrawOrder drawOrder, SwipeDirection swipeDirection, + PageDirection pageDirection, int currentPage, int targetPage); PageMoveHandler( SwipeDirection swipeDirection, + PageDirection pageDirection, int currentPage, int targetPage, ITPtr&& mCurrentPageTransform, @@ -88,6 +90,12 @@ class PageMoveHandler { */ void execute(const CoreComponentPtr& component, float amount); + /** + * Reset state of affected pages. + * @param component parent pager component. + */ + void reset(const CoreComponent& component); + PageMoveDrawOrder getDrawOrder() const { return mDrawOrder; } int getCurrentPage() const { return mCurrentPage; } int getTargetPage() const { return mTargetPage; } @@ -98,6 +106,7 @@ class PageMoveHandler { static ContextPtr createPageMoveContext( float amount, SwipeDirection direction, + PageDirection pageDirection, const CoreComponentPtr& self, const CoreComponentPtr& currentChild, const CoreComponentPtr& nextChild); @@ -113,6 +122,7 @@ class PageMoveHandler { Object mCommands; PageMoveDrawOrder mDrawOrder; SwipeDirection mSwipeDirection; + PageDirection mPageDirection; int mCurrentPage; int mTargetPage; @@ -123,4 +133,4 @@ class PageMoveHandler { } // namespace apl -#endif //_APL_PAGE_MOVE_HANDLER_H \ No newline at end of file +#endif //_APL_PAGE_MOVE_HANDLER_H diff --git a/aplcore/include/apl/utils/bimap.h b/aplcore/include/apl/utils/bimap.h index c4dc25c..8d409bb 100644 --- a/aplcore/include/apl/utils/bimap.h +++ b/aplcore/include/apl/utils/bimap.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/counter.h b/aplcore/include/apl/utils/counter.h index fabeea5..6f80346 100644 --- a/aplcore/include/apl/utils/counter.h +++ b/aplcore/include/apl/utils/counter.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/dump_object.h b/aplcore/include/apl/utils/dump_object.h index fa64462..46f3026 100644 --- a/aplcore/include/apl/utils/dump_object.h +++ b/aplcore/include/apl/utils/dump_object.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/log.h b/aplcore/include/apl/utils/log.h index 59ed245..6993cbc 100644 --- a/aplcore/include/apl/utils/log.h +++ b/aplcore/include/apl/utils/log.h @@ -1,5 +1,5 @@ /* - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/path.h b/aplcore/include/apl/utils/path.h index 4b68dea..de2ce08 100644 --- a/aplcore/include/apl/utils/path.h +++ b/aplcore/include/apl/utils/path.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/random.h b/aplcore/include/apl/utils/random.h new file mode 100644 index 0000000..aff2556 --- /dev/null +++ b/aplcore/include/apl/utils/random.h @@ -0,0 +1,45 @@ +/** + * 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_RANDOM_H +#define _APL_RANDOM_H + +#include + +namespace apl { + +/** + * Utilities for random generators. + */ +class Random +{ +public: + + /** + * @return 32-bit Mersenne Twister random number generator. + */ + static std::mt19937 + mt32Generator() + { + static std::random_device random_device; + static std::mt19937 generator(random_device()); + return generator; + } + +}; + +} // namespace apl + +#endif // _APL_RANDOM_H diff --git a/aplcore/include/apl/utils/range.h b/aplcore/include/apl/utils/range.h index 5b79b96..fab37b8 100644 --- a/aplcore/include/apl/utils/range.h +++ b/aplcore/include/apl/utils/range.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/session.h b/aplcore/include/apl/utils/session.h index 8b00ca4..6e5cd84 100644 --- a/aplcore/include/apl/utils/session.h +++ b/aplcore/include/apl/utils/session.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -79,6 +79,8 @@ class SessionMessage { SessionMessage(const Context& context, const char *filename, const char *function); + SessionMessage(const std::weak_ptr& contextPtr, const char *filename, const char *function); + ~SessionMessage(); template friend SessionMessage& operator<<(SessionMessage&& sm, T&& value) @@ -111,7 +113,7 @@ class SessionMessage { SessionMessage& log(const char *format, ...); private: - const SessionPtr mSession; + SessionPtr mSession; std::string mFilename; std::string mFunction; @@ -128,7 +130,6 @@ class SessionMessage { /// Report content errors using a context object (which contains a session) #define CONSOLE_CTX(CONTEXT) SessionMessage(CONTEXT,__FILENAME__,__func__) - } // namespace apl #endif // _APL_SESSION_H diff --git a/aplcore/include/apl/utils/stickychildrentree.h b/aplcore/include/apl/utils/stickychildrentree.h new file mode 100644 index 0000000..891a03d --- /dev/null +++ b/aplcore/include/apl/utils/stickychildrentree.h @@ -0,0 +1,69 @@ +/** + * 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_STICKYCHILDRENTREE_H +#define APL_STICKYCHILDRENTREE_H + +#include "apl/common.h" +#include "apl/component/corecomponent.h" + +namespace apl { + +class StickyNode; +using StickyChildrenTreePtr = std::shared_ptr; +/** + * Used by scrollable components to keep track of descendants with position: sticky. To calculate + * nested sticky components need to keep a tree of sticky descendants so that we can update them + * in order from ancestor to descendant. + */ +class StickyChildrenTree { +public: + StickyChildrenTree(CoreComponent &scrollable); + + /** + * Handle when a components position type is set to 'sticky' + */ + void handleChildStickySet(); + + /** + * Handle when a components position type is set from 'sticky' to something else + */ + void handleChildStickyUnset(); + + /** + * When a child is inserted we must check the child and it's descendants to see if there + * is any sticky components we need to add to our tree of sticky components + */ + void handleChildInsert(const CoreComponentPtr& component); + + /** + * When a child is removed we must update our tree of sticky components in case a sticky + * component has been removed + */ + void handleChildRemove(); + + /** + * Recalculate and update the sticky offsets applied to all the sticky components in this tree + */ + void updateStickyOffsets(); + +private: + std::shared_ptr mRoot; + CoreComponent& mScrollable;//The scrollable that contains this tree +}; + +} // namespace apl + +#endif // APL_STICKYCHILDRENTREE_H \ No newline at end of file diff --git a/aplcore/include/apl/utils/stickyfunctions.h b/aplcore/include/apl/utils/stickyfunctions.h new file mode 100644 index 0000000..86d200c --- /dev/null +++ b/aplcore/include/apl/utils/stickyfunctions.h @@ -0,0 +1,55 @@ +/** + * 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_STICKYFUNCTIONS_H +#define APL_STICKYFUNCTIONS_H + +#include "apl/common.h" +#include "apl/component/component.h" +#include "apl/primitives/point.h" + +namespace apl { + +/** + * Helper functions for position: sticky + */ +namespace stickyfunctions { + +/** + * Handle when a components position type is set from 'sticky' to something else + */ +void restoreComponentInsets(const CoreComponentPtr &component); + +/** + * Calculate and apply the left/top offset for a component with position: sticky + */ +void updateStickyOffset(const CoreComponentPtr &component); + +/** + * @return The offset which should be applied to this component if it has position: sticky + */ +Point calculateStickyOffset(const CoreComponentPtr &component); + +/** + * Traverse up the ancestors and find the nearest horizontal and vertical scrollables if they exist + */ +std::pair +getAncestorHorizontalAndVerticalScrollable(const CoreComponentPtr &component); + +} // namespace stickyfunctions + +} // namespace apl + +#endif //APL_STICKYFUNCTIONS_H diff --git a/aplcore/include/apl/utils/streamer.h b/aplcore/include/apl/utils/streamer.h index 28128dc..cdb44c6 100644 --- a/aplcore/include/apl/utils/streamer.h +++ b/aplcore/include/apl/utils/streamer.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -55,6 +55,11 @@ class streamer { return *this; } + streamer& operator<<(char __c) { + mString += __c; + return *this; + } + streamer& operator<<(std::string& __s) { mString += __s; return *this; diff --git a/aplcore/include/apl/utils/telemetry.h b/aplcore/include/apl/utils/telemetry.h deleted file mode 100644 index 7a15930..0000000 --- a/aplcore/include/apl/utils/telemetry.h +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2019 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_TELEMETRY_H -#define _APL_TELEMETRY_H - -#ifdef WITH_TELEMETRY -#include -#include -#include -#include -#include -#include "rapidjson/document.h" -#include "rapidjson/writer.h" - -namespace apl { - -class Telemetry { -public: - /** - * Create default root telemetry object. - * - * @return Shared pointer to created Telemetry object. - */ - static std::shared_ptr create(); - - /** - * Create child telemetry object. - * - * @return Shared pointer to created Telemetry object. - */ - std::shared_ptr create(const std::string &name); - - /** - * Constructor. - * @param name Telemetry object name. - */ - Telemetry(const std::string &name) : mName{name} {}; - - /** - * Delete default constructor. - */ - Telemetry() = delete; - - /** - * Retrieve current telemetry state. - * - * @return JSON formatted string. - */ - std::string retrieve(); - - /** - * Reset current telemetry state. Current object can be reused but all child objects will be removed. - */ - void release(); - - /** - * Add time metric. Output name will have ".Time" suffix. Subsequent calls with the same name will add time - * up to existing metric. - * - * @param name Metric name. - * @param ms Time in milliseconds. - */ - void addTime(const std::string &name, uint32_t ms); - - /** - * Start time metric. Should be followed up with @see Telemetry::endTime(name). Subsequent calls of start/stop - * pair with the same name will add time up to existing metric. - * - * @param name Metric name. - */ - void startTime(const std::string &name); - - /** - * Finalize and record time metric. - * - * @param name Metric name. Should be same as used for @see Telemetry::startTime(name) - */ - void endTime(const std::string &name); - - /** - * Add counter. Subsequent calls with the same name will add counter up to existing metric. - * - * @param name Metric name - * @param count Counter to be added. - */ - void addCounter(const std::string &name, uint32_t count); - - /** - * Add arbitrary data to the telemetry object. Subsequent calls with the same name will overwrite existing metric. - * - * @param name Field name. - * @param data Arbitrary data as string. - */ - void addData(const std::string &name, const std::string &data); - -private: - const std::string mName; - std::map mCounters; - std::map> mTimers; - std::map mCounts; - std::map mMetadata; - - std::vector> mChildren; - - void collect(rapidjson::Writer* writer); -}; - -} // namespace apl - -#define TELEMETRY(code) code - -#else // WITH_TELEMETRY - -#define TELEMETRY(code) - -#endif // WITH_TELEMETRY - -#endif // _APL_TELEMETRY_H diff --git a/aplcore/include/apl/utils/tracing.h b/aplcore/include/apl/utils/tracing.h new file mode 100644 index 0000000..2477e91 --- /dev/null +++ b/aplcore/include/apl/utils/tracing.h @@ -0,0 +1,95 @@ +/** + * 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_TRACING_H +#define APL_TRACING_H + +#include + +/** + * This file defines macros to enable tracing viewhost activity: + * + * APL_TRACE_BEGIN(NAME) / APL_TRACE_END(NAME) : used to mark the beginning and end of an activity. These need + * to always appear in pairs with matching names. Only C-style + * strings can be passed in as arguments (typically a string + * literal, e.g. APL_TRACE_BEGIN("myInterestingTask"). + * APL_TRACE_BLOCK(NAME) : used to register a tracepoint for an entire block (e.g. a C++ function). The tracepoint + * will begin from the macro location, and automatically end when the block is exited. + * This macro accepts a @c std::string parameter. + * + * When Trace support is disabled, these macros are noops. + */ + +#ifdef ENABLE_TRACING + +#define APL_TRACE_BEGIN(NAME) Tracing::beginSection(NAME) +#define APL_TRACE_END(NAME) Tracing::endSection(NAME) +#define APL_TRACE_BLOCK(NAME) apl::TraceBlock _apl_trace_block(NAME) + +#else + +#define APL_TRACE_BEGIN(NAME) +#define APL_TRACE_END(NAME) +#define APL_TRACE_BLOCK(NAME) + +#endif + +namespace apl { + +/** + * Support class to handle tracing libraries. + */ +class Tracing { +public: + static void beginSection(const char *sectionName); + static void endSection(const char *sectionName); + +private: + static void initialize(); + +private: + static bool mSupported; + static bool mInitialized; +}; + +/** + * Convenience class to add begin/end tracepoints to any block. It relies on the lifetime of a local object + * to start and stop the instrumentation (i.e., RAII-style). + */ +class TraceBlock { +public: + /** + * Constructor. Begins the specified tracepoint on allocation. + * @param name The name of the tracepoint controlled by this instance. + */ + explicit TraceBlock(const std::string& name) + : mName(name) { + APL_TRACE_BEGIN(mName.c_str()); + } + + /** + * Destructor. Ends the stored tracepoint on deallocation. + */ + ~TraceBlock() { + APL_TRACE_END(mName.c_str()); + } + +private: + std::string mName; +}; + +} // namespace apl + +#endif //APL_TRACING_H \ No newline at end of file diff --git a/aplcore/include/apl/utils/userdata.h b/aplcore/include/apl/utils/userdata.h index ffa23f2..47fe78f 100644 --- a/aplcore/include/apl/utils/userdata.h +++ b/aplcore/include/apl/utils/userdata.h @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/include/apl/utils/visitor.h b/aplcore/include/apl/utils/visitor.h index 43c2b82..b94265c 100644 --- a/aplcore/include/apl/utils/visitor.h +++ b/aplcore/include/apl/utils/visitor.h @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/action/animatedscrollaction.cpp b/aplcore/src/action/animatedscrollaction.cpp index f38de86..c05be6f 100644 --- a/aplcore/src/action/animatedscrollaction.cpp +++ b/aplcore/src/action/animatedscrollaction.cpp @@ -24,8 +24,6 @@ namespace apl { -static const bool DEBUG_SCROLL = false; - AnimatedScrollAction::AnimatedScrollAction(const TimersPtr& timers, const ContextPtr& context, const CoreComponentPtr& container, @@ -39,34 +37,29 @@ AnimatedScrollAction::AnimatedScrollAction(const TimersPtr& timers, } void -AnimatedScrollAction::scroll(bool vertical, const Point& position) { - if (mContext->getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) { - if (isTerminated()) - return; +AnimatedScrollAction::scroll(bool vertical, const Point& position) +{ + if (isTerminated()) + return; - // Ensure that it doesn't scroll if don't need to. - if (mContainer->scrollPosition() == position) { - resolve(); - return; - } + // Ensure that it doesn't scroll if don't need to. + if (mContainer->scrollPosition() == position) { + resolve(); + return; + } - mScroller = AutoScroller::make(mContext->getRootConfig(), - std::dynamic_pointer_cast(mContainer), - []() {}, - position - mContainer->scrollPosition(), - mDuration); - advance(); - } else { - EventBag bag; - bag.emplace(kEventPropertyPosition, Dimension(vertical ? position.getY() : position.getX())); - LOG_IF(DEBUG_SCROLL) << "Pushing scroll event position=" << position; - mContext->pushEvent(Event(kEventTypeScrollTo, std::move(bag), mContainer, shared_from_this())); - } + mScroller = AutoScroller::make(mContext->getRootConfig(), + std::dynamic_pointer_cast(mContainer), + []() {}, + position - mContainer->scrollPosition(), + mDuration); + advance(); } void -AnimatedScrollAction::advance() { +AnimatedScrollAction::advance() +{ std::weak_ptr weak_ptr(std::static_pointer_cast(shared_from_this())); mCurrentAction = Action::makeAnimation(timers(), mScroller->getDuration(), [weak_ptr](apl_duration_t offset) { diff --git a/aplcore/src/action/animateitemaction.cpp b/aplcore/src/action/animateitemaction.cpp index 03d0c76..6c89511 100644 --- a/aplcore/src/action/animateitemaction.cpp +++ b/aplcore/src/action/animateitemaction.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/action/documentaction.cpp b/aplcore/src/action/documentaction.cpp index deb614c..9b8af9b 100644 --- a/aplcore/src/action/documentaction.cpp +++ b/aplcore/src/action/documentaction.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/action/extensioneventaction.cpp b/aplcore/src/action/extensioneventaction.cpp index 8503c9d..dbcaa3f 100644 --- a/aplcore/src/action/extensioneventaction.cpp +++ b/aplcore/src/action/extensioneventaction.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/action/resourceholdingaction.cpp b/aplcore/src/action/resourceholdingaction.cpp index dd54075..7ff1b83 100644 --- a/aplcore/src/action/resourceholdingaction.cpp +++ b/aplcore/src/action/resourceholdingaction.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/action/scrollaction.cpp b/aplcore/src/action/scrollaction.cpp index 1d09394..9327cd1 100644 --- a/aplcore/src/action/scrollaction.cpp +++ b/aplcore/src/action/scrollaction.cpp @@ -65,6 +65,11 @@ ScrollAction::start() { else if (mTargetDistance.isAbsoluteDimension()) distance = mTargetDistance.getAbsoluteDimension(); + bool isRTL = mContainer->getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL; + if (!vertical && isRTL) { + distance *= -1.0f; + } + // Calculate the new position by trimming the old position plus the distance auto position = mContainer->trimScroll(mContainer->scrollPosition() + Point(distance, distance)); diff --git a/aplcore/src/action/scrolltoaction.cpp b/aplcore/src/action/scrolltoaction.cpp index 50225f4..660e6b3 100644 --- a/aplcore/src/action/scrolltoaction.cpp +++ b/aplcore/src/action/scrolltoaction.cpp @@ -28,7 +28,7 @@ ScrollToAction::ScrollToAction(const TimersPtr& timers, const Rect& subBounds, const ContextPtr& context, bool scrollToSubBounds, - const ComponentPtr& target, + const CoreComponentPtr& target, const CoreComponentPtr& scrollableParent, apl_duration_t duration) : AnimatedScrollAction(timers, context, scrollableParent, duration), @@ -42,9 +42,9 @@ std::shared_ptr ScrollToAction::make(const TimersPtr& timers, const std::shared_ptr& command, const Rect& subBounds, - const ComponentPtr& target) + const CoreComponentPtr& target) { - auto t = target ? target : std::static_pointer_cast(command->target()); + auto t = target ? target : command->target(); if (!t) return nullptr; auto align = static_cast(command->getValue(kCommandPropertyAlign).getInteger()); @@ -54,9 +54,9 @@ ScrollToAction::make(const TimersPtr& timers, std::shared_ptr ScrollToAction::make(const TimersPtr& timers, const std::shared_ptr& command, - const ComponentPtr& target) + const CoreComponentPtr& target) { - auto t = target ? target : std::static_pointer_cast(command->target()); + auto t = target ? target : command->target(); if (!t) return nullptr; auto align = static_cast(command->getValue(kCommandPropertyAlign).getInteger()); @@ -65,7 +65,7 @@ ScrollToAction::make(const TimersPtr& timers, std::shared_ptr ScrollToAction::makeUsingSnap(const TimersPtr& timers, - const ComponentPtr& target, + const CoreComponentPtr& target, apl_duration_t duration) { return make(timers, kCommandScrollAlignVisible, Rect(), target->getContext(), false, @@ -77,7 +77,7 @@ ScrollToAction::make(const TimersPtr& timers, const CommandScrollAlign& align, const Rect& subBounds, const ContextPtr& context, - const ComponentPtr& target) { + const CoreComponentPtr& target) { return make(timers, align, subBounds, context, true, target); } @@ -87,7 +87,7 @@ ScrollToAction::make(const TimersPtr& timers, const Rect& subBounds, const ContextPtr& context, bool scrollToSubBounds, - const ComponentPtr& target, + const CoreComponentPtr& target, apl_duration_t duration, bool useSnap) { if (!target) @@ -136,7 +136,7 @@ ScrollToAction::make(const TimersPtr& timers, scrollToSubBounds, target, std::dynamic_pointer_cast(container), - duration ? duration : context->getRootConfig().getScrollCommandDuration()); + duration > 0 ? duration : context->getRootConfig().getScrollCommandDuration()); context->sequencer().claimResource({kExecutionResourcePosition, container}, ptr); @@ -147,7 +147,7 @@ ScrollToAction::make(const TimersPtr& timers, void ScrollToAction::start() { // Find a scrollable or page-able parent - mTarget->ensureLayout(true); + mContainer->ensureChildLayout(mTarget, true); switch (mContainer->scrollType()) { case kScrollTypeNone: @@ -190,10 +190,12 @@ ScrollToAction::scrollTo() Rect parentInnerBounds = mContainer->getCalculated(kPropertyInnerBounds).getRect(); bool vertical = (mContainer->scrollType() == kScrollTypeVertical); + bool isLTR = mContainer->getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; float parentStart, parentEnd; float childStart, childEnd; float scrollTo; + bool beforeParentStart, afterParentEnd; if (vertical) { parentStart = parentInnerBounds.getTop(); @@ -201,12 +203,24 @@ ScrollToAction::scrollTo() childStart = childBoundsInParent.getTop(); childEnd = childBoundsInParent.getBottom(); scrollTo = mContainer->scrollPosition().getY(); - } else { + beforeParentStart = childStart - scrollTo < parentStart; + afterParentEnd = childEnd - scrollTo > parentEnd; + } else if (isLTR) { // Horizontal LTR parentStart = parentInnerBounds.getLeft(); parentEnd = parentInnerBounds.getRight(); childStart = childBoundsInParent.getLeft(); childEnd = childBoundsInParent.getRight(); scrollTo = mContainer->scrollPosition().getX(); + beforeParentStart = childStart - scrollTo < parentStart; + afterParentEnd = childEnd - scrollTo > parentEnd; + } else { // Horizontal RTL + parentStart = parentInnerBounds.getRight(); + parentEnd = parentInnerBounds.getLeft(); + childStart = childBoundsInParent.getRight(); + childEnd = childBoundsInParent.getLeft(); + scrollTo = mContainer->scrollPosition().getX(); + beforeParentStart = childStart - scrollTo > parentStart; + afterParentEnd = childEnd - scrollTo < parentEnd; } LOG_IF(DEBUG_SCROLL_TO) << "parent start=" << parentStart << " end=" << parentEnd; @@ -227,9 +241,9 @@ ScrollToAction::scrollTo() break; case kCommandScrollAlignVisible: - if (childStart - scrollTo < parentStart) + if (beforeParentStart) scrollTo = childStart - parentStart; - else if (childEnd - scrollTo > parentEnd) + else if (afterParentEnd) scrollTo = childEnd - parentEnd; break; } @@ -252,7 +266,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 = component->getParent(); + component = std::static_pointer_cast(component->getParent()); int targetPage = -1; for (int i = 0 ; i < mContainer->getChildCount() ; i++) { diff --git a/aplcore/src/action/setpageaction.cpp b/aplcore/src/action/setpageaction.cpp index 87cf03f..72b91fd 100644 --- a/aplcore/src/action/setpageaction.cpp +++ b/aplcore/src/action/setpageaction.cpp @@ -35,7 +35,7 @@ inline int modulus(int a, int b) SetPageAction::SetPageAction(const TimersPtr& timers, const std::shared_ptr& command, - const ComponentPtr& target) + const CoreComponentPtr& target) : ResourceHoldingAction(timers, command->context()), mCommand(command), mTarget(target) @@ -61,8 +61,6 @@ SetPageAction::make(const TimersPtr& timers, void SetPageAction::start() { - mTarget->ensureLayout(true); - auto position = static_cast(mCommand->getValue(kCommandPropertyPosition).asInt()); auto value = mCommand->getValue(kCommandPropertyValue).asInt(); int len = mTarget->getChildCount(); @@ -101,7 +99,7 @@ SetPageAction::start() resolve(); } else { - mTarget->getChildAt(index)->ensureLayout(true); + mTarget->ensureChildLayout(mTarget->getCoreChildAt(index), true); PagerComponent::setPageUtil(mContext, mTarget, index, direction, shared_from_this(), position == kCommandPositionAbsolute || mContext->getRequestedAPLVersion().compare("1.6") < 0); } diff --git a/aplcore/src/action/speaklistaction.cpp b/aplcore/src/action/speaklistaction.cpp index db2ebbc..c4e51cb 100644 --- a/aplcore/src/action/speaklistaction.cpp +++ b/aplcore/src/action/speaklistaction.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -27,7 +27,7 @@ SpeakListAction::make(const TimersPtr& timers, auto container = command->target(); auto start = command->getValue(kCommandPropertyStart).asInt(); auto count = command->getValue(kCommandPropertyCount).asInt(); - int len = container->getChildCount(); + auto len = static_cast(container->getChildCount()); // Sanity checks if (len <= 0 || count <= 0 || start >= len) diff --git a/aplcore/src/animation/animatedproperty.cpp b/aplcore/src/animation/animatedproperty.cpp index b19cefa..3920a90 100644 --- a/aplcore/src/animation/animatedproperty.cpp +++ b/aplcore/src/animation/animatedproperty.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/animation/easing.cpp b/aplcore/src/animation/easing.cpp index 985a2fb..1f8bd53 100644 --- a/aplcore/src/animation/easing.cpp +++ b/aplcore/src/animation/easing.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/autopagecommand.cpp b/aplcore/src/command/autopagecommand.cpp index 2f7ebd0..2f0e2e0 100644 --- a/aplcore/src/command/autopagecommand.cpp +++ b/aplcore/src/command/autopagecommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/commandfactory.cpp b/aplcore/src/command/commandfactory.cpp index a94c1d9..a7d9014 100644 --- a/aplcore/src/command/commandfactory.cpp +++ b/aplcore/src/command/commandfactory.cpp @@ -39,15 +39,17 @@ void CommandFactory::reset() { mCommandMap.clear(); - auto it = sCommandNameBimap.beginBtoA(); - for (; it != sCommandNameBimap.endBtoA(); ++it) - mCommandMap.emplace(it->first, sCommandCreatorMap.at(it->second)); + for (auto it = sCommandNameBimap.beginBtoA() ; it != sCommandNameBimap.endBtoA() ; ++it) { + auto fptr = sCommandCreatorMap.find(it->second); + if (fptr != sCommandCreatorMap.end()) + mCommandMap.emplace(it->first, fptr->second); + } } CommandFactory& CommandFactory::set(const char *name, CommandFunc func) { - mCommandMap[name] = func; // Allow overrwriting + mCommandMap[name] = func; // Allow over-writing return *this; } diff --git a/aplcore/src/command/corecommand.cpp b/aplcore/src/command/corecommand.cpp index 3282729..1872e87 100644 --- a/aplcore/src/command/corecommand.cpp +++ b/aplcore/src/command/corecommand.cpp @@ -44,6 +44,8 @@ #include "apl/command/finishcommand.h" #include "apl/command/reinflatecommand.h" +#include "apl/engine/layoutmanager.h" + namespace apl { const static bool DEBUG_COMMAND_VALUES = false; @@ -173,14 +175,22 @@ CoreCommand::calculateProperties() auto cpd = propDefSet().find(kCommandPropertyComponentId); if (cpd != propDefSet().end()) { auto p = mProperties.find(cpd->second.names); + std::string id; if (p != mProperties.end()) { - std::string id = evaluate(*mContext, p->second).asString(); + id = evaluate(*mContext, p->second).asString(); mTarget = std::dynamic_pointer_cast(mContext->findComponentById(id)); } if (mTarget == nullptr) { - CONSOLE_CTP(mContext) << "Illegal command - need to specify a target componentId"; - return false; + // TODO: Try full inflation, we may be missing deep component inflated. Quite inefficient, especially if done + // in onMount, revisit. + auto& lm = mContext->layoutManager(); + lm.flushLazyInflation(); + mTarget = std::dynamic_pointer_cast(mContext->findComponentById(id)); + if (mTarget == nullptr) { + CONSOLE_CTP(mContext) << "Illegal command - need to specify a target componentId"; + return false; + } } mValues.emplace(kCommandPropertyComponentId, mTarget->getUniqueId()); diff --git a/aplcore/src/command/extensioneventcommand.cpp b/aplcore/src/command/extensioneventcommand.cpp index 1643fe6..921d34b 100644 --- a/aplcore/src/command/extensioneventcommand.cpp +++ b/aplcore/src/command/extensioneventcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/finishcommand.cpp b/aplcore/src/command/finishcommand.cpp index 5ab3fc7..30b6c4d 100644 --- a/aplcore/src/command/finishcommand.cpp +++ b/aplcore/src/command/finishcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/openurlcommand.cpp b/aplcore/src/command/openurlcommand.cpp index bba80e9..e0b3dad 100644 --- a/aplcore/src/command/openurlcommand.cpp +++ b/aplcore/src/command/openurlcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/scrolltocomponentcommand.cpp b/aplcore/src/command/scrolltocomponentcommand.cpp index bb1f4c3..8c666ee 100644 --- a/aplcore/src/command/scrolltocomponentcommand.cpp +++ b/aplcore/src/command/scrolltocomponentcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/scrolltoindexcommand.cpp b/aplcore/src/command/scrolltoindexcommand.cpp index d12e6a7..e239e96 100644 --- a/aplcore/src/command/scrolltoindexcommand.cpp +++ b/aplcore/src/command/scrolltoindexcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/sendeventcommand.cpp b/aplcore/src/command/sendeventcommand.cpp index bf6ba14..711fb56 100644 --- a/aplcore/src/command/sendeventcommand.cpp +++ b/aplcore/src/command/sendeventcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/sequentialcommand.cpp b/aplcore/src/command/sequentialcommand.cpp index 78d9c78..22f8b11 100644 --- a/aplcore/src/command/sequentialcommand.cpp +++ b/aplcore/src/command/sequentialcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/setpagecommand.cpp b/aplcore/src/command/setpagecommand.cpp index 3954f83..9027062 100644 --- a/aplcore/src/command/setpagecommand.cpp +++ b/aplcore/src/command/setpagecommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/setvaluecommand.cpp b/aplcore/src/command/setvaluecommand.cpp index d351abe..a5a2d26 100644 --- a/aplcore/src/command/setvaluecommand.cpp +++ b/aplcore/src/command/setvaluecommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/speakitemcommand.cpp b/aplcore/src/command/speakitemcommand.cpp index 30d1633..4cb41b8 100644 --- a/aplcore/src/command/speakitemcommand.cpp +++ b/aplcore/src/command/speakitemcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/command/speaklistcommand.cpp b/aplcore/src/command/speaklistcommand.cpp index ea778aa..40a9e66 100644 --- a/aplcore/src/command/speaklistcommand.cpp +++ b/aplcore/src/command/speaklistcommand.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/component/actionablecomponent.cpp b/aplcore/src/component/actionablecomponent.cpp index 0eb7201..7fd593b 100644 --- a/aplcore/src/component/actionablecomponent.cpp +++ b/aplcore/src/component/actionablecomponent.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -85,32 +85,30 @@ ActionableComponent::executeKeyHandlers(KeyHandlerType type, const Keyboard& key bool ActionableComponent::executeIntrinsicKeyHandlers(KeyHandlerType type, const Keyboard& keyboard) { - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleFocusInCore)) { - // TODO: Do we need to allow SpatialNav to be disabled by runtime configuration? - if (type == kKeyDown) { - auto it = keyboardToFocusDirection().find(keyboard); - if (it != keyboardToFocusDirection().end()) { - // We consume the key, but don't perform any action as one is already in progress. - if (mContext->sequencer().isRunning(FOCUS_SEQUENCER)) - return true; - - auto focusDirection = it->second; - auto& fm = getContext()->focusManager(); - auto nextFocus = getUserSpecifiedNextFocus(focusDirection); - if (!nextFocus) - nextFocus = takeFocusFromChild(focusDirection, Rect()); - - if (nextFocus) { - // If returned self - it's already processed. Just ignore. - if (nextFocus != shared_from_this()) { - fm.setFocus(nextFocus, true); - } - } else { - // "Default" processing - means navigate out of component. - fm.focus(focusDirection); - } + // TODO: Do we need to allow SpatialNav to be disabled by runtime configuration? + if (type == kKeyDown) { + auto it = keyboardToFocusDirection().find(keyboard); + if (it != keyboardToFocusDirection().end()) { + // We consume the key, but don't perform any action as one is already in progress. + if (mContext->sequencer().isRunning(FOCUS_SEQUENCER)) return true; + + auto focusDirection = it->second; + auto& fm = getContext()->focusManager(); + auto nextFocus = getUserSpecifiedNextFocus(focusDirection); + if (!nextFocus) + nextFocus = takeFocusFromChild(focusDirection, Rect()); + + if (nextFocus) { + // If returned self - it's already processed. Just ignore. + if (nextFocus != shared_from_this()) { + fm.setFocus(nextFocus, true); + } + } else { + // "Default" processing - means navigate out of component. + fm.focus(focusDirection); } + return true; } } diff --git a/aplcore/src/component/componentproperties.cpp b/aplcore/src/component/componentproperties.cpp index 2d25832..f45eb22 100644 --- a/aplcore/src/component/componentproperties.cpp +++ b/aplcore/src/component/componentproperties.cpp @@ -67,7 +67,9 @@ Bimap sTextAlignMap = { {kTextAlignAuto, "auto"}, {kTextAlignLeft, "left"}, {kTextAlignCenter, "center"}, - {kTextAlignRight, "right"} + {kTextAlignRight, "right"}, + {kTextAlignStart, "start"}, + {kTextAlignEnd, "end"} }; Bimap sTextAlignVerticalMap = { @@ -122,7 +124,8 @@ Bimap sNumberingMap = { Bimap sPositionMap = { {kPositionAbsolute, "absolute"}, - {kPositionRelative, "relative"} + {kPositionRelative, "relative"}, + {kPositionSticky, "sticky"} }; Bimap sScrollDirectionMap = { @@ -251,6 +254,8 @@ Bimap sSwipeDirectionMap = { {kSwipeDirectionLeft, "left"}, {kSwipeDirectionDown, "down"}, {kSwipeDirectionRight, "right"}, + {kSwipeDirectionForward, "forward"}, + {kSwipeDirectionBackward, "backward"}, }; Bimap sScrollAnimationMap = { @@ -258,6 +263,17 @@ Bimap sScrollAnimationMap = { {kScrollAnimationSmoothInOut, "smoothInOut"} }; +Bimap sLayoutDirectionMap = { + {kLayoutDirectionInherit, "inherit"}, + {kLayoutDirectionLTR, "LTR"}, + {kLayoutDirectionRTL, "RTL"} +}; + +Bimap sKeyboardBehaviorOnFocusMap = { + {kBehaviorOnFocusSystemDefault, "systemDefault"}, + {kBehaviorOnFocusOpenKeyboard, "openKeyboard"} +}; + /** * Map between property names and property constants. * @@ -307,6 +323,7 @@ Bimap sComponentPropertyBimap = { {kPropertyDisabled, "disabled"}, {kPropertyDisplay, "display"}, {kPropertyDrawnBorderWidth, "_drawnBorderWidth"}, + {kPropertyEnd, "end"}, {kPropertyEntities, "entities"}, {kPropertyFastScrollScale, "-fastScrollScale"}, {kPropertyFilters, "filters"}, @@ -336,7 +353,11 @@ Bimap sComponentPropertyBimap = { {kPropertyInnerBounds, "_innerBounds"}, {kPropertyItemsPerCourse, "_itemsPerCourse"}, {kPropertyJustifyContent, "justifyContent"}, + {kPropertyKeyboardBehaviorOnFocus, "-keyboardBehaviorOnFocus"}, {kPropertyKeyboardType, "keyboardType"}, + {kPropertyLang, "lang"}, + {kPropertyLayoutDirection, "_layoutDirection"}, + {kPropertyLayoutDirectionAssigned, "layoutDirection"}, {kPropertyLeft, "left"}, {kPropertyLetterSpacing, "letterSpacing"}, {kPropertyLineHeight, "lineHeight"}, @@ -381,8 +402,10 @@ Bimap sComponentPropertyBimap = { {kPropertyOverlayGradient, "overlayGradient"}, {kPropertyPadding, "padding"}, {kPropertyPaddingBottom, "paddingBottom"}, + {kPropertyPaddingEnd, "paddingEnd"}, {kPropertyPaddingLeft, "paddingLeft"}, {kPropertyPaddingRight, "paddingRight"}, + {kPropertyPaddingStart, "paddingStart"}, {kPropertyPaddingTop, "paddingTop"}, {kPropertyPageDirection, "pageDirection"}, {kPropertyPageId, "pageId"}, @@ -412,8 +435,10 @@ Bimap sComponentPropertyBimap = { {kPropertySpacing, "spacing"}, {kPropertySpeech, "speech"}, {kPropertySubmitKeyType, "submitKeyType"}, + {kPropertyStart, "start"}, {kPropertyText, "text"}, - {kPropertyTextAlign, "textAlign"}, + {kPropertyTextAlign, "_textAlign"}, + {kPropertyTextAlignAssigned, "textAlign"}, {kPropertyTextAlignVertical, "textAlignVertical"}, {kPropertyTop, "top"}, {kPropertyTrackCount, "_trackCount"}, diff --git a/aplcore/src/component/containercomponent.cpp b/aplcore/src/component/containercomponent.cpp index bee078a..b743534 100644 --- a/aplcore/src/component/containercomponent.cpp +++ b/aplcore/src/component/containercomponent.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -22,7 +22,7 @@ namespace apl { CoreComponentPtr ContainerComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -31,13 +31,13 @@ ContainerComponent::create(const ContextPtr& context, ContainerComponent::ContainerComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : CoreComponent(context, std::move(properties), path) { } void -ContainerComponent::processLayoutChanges(bool useDirtyFlag) { +ContainerComponent::processLayoutChanges(bool useDirtyFlag, bool first) { // TODO: FIX THIS - IT CAN CHANGE THE SIZE OF THE CONTAINER // Quite brute-forcy to do that, though it will handle any strange changes to layout direction in the future. if (!mChildren.empty()) { @@ -46,7 +46,7 @@ ContainerComponent::processLayoutChanges(bool useDirtyFlag) { } } - CoreComponent::processLayoutChanges(useDirtyFlag); + CoreComponent::processLayoutChanges(useDirtyFlag, first); } const ComponentPropDefSet& @@ -69,16 +69,18 @@ ContainerComponent::layoutPropDefSet() const { // TODO: Break these into two sets so we don't calculate everything all of the time. static ComponentPropDefSet sContainerChildProperties = ComponentPropDefSet().add({ - {kPropertyAlignSelf, kFlexboxAlignAuto, sFlexboxAlignMap, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setAlignSelf }, - {kPropertyBottom, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, - {kPropertyGrow, 0, asNonNegativeNumber, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPropertyGrow }, - {kPropertyLeft, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, - {kPropertyNumbering, kNumberingNormal, sNumberingMap, kPropIn }, - {kPropertyPosition, kPositionRelative, sPositionMap, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPositionType }, - {kPropertyRight, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, - {kPropertyShrink, 0, asNonNegativeNumber, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPropertyShrink }, - {kPropertySpacing, Dimension(0), asAbsoluteDimension, kPropIn | kPropStyled | kPropDynamic | kPropNeedsNode | kPropResetOnRemove, yn::setSpacing }, - {kPropertyTop, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition } + {kPropertyAlignSelf, kFlexboxAlignAuto, sFlexboxAlignMap, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setAlignSelf }, + {kPropertyBottom, Dimension(DimensionType::Auto, 0), asDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, + {kPropertyEnd, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, + {kPropertyGrow, 0, asNonNegativeNumber, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPropertyGrow }, + {kPropertyLeft, Dimension(DimensionType::Auto, 0), asDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, + {kPropertyNumbering, kNumberingNormal, sNumberingMap, kPropIn }, + {kPropertyPosition, kPositionRelative, sPositionMap, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPositionType }, + {kPropertyRight, Dimension(DimensionType::Auto, 0), asDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, + {kPropertyShrink, 0, asNonNegativeNumber, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPropertyShrink }, + {kPropertySpacing, Dimension(0), asAbsoluteDimension, kPropIn | kPropStyled | kPropDynamic | kPropNeedsNode | kPropResetOnRemove, yn::setSpacing }, + {kPropertyStart, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition }, + {kPropertyTop, Dimension(DimensionType::Auto, 0), asDimension, kPropIn | kPropStyled | kPropDynamic | kPropResetOnRemove, yn::setPosition } }); return &sContainerChildProperties; } diff --git a/aplcore/src/component/corecomponent.cpp b/aplcore/src/component/corecomponent.cpp index 07d01ae..00396a9 100644 --- a/aplcore/src/component/corecomponent.cpp +++ b/aplcore/src/component/corecomponent.cpp @@ -32,6 +32,8 @@ #include "apl/engine/layoutmanager.h" #include "apl/focus/focusmanager.h" #include "apl/livedata/layoutrebuilder.h" +#include "apl/livedata/livearray.h" +#include "apl/livedata/livearrayobject.h" #include "apl/primitives/accessibilityaction.h" #include "apl/primitives/keyboard.h" #include "apl/time/sequencer.h" @@ -39,6 +41,9 @@ #include "apl/touch/pointerevent.h" #include "apl/utils/searchvisitor.h" #include "apl/utils/session.h" +#include "apl/utils/stickyfunctions.h" +#include "apl/utils/stickychildrentree.h" +#include "apl/utils/tracing.h" namespace apl { @@ -52,11 +57,44 @@ const std::string VISUAL_CONTEXT_TYPE_EMPTY = "empty"; const static bool DEBUG_BOUNDS = false; const static bool DEBUG_ENSURE = false; +const static bool DEBUG_LAYOUTDIRECTION = false; const static bool DEBUG_PADDING = false; + +/** + * A helper function to zero the insets for when a component's position is change to "sticky" + */ +static void +zeroComponentInsets(const CoreComponentPtr &component) { + auto nodeRef = component->getNode(); + yn::setPosition(nodeRef, NAN, *component->getContext()); + yn::setPosition(nodeRef, NAN, *component->getContext()); + yn::setPosition(nodeRef, NAN, *component->getContext()); + yn::setPosition(nodeRef, NAN, *component->getContext()); + yn::setPosition(nodeRef, NAN, *component->getContext()); + yn::setPosition(nodeRef, NAN, *component->getContext()); + component->setDirty(kPropertyBounds); +} + +/** + * A helper function to restore the zeroed insets for when a component's position is change from "sticky" + * to something else + */ +static void +restoreComponentInsets(const CoreComponentPtr &component) { + auto nodeRef = component->getNode(); + yn::setPosition(nodeRef, component->getCalculated(kPropertyLeft), *component->getContext()); + yn::setPosition(nodeRef, component->getCalculated(kPropertyBottom), *component->getContext()); + yn::setPosition(nodeRef, component->getCalculated(kPropertyRight), *component->getContext()); + yn::setPosition(nodeRef, component->getCalculated(kPropertyTop), *component->getContext()); + yn::setPosition(nodeRef, component->getCalculated(kPropertyStart), *component->getContext()); + yn::setPosition(nodeRef, component->getCalculated(kPropertyEnd), *component->getContext()); + component->setDirty(kPropertyBounds); +} + CoreComponent::CoreComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : Component(context, properties.asLabel(*context, "id")), mInheritParentState(properties.asBoolean(*context, "inheritParentState", false)), mStyle(properties.asString(*context, "style", "")), @@ -105,6 +143,7 @@ CoreComponent::processTickHandlers() { void CoreComponent::initialize() { + APL_TRACE_BLOCK("CoreComponent:initialize"); // TODO: Would be nice to work this in with the regular properties more cleanly. mState.set(kStateChecked, mProperties.asBoolean(*mContext, "checked", false)); mState.set(kStateDisabled, mProperties.asBoolean(*mContext, "disabled", false)); @@ -145,6 +184,12 @@ CoreComponent::initialize() // Process tick handlers here. Not same as onMount as it's a bad idea to go through every component on every tick // to collect handlers and run them on mass. processTickHandlers(); + + // Add this component to the list of pending onMount elements if it has the handler. + auto commands = getCalculated(kPropertyOnMount); + if (commands.isArray() && !commands.empty()) { + mContext->pendingOnMounts().emplace(shared_from_corecomponent()); + } } void @@ -325,21 +370,26 @@ CoreComponent::attachYogaNode(const CoreComponentPtr& child) } void -CoreComponent::markDisplayedChildrenStale() { - mDisplayedChildrenStale = true; - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureNotifyChildrenChangedOnDisplayChange)) - setDirty(kPropertyNotifyChildrenChanged); -}; +CoreComponent::markDisplayedChildrenStale(bool useDirtyFlag) +{ + // Children visibility can't be stale if component can't have one. + if (multiChild() || singleChild()) { + mDisplayedChildrenStale = true; + if (useDirtyFlag) setDirty(kPropertyNotifyChildrenChanged); + } +} size_t -CoreComponent::getDisplayedChildCount() const { +CoreComponent::getDisplayedChildCount() const +{ auto& mutableThis = const_cast(*this); mutableThis.ensureDisplayedChildren(); return mDisplayedChildren.size(); } ComponentPtr -CoreComponent::getDisplayedChildAt(size_t drawIndex) const { +CoreComponent::getDisplayedChildAt(size_t drawIndex) const +{ auto& mutableThis = const_cast(*this); mutableThis.ensureDisplayedChildren(); return mDisplayedChildren.at(drawIndex); @@ -354,7 +404,8 @@ CoreComponent::isDisplayedChild(const CoreComponent& child) const } void -CoreComponent::ensureDisplayedChildren() { +CoreComponent::ensureDisplayedChildren() +{ if (!mDisplayedChildrenStale) return; @@ -371,8 +422,9 @@ CoreComponent::ensureDisplayedChildren() { Rect viewportRect = Rect(0,0,bounds.getWidth(),bounds.getHeight()); viewportRect.offset(scrollPosition()); + std::vector sticky; // Process the children, identify those displayed within local viewport - for (auto child : mChildren) { + for (auto& child : mChildren) { // only visible children if (child->isDisplayable()) { // compare child rect, transformed as needed, against the viewport @@ -391,14 +443,33 @@ CoreComponent::ensureDisplayedChildren() { childBounds.offset(childBoundsTopLeft); if (!viewportRect.intersect(childBounds).isEmpty()) { - mDisplayedChildren.emplace_back(child); + if (child->getCalculated(kPropertyPosition) == kPositionSticky) { + sticky.emplace_back(child); + } else { + mDisplayedChildren.emplace_back(child); + } } } } + // Insert the sticky elements at the end + mDisplayedChildren.insert(mDisplayedChildren.end(), sticky.begin(), sticky.end()); + mDisplayedChildrenStale = false; } +bool +CoreComponent::insertChild(const ComponentPtr& child, size_t index) +{ + return canInsertChild() && insertChild(std::dynamic_pointer_cast(child), index, true); +} + +bool +CoreComponent::appendChild(const ComponentPtr& child) +{ + return insertChild(child, mChildren.size()); +} + bool CoreComponent::insertChild(const CoreComponentPtr& child, size_t index, bool useDirtyFlag) { @@ -422,9 +493,18 @@ CoreComponent::insertChild(const CoreComponentPtr& child, size_t index, bool use coreChild->attachedToParent(shared_from_corecomponent()); coreChild->markGlobalToLocalTransformStale(); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(useDirtyFlag); setVisualContextDirty(); + // Update the position: sticky components tree + auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(coreChild); + auto horizontalScrollable = std::get<0>(p); + auto verticalScrollable = std::get<1>(p); + if (horizontalScrollable && horizontalScrollable->getStickyTree()) + horizontalScrollable->getStickyTree()->handleChildInsert(coreChild); + if (verticalScrollable && verticalScrollable->getStickyTree()) + verticalScrollable->getStickyTree()->handleChildInsert(coreChild); + return true; } @@ -465,8 +545,17 @@ CoreComponent::removeChild(const CoreComponentPtr& child, size_t index, bool use if (useDirtyFlag) notifyChildChanged(index, child->getUniqueId(), "remove"); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(useDirtyFlag); setVisualContextDirty(); + + // Update the position: sticky components tree + auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(child); + auto horizontalScrollable = std::get<0>(p); + auto verticalScrollable = std::get<1>(p); + if (horizontalScrollable && horizontalScrollable->getStickyTree()) + horizontalScrollable->getStickyTree()->handleChildRemove(); + if (verticalScrollable && verticalScrollable->getStickyTree()) + verticalScrollable->getStickyTree()->handleChildRemove(); } void @@ -528,16 +617,24 @@ CoreComponent::markAdded() } void -CoreComponent::ensureLayout(bool useDirtyFlag) +CoreComponent::ensureLayoutInternal(bool useDirtyFlag) { LOG_IF(DEBUG_ENSURE) << toDebugSimpleString() << " useDirtyFlag=" << useDirtyFlag; + APL_TRACE_BLOCK("CoreComponent:ensureLayout"); auto& lm = mContext->layoutManager(); if (lm.ensure(shared_from_corecomponent())) lm.layout(useDirtyFlag); } void -CoreComponent::reportLoaded(size_t index) { +CoreComponent::ensureChildLayout(const CoreComponentPtr& child, bool useDirtyFlag) +{ + child->ensureLayoutInternal(useDirtyFlag); +} + +void +CoreComponent::reportLoaded(size_t index) +{ if (mRebuilder) { if (mRebuilder->hasFirstItem() && index == 0) { index++; @@ -647,6 +744,17 @@ CoreComponent::assignProperties(const ComponentPropDefSet& propDefSet) mCalculated.set(pd.key, value); + if (pd.key == kPropertyPosition && value == kPositionSticky) { + zeroComponentInsets(shared_from_corecomponent()); + auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(shared_from_corecomponent()); + auto horizontalScrollable = std::get<0>(p); + auto verticalScrollable = std::get<1>(p); + if (horizontalScrollable && horizontalScrollable->getStickyTree()) + horizontalScrollable->getStickyTree()->handleChildStickySet(); + if (verticalScrollable && verticalScrollable->getStickyTree()) + verticalScrollable->getStickyTree()->handleChildStickySet(); + } + //Apply this property to the yn if we care about it if (pd.layoutFunc != nullptr) pd.layoutFunc(mYGNodeRef, value, *mContext); @@ -667,12 +775,33 @@ CoreComponent::handlePropertyChange(const ComponentPropDef& def, const Object& v if (value != previous) { mCalculated.set(def.key, value); + // Update the position: sticky components tree if needed + if (def.key == kPropertyPosition && value == kPositionSticky) { + zeroComponentInsets(shared_from_corecomponent()); + auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(shared_from_corecomponent()); + auto horizontalScrollable = std::get<0>(p); + auto verticalScrollable = std::get<1>(p); + if (horizontalScrollable && horizontalScrollable->getStickyTree()) + horizontalScrollable->getStickyTree()->handleChildStickySet(); + if (verticalScrollable && verticalScrollable->getStickyTree()) + verticalScrollable->getStickyTree()->handleChildStickySet(); + } else if (def.key == kPropertyPosition && previous == kPositionSticky) { + restoreComponentInsets(shared_from_corecomponent()); + auto p = stickyfunctions::getAncestorHorizontalAndVerticalScrollable(shared_from_corecomponent()); + auto horizontalScrollable = std::get<0>(p); + auto verticalScrollable = std::get<1>(p); + if (horizontalScrollable && horizontalScrollable->getStickyTree()) + horizontalScrollable->getStickyTree()->handleChildStickyUnset(); + if (verticalScrollable && verticalScrollable->getStickyTree()) + verticalScrollable->getStickyTree()->handleChildStickyUnset(); + } + // display change, or opacity change to/from 0, makes parent display stale if (mParent && (def.key == kPropertyDisplay ||(def.key == kPropertyOpacity && ((value.asNumber() == 0) != (previous.asNumber() == 0))))) { - mParent->markDisplayedChildrenStale(); + mParent->markDisplayedChildrenStale(true); } // Properties with the kPropVisualContext flag the visual context as dirty @@ -981,7 +1110,7 @@ CoreComponent::setState( StateProperty stateProperty, bool value ) } if (mState.set(stateProperty, value)) { - + auto self = shared_from_corecomponent(); if (stateProperty == kStateChecked || stateProperty == kStateFocused || stateProperty == kStateDisabled) { setVisualContextDirty(); @@ -991,7 +1120,14 @@ CoreComponent::setState( StateProperty stateProperty, bool value ) if (value) { mState.set(kStatePressed, false); mState.set(kStateHover, false); - mContext->focusManager().releaseFocus(shared_from_corecomponent(), true); + auto& fm = mContext->focusManager(); + if (fm.getFocus() == self) { + auto next = fm.find(kFocusDirectionForward); + if (next) + fm.setFocus(next, true); + else // If nothing suitable is found - clear focus forcefully as we don't have another choice. + fm.clearFocus(true, kFocusDirectionNone, true); + } } } @@ -1000,7 +1136,7 @@ CoreComponent::setState( StateProperty stateProperty, bool value ) if (stateProperty == kStateDisabled) { // notify the hover manager that the disable state has changed - mContext->hoverManager().componentToggledDisabled(shared_from_corecomponent()); + mContext->hoverManager().componentToggledDisabled(self); } updateStyle(); @@ -1093,27 +1229,43 @@ CoreComponent::shouldNotPropagateLayoutChanges() const } void -CoreComponent::processLayoutChanges(bool useDirtyFlag) +CoreComponent::processLayoutChanges(bool useDirtyFlag, bool first) { if (DEBUG_BOUNDS) YGNodePrint(mYGNodeRef, YGPrintOptions::YGPrintOptionsLayout); + APL_TRACE_BLOCK("CoreComponent:processLayoutChanges"); float left = YGNodeLayoutGetLeft(mYGNodeRef); float top = YGNodeLayoutGetTop(mYGNodeRef); float width = YGNodeLayoutGetWidth(mYGNodeRef); float height = YGNodeLayoutGetHeight(mYGNodeRef); + bool changed = false; + // If no bounds set - set some now to get sticky stuff a chance to calculate on the very first pass + if (first && mCalculated.get(kPropertyBounds).empty()) { + changed = true; + mCalculated.set(kPropertyBounds, Rect(left, top, width, height)); + } + + if (getCalculated(kPropertyPosition) == kPositionSticky) { + mStickyOffset = stickyfunctions::calculateStickyOffset(shared_from_corecomponent()); + left += mStickyOffset.getX(); + top += mStickyOffset.getY(); + } + // Update the transformation matrix fixTransform(useDirtyFlag); + // Update the layoutDirection. + fixLayoutDirection(useDirtyFlag); Rect rect(left, top, width, height); - bool changed = rect != mCalculated.get(kPropertyBounds).getRect(); + changed |= rect != mCalculated.get(kPropertyBounds).getRect(); if (changed) { mCalculated.set(kPropertyBounds, std::move(rect)); markGlobalToLocalTransformStale(); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(useDirtyFlag); if (mParent) - mParent->markDisplayedChildrenStale(); + mParent->markDisplayedChildrenStale(useDirtyFlag); setVisualContextDirty(); if (useDirtyFlag) setDirty(kPropertyBounds); @@ -1138,7 +1290,7 @@ CoreComponent::processLayoutChanges(bool useDirtyFlag) if (changed) { mCalculated.set(kPropertyInnerBounds, std::move(inner)); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(useDirtyFlag); if (useDirtyFlag) setDirty(kPropertyInnerBounds); } @@ -1156,7 +1308,7 @@ CoreComponent::processLayoutChanges(bool useDirtyFlag) // Note that children of a Pager are not attached, and hence they will not be processed. for (auto& child : mChildren) if (child->isAttached()) - child->processLayoutChanges(useDirtyFlag); + child->processLayoutChanges(useDirtyFlag, first); } @@ -1214,19 +1366,20 @@ CoreComponent::createKeyboardEventContext(const std::string& handler, const Obje const EventPropertyMap & CoreComponent::eventPropertyMap() const { static EventPropertyMap sCoreEventProperties( - { - {"bind", [](const CoreComponent *c) { return ContextWrapper::create(c->getContext()); }}, - {"checked", [](const CoreComponent *c) { return c->getState().get(kStateChecked); }}, - {"disabled", [](const CoreComponent *c) { return c->getState().get(kStateDisabled); }}, - {"focused", [](const CoreComponent *c) { return c->getState().get(kStateFocused); }}, - {"id", [](const CoreComponent *c) { return c->getId(); }}, - {"uid", [](const CoreComponent *c) { return c->getUniqueId(); }}, - {"width", [](const CoreComponent *c) { return YGNodeLayoutGetWidth(c->mYGNodeRef); }}, - {"height", [](const CoreComponent *c) { return YGNodeLayoutGetHeight(c->mYGNodeRef); }}, - {"opacity", [](const CoreComponent *c) { return c->getCalculated(kPropertyOpacity); }}, - {"pressed", [](const CoreComponent *c) { return c->getState().get(kStatePressed); }}, - {"type", [](const CoreComponent *c) { return sComponentTypeBimap.at(c->getType()); }} - }); + { + {"bind", [](const CoreComponent *c) { return ContextWrapper::create(c->getContext()); }}, + {"checked", [](const CoreComponent *c) { return c->getState().get(kStateChecked); }}, + {"disabled", [](const CoreComponent *c) { return c->getState().get(kStateDisabled); }}, + {"focused", [](const CoreComponent *c) { return c->getState().get(kStateFocused); }}, + {"id", [](const CoreComponent *c) { return c->getId(); }}, + {"uid", [](const CoreComponent *c) { return c->getUniqueId(); }}, + {"width", [](const CoreComponent *c) { return YGNodeLayoutGetWidth(c->mYGNodeRef); }}, + {"height", [](const CoreComponent *c) { return YGNodeLayoutGetHeight(c->mYGNodeRef); }}, + {"opacity", [](const CoreComponent *c) { return c->getCalculated(kPropertyOpacity); }}, + {"pressed", [](const CoreComponent *c) { return c->getState().get(kStatePressed); }}, + {"type", [](const CoreComponent *c) { return sComponentTypeBimap.at(c->getType()); }}, + {"layoutDirection", [](const CoreComponent *c) { return sLayoutDirectionMap.at(c->getCalculated(kPropertyLayoutDirection).asInt()); }} + }); return sCoreEventProperties; } @@ -1297,7 +1450,7 @@ CoreComponent::fixTransform(bool useDirtyFlag) markGlobalToLocalTransformStale(); // transform change make parent display stale if (mParent) { - mParent->markDisplayedChildrenStale(); + mParent->markDisplayedChildrenStale(useDirtyFlag); } setVisualContextDirty(); if (useDirtyFlag) @@ -1307,6 +1460,16 @@ CoreComponent::fixTransform(bool useDirtyFlag) } } +static bool +setPaddingIfKeyFound(PropertyKey key, YGEdge edge, CalculatedPropertyMap& map, YGNodeRef nodeRef, + const ContextPtr& context) { + auto v = map.find(key); + bool found = v != map.end() && !v->second.isNull(); + if (found) + yn::setPadding(nodeRef, edge, v->second, *context); + return found; +} + void CoreComponent::fixPadding() { @@ -1323,16 +1486,34 @@ CoreComponent::fixPadding() for (size_t i = 0 ; i < EDGES.size() ; i++) { const auto& edge = EDGES.at(i); + // That value may be overridden by the specific paddingLeft/Top/Right/Bottom values + if (!setPaddingIfKeyFound(edge.first, edge.second, mCalculated, mYGNodeRef, mContext)) { + // If edge isn't set directly use padding value assigned by the "padding" property + auto assigned = commonPadding.size() >= i ? commonPadding.at(i) : Dimension(0); + yn::setPadding(mYGNodeRef, edge.second, assigned, *mContext); + } + } - // Padding value assigned by the "padding" property - auto assigned = commonPadding.size() >= i ? commonPadding.at(i) : Dimension(0); + // paddingStart overrides left padding if layout is "ltf" or right padding if "rtl" + setPaddingIfKeyFound(kPropertyPaddingStart, YGEdgeStart, mCalculated, mYGNodeRef, mContext); - // That value may be overridden by the specific paddingLeft/Top/Right/Bottom values - auto it = mCalculated.find(edge.first); - if (it != mCalculated.end() && !it->second.isNull()) - assigned = it->second; + // paddingEnd overrides left padding if layout is "ltf" or right padding if "rtl" + setPaddingIfKeyFound(kPropertyPaddingEnd, YGEdgeEnd, mCalculated, mYGNodeRef, mContext); +} - yn::setPadding(mYGNodeRef, edge.second, assigned, *mContext); +void +CoreComponent::fixLayoutDirection(bool useDirtyFlag) +{ + LOG_IF(DEBUG_LAYOUTDIRECTION) << mCalculated.get(kPropertyLayoutDirection); + auto reportedLayoutDirection = static_cast(mCalculated.get(kPropertyLayoutDirection).asInt()); + auto currentLayoutDirection = getLayoutDirection() == YGDirectionLTR ? kLayoutDirectionLTR : kLayoutDirectionRTL; + if (reportedLayoutDirection != currentLayoutDirection) { + mCalculated.set(kPropertyLayoutDirection, currentLayoutDirection); + if (useDirtyFlag) { + setDirty(kPropertyLayoutDirection); + } + handleLayoutDirectionChange(useDirtyFlag); + LOG_IF(DEBUG_LAYOUTDIRECTION) << "updated to " << mCalculated.get(kPropertyLayoutDirection); } } @@ -1401,7 +1582,7 @@ CoreComponent::serializeAll(rapidjson::Document::AllocatorType& allocator) const component.AddMember("__id", rapidjson::Value(mId.c_str(), allocator), allocator); component.AddMember("__inheritParentState", mInheritParentState, allocator); component.AddMember("__style", rapidjson::Value(mStyle.c_str(), allocator), allocator); - component.AddMember("__path", rapidjson::Value(mPath.c_str(), allocator), allocator); + component.AddMember("__path", rapidjson::Value(mPath.toString().c_str(), allocator), allocator); for (const auto& pds : propDefSet()) { if (pds.second.map) { @@ -1535,7 +1716,8 @@ CoreComponent::serializeVisualContextInternal(rapidjson::Value &outArray, rapidj } std::string -CoreComponent::getVisualContextType() { +CoreComponent::getVisualContextType() const +{ std::string type; for(const auto& child : mChildren) { auto childType = child->getVisualContextType(); @@ -1578,7 +1760,8 @@ CoreComponent::getChildrenVisibility(float realOpacity, const Rect &visibleRect) } float -CoreComponent::calculateVisibility(float parentRealOpacity, const Rect& parentVisibleRect) { +CoreComponent::calculateVisibility(float parentRealOpacity, const Rect& parentVisibleRect) const +{ auto realOpacity = calculateRealOpacity(parentRealOpacity); if(realOpacity <= 0 || (getCalculated(kPropertyDisplay).asInt() != kDisplayNormal)) { return 0.0; @@ -1597,7 +1780,8 @@ CoreComponent::calculateVisibility(float parentRealOpacity, const Rect& parentVi } bool -CoreComponent::getTags(rapidjson::Value &outMap, rapidjson::Document::AllocatorType &allocator) { +CoreComponent::getTags(rapidjson::Value &outMap, rapidjson::Document::AllocatorType &allocator) +{ bool actionable = false; bool checked = mState.get(kStateChecked); bool disabled = mState.get(kStateDisabled); @@ -1638,12 +1822,14 @@ CoreComponent::getTags(rapidjson::Value &outMap, rapidjson::Document::AllocatorT } Rect -CoreComponent::calculateVisibleRect(const Rect& parentVisibleRect) { +CoreComponent::calculateVisibleRect(const Rect& parentVisibleRect) const +{ return getGlobalBounds().intersect(parentVisibleRect); } Rect -CoreComponent::calculateVisibleRect() { +CoreComponent::calculateVisibleRect() const +{ auto rect = getGlobalBounds(); if(!mParent) { float viewportWidth = mContext->width(); @@ -1655,13 +1841,15 @@ CoreComponent::calculateVisibleRect() { } float -CoreComponent::calculateRealOpacity(float parentRealOpacity) { +CoreComponent::calculateRealOpacity(float parentRealOpacity) const +{ auto assignedOpacity = getCalculated(kPropertyOpacity).asNumber(); return assignedOpacity * parentRealOpacity; } float -CoreComponent::calculateRealOpacity() { +CoreComponent::calculateRealOpacity() const +{ auto assignedOpacity = getCalculated(kPropertyOpacity).asNumber(); if(!mParent) { return assignedOpacity; @@ -1703,27 +1891,27 @@ CoreComponent::processKeyPress(KeyHandlerType type, const Keyboard& keyboard) { /*****************************************************************/ -inline void +static inline void inlineFixTransform(Component& component) { auto& core = dynamic_cast(component); core.fixTransform(true); } -inline void +static inline void inlineFixPadding(Component& component) { auto& core = dynamic_cast(component); core.fixPadding(); } -inline Object +static inline Object defaultWidth(Component& component, const RootConfig& rootConfig) { return rootConfig.getDefaultComponentWidth(component.getType()); } -inline Object +static inline Object defaultHeight(Component& component, const RootConfig& rootConfig) { return rootConfig.getDefaultComponentHeight(component.getType()); @@ -1738,9 +1926,16 @@ CoreComponent::fixSpacing(bool reset) { if (!parent) return; - YGEdge edge = YGNodeStyleGetFlexDirection(parent) == YGFlexDirectionColumn ? YGEdgeTop : YGEdgeLeft; + auto dir = YGNodeStyleGetFlexDirection(parent); + YGEdge edge = YGEdgeLeft; + switch (dir) { + case YGFlexDirectionColumn: edge = YGEdgeTop; break; + case YGFlexDirectionColumnReverse: edge = YGEdgeBottom; break; + case YGFlexDirectionRow: edge = YGEdgeLeft; break; + case YGFlexDirectionRowReverse: edge = YGEdgeRight; break; + } float currentValue = YGNodeStyleGetMargin(mYGNodeRef, edge).value; - if (std::isnan(currentValue) || std::abs(currentValue - spacing.getValue()) > 0.1) { + if ((std::isnan(currentValue) && spacing.getValue() != 0) || std::abs(currentValue - spacing.getValue()) > 0.1) { YGNodeStyleSetMargin(mYGNodeRef, edge, spacing.getValue()); } } @@ -1809,94 +2004,104 @@ CoreComponent::executeEventHandler(const std::string& event, const Object& comma const ComponentPropDefSet& CoreComponent::propDefSet() const { static ComponentPropDefSet sCommonComponentProperties = ComponentPropDefSet().add({ - {kPropertyAccessibilityLabel, "", asString, kPropInOut | - kPropDynamic}, - {kPropertyAccessibilityActions, Object::EMPTY_ARRAY(), asArray, kPropInOut}, - {kPropertyBounds, Object::EMPTY_RECT(), nullptr, kPropOut | - kPropVisualContext}, - {kPropertyChecked, false, asBoolean, kPropInOut | - kPropDynamic | - kPropMixedState | - kPropVisualContext}, - {kPropertyDescription, "", asString, kPropIn}, - {kPropertyDisplay, kDisplayNormal, sDisplayMap, kPropInOut | - kPropStyled | - kPropDynamic | - kPropVisualContext, yn::setDisplay}, - {kPropertyDisabled, false, asBoolean, kPropInOut | - kPropDynamic | - kPropMixedState | - kPropVisualContext}, - {kPropertyEntities, Object::EMPTY_ARRAY(), asDeepArray, kPropIn | - kPropVisualContext}, - {kPropertyFocusable, false, nullptr, kPropOut}, - {kPropertyHandleTick, Object::EMPTY_ARRAY(), asArray, kPropIn}, - {kPropertyHeight, Dimension(), asDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setHeight, defaultHeight}, - {kPropertyInnerBounds, Object::EMPTY_RECT(), nullptr, kPropOut | kPropVisualContext}, - {kPropertyMaxHeight, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setMaxHeight}, - {kPropertyMaxWidth, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setMaxWidth}, - {kPropertyMinHeight, Dimension(0), asNonAutoDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setMinHeight}, - {kPropertyMinWidth, Dimension(0), asNonAutoDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setMinWidth}, - {kPropertyOnMount, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyOpacity, 1.0, asOpacity, kPropInOut | - kPropStyled | - kPropDynamic | - kPropVisualContext}, - {kPropertyPadding, Object::EMPTY_ARRAY(), asPaddingArray, kPropIn | - kPropDynamic | - kPropStyled, inlineFixPadding}, - {kPropertyPaddingBottom, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | - kPropDynamic | - kPropStyled, inlineFixPadding}, - {kPropertyPaddingLeft, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | - kPropDynamic | - kPropStyled, inlineFixPadding}, - {kPropertyPaddingRight, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | - kPropDynamic | - kPropStyled, inlineFixPadding}, - {kPropertyPaddingTop, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | - kPropDynamic | - kPropStyled, inlineFixPadding}, - {kPropertyPreserve, Object::EMPTY_ARRAY(), asArray, kPropIn}, - {kPropertyRole, kRoleNone, sRoleMap, kPropInOut | - kPropStyled}, - {kPropertyShadowColor, Color(), asColor, kPropInOut | - kPropDynamic | - kPropStyled}, - {kPropertyShadowHorizontalOffset, Dimension(0), asAbsoluteDimension, kPropInOut | - kPropDynamic | - kPropStyled}, - {kPropertyShadowRadius, Dimension(0), asAbsoluteDimension, kPropInOut | - kPropDynamic | - kPropStyled}, - {kPropertyShadowVerticalOffset, Dimension(0), asAbsoluteDimension, kPropInOut | - kPropDynamic | - kPropStyled}, - {kPropertySpeech, "", asString, kPropIn | kPropVisualContext}, - {kPropertyTransformAssigned, Object::NULL_OBJECT(), asTransformOrArray, kPropIn | - kPropDynamic | - kPropEvaluated | - kPropVisualContext, inlineFixTransform}, - {kPropertyTransform, Object::IDENTITY_2D(), nullptr, kPropOut | - kPropVisualContext}, - {kPropertyUser, Object::NULL_OBJECT(), nullptr, kPropOut}, - {kPropertyWidth, Dimension(), asDimension, kPropIn | - kPropDynamic | - kPropStyled, yn::setWidth, defaultWidth}, - {kPropertyOnCursorEnter, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyOnCursorExit, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyLaidOut, false, asBoolean, kPropOut | - kPropVisualContext} + {kPropertyAccessibilityLabel, "", asString, kPropInOut | + kPropDynamic}, + {kPropertyAccessibilityActions, Object::EMPTY_ARRAY(), asArray, kPropInOut}, + {kPropertyBounds, Object::EMPTY_RECT(), nullptr, kPropOut | + kPropVisualContext}, + {kPropertyChecked, false, asBoolean, kPropInOut | + kPropDynamic | + kPropMixedState | + kPropVisualContext}, + {kPropertyDescription, "", asString, kPropIn}, + {kPropertyDisplay, kDisplayNormal, sDisplayMap, kPropInOut | + kPropStyled | + kPropDynamic | + kPropVisualContext, yn::setDisplay}, + {kPropertyDisabled, false, asBoolean, kPropInOut | + kPropDynamic | + kPropMixedState | + kPropVisualContext}, + {kPropertyEntities, Object::EMPTY_ARRAY(), asDeepArray, kPropIn | + kPropVisualContext}, + {kPropertyFocusable, false, nullptr, kPropOut}, + {kPropertyHandleTick, Object::EMPTY_ARRAY(), asArray, kPropIn}, + {kPropertyHeight, Dimension(), asDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setHeight, defaultHeight}, + {kPropertyInnerBounds, Object::EMPTY_RECT(), nullptr, kPropOut | kPropVisualContext}, + {kPropertyLayoutDirectionAssigned, kLayoutDirectionInherit, sLayoutDirectionMap, kPropIn | + kPropDynamic | + kPropStyled, yn::setLayoutDirection}, + {kPropertyLayoutDirection, kLayoutDirectionLTR, sLayoutDirectionMap, kPropOut}, + {kPropertyMaxHeight, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setMaxHeight}, + {kPropertyMaxWidth, Object::NULL_OBJECT(), asNonAutoDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setMaxWidth}, + {kPropertyMinHeight, Dimension(0), asNonAutoDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setMinHeight}, + {kPropertyMinWidth, Dimension(0), asNonAutoDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setMinWidth}, + {kPropertyOnMount, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyOpacity, 1.0, asOpacity, kPropInOut | + kPropStyled | + kPropDynamic | + kPropVisualContext}, + {kPropertyPadding, Object::EMPTY_ARRAY(), asPaddingArray, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingBottom, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingLeft, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingRight, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingTop, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingStart, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPaddingEnd, Object::NULL_OBJECT(), asAbsoluteDimension, kPropIn | + kPropDynamic | + kPropStyled, inlineFixPadding}, + {kPropertyPreserve, Object::EMPTY_ARRAY(), asArray, kPropIn}, + {kPropertyRole, kRoleNone, sRoleMap, kPropInOut | + kPropStyled}, + {kPropertyShadowColor, Color(), asColor, kPropInOut | + kPropDynamic | + kPropStyled}, + {kPropertyShadowHorizontalOffset, Dimension(0), asAbsoluteDimension, kPropInOut | + kPropDynamic | + kPropStyled}, + {kPropertyShadowRadius, Dimension(0), asAbsoluteDimension, kPropInOut | + kPropDynamic | + kPropStyled}, + {kPropertyShadowVerticalOffset, Dimension(0), asAbsoluteDimension, kPropInOut | + kPropDynamic | + kPropStyled}, + {kPropertySpeech, "", asString, kPropIn | kPropVisualContext}, + {kPropertyTransformAssigned, Object::NULL_OBJECT(), asTransformOrArray, kPropIn | + kPropDynamic | + kPropEvaluated | + kPropVisualContext, inlineFixTransform}, + {kPropertyTransform, Object::IDENTITY_2D(), nullptr, kPropOut | + kPropVisualContext}, + {kPropertyUser, Object::NULL_OBJECT(), nullptr, kPropOut}, + {kPropertyWidth, Dimension(), asDimension, kPropIn | + kPropDynamic | + kPropStyled, yn::setWidth, defaultWidth}, + {kPropertyOnCursorEnter, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyOnCursorExit, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyLaidOut, false, asBoolean, kPropOut | + kPropVisualContext} }); return sCommonComponentProperties; @@ -2006,10 +2211,25 @@ CoreComponent::ensureGlobalToLocalTransform() { mGlobalToLocalIsStale = false; } -const Transform2D& CoreComponent::getGlobalToLocalTransform() const { +const Transform2D& +CoreComponent::getGlobalToLocalTransform() const { auto &mutableThis = const_cast(*this); mutableThis.ensureGlobalToLocalTransform(); return mGlobalToLocal; } +YGDirection +CoreComponent::getLayoutDirection() const +{ + auto direction = YGNodeStyleGetDirection(mYGNodeRef); + if (direction == YGDirectionInherit) { + if (!mParent) + // Fallback to document level layoutDirection + return mContext->getLayoutDirection() == kLayoutDirectionRTL ? YGDirectionRTL : YGDirectionLTR; + + return mParent->getLayoutDirection(); + } + return direction; +} + } // namespace apl diff --git a/aplcore/src/component/edittextcomponent.cpp b/aplcore/src/component/edittextcomponent.cpp index 200b1d2..ab6d1fa 100644 --- a/aplcore/src/component/edittextcomponent.cpp +++ b/aplcore/src/component/edittextcomponent.cpp @@ -29,7 +29,7 @@ namespace apl { CoreComponentPtr EditTextComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -38,7 +38,7 @@ EditTextComponent::create(const ContextPtr& context, EditTextComponent::EditTextComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : ActionableComponent(context, std::move(properties), path) { YGNodeSetMeasureFunc(mYGNodeRef, textMeasureFunc); @@ -60,17 +60,22 @@ EditTextComponent::assignProperties(const ComponentPropDefSet& propDefSet) parseValidCharactersProperty(); } -inline Object defaultFontColor(Component& component, const RootConfig& rootConfig) +static inline Object defaultFontColor(Component& component, const RootConfig& rootConfig) { return Object(rootConfig.getDefaultFontColor(component.getContext()->getTheme())); } -inline Object defaultFontFamily(Component& component, const RootConfig& rootConfig) +static inline Object defaultFontFamily(Component& component, const RootConfig& rootConfig) { return Object(rootConfig.getDefaultFontFamily()); } -inline Object defaultHighlightColor(Component& component, const RootConfig& rootConfig) +static inline Object inheritLang(Component& comp, const RootConfig& rconfig) +{ + return Object(comp.getContext()->getLang()); +}; + +static inline Object defaultHighlightColor(Component& component, const RootConfig& rootConfig) { return Object(rootConfig.getDefaultHighlightColor(component.getContext()->getTheme())); } @@ -79,33 +84,35 @@ const ComponentPropDefSet& EditTextComponent::propDefSet() const { static ComponentPropDefSet sEditTextComponentProperties(ActionableComponent::propDefSet(), { - {kPropertyBorderColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertyBorderWidth, Dimension(0), asNonNegativeAbsoluteDimension, kPropInOut | kPropStyled | kPropDynamic, yn::setBorder}, - {kPropertyColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultFontColor}, - {kPropertyFontFamily, "", asString, kPropInOut | kPropLayout | kPropStyled | kPropDynamic, defaultFontFamily}, - {kPropertyFontSize, Dimension(40), asAbsoluteDimension, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, - {kPropertyFontStyle, kFontStyleNormal, sFontStyleMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, - {kPropertyFontWeight, 400, sFontWeightMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, - {kPropertyHighlightColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultHighlightColor}, - {kPropertyHint, "", asString, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertyHintColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultFontColor}, - {kPropertyHintStyle, kFontStyleNormal, sFontStyleMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, - {kPropertyHintWeight, 400, sFontWeightMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, - {kPropertyKeyboardType, kKeyboardTypeNormal, sKeyboardTypeMap, kPropInOut | kPropStyled}, - {kPropertyMaxLength, 0, asInteger, kPropInOut | kPropStyled}, - {kPropertyOnSubmit, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertyOnTextChange, Object::EMPTY_ARRAY(), asCommand, kPropIn}, - {kPropertySecureInput, false, asBoolean, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertySelectOnFocus, false, asBoolean, kPropInOut | kPropStyled}, - {kPropertySize, 8, asPositiveInteger, kPropInOut | kPropStyled | kPropLayout}, - {kPropertySubmitKeyType, kSubmitKeyTypeDone, sSubmitKeyTypeMap, kPropInOut | kPropStyled}, - {kPropertyText, "", asString, kPropInOut | kPropDynamic | kPropVisualContext}, - {kPropertyValidCharacters, "", asString, kPropIn | kPropStyled}, + {kPropertyBorderColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic}, + {kPropertyBorderWidth, Dimension(0), asNonNegativeAbsoluteDimension, kPropInOut | kPropStyled | kPropDynamic, yn::setBorder}, + {kPropertyColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultFontColor}, + {kPropertyFontFamily, "", asString, kPropInOut | kPropLayout | kPropStyled | kPropDynamic, defaultFontFamily}, + {kPropertyFontSize, Dimension(40), asAbsoluteDimension, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyFontStyle, kFontStyleNormal, sFontStyleMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyFontWeight, 400, sFontWeightMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyHighlightColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultHighlightColor}, + {kPropertyHint, "", asString, kPropInOut | kPropStyled | kPropDynamic}, + {kPropertyHintColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, defaultFontColor}, + {kPropertyHintStyle, kFontStyleNormal, sFontStyleMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyHintWeight, 400, sFontWeightMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyKeyboardType, kKeyboardTypeNormal, sKeyboardTypeMap, kPropInOut | kPropStyled}, + {kPropertyLang, "", asString, kPropInOut | kPropLayout | kPropStyled | kPropDynamic, inheritLang}, + {kPropertyMaxLength, 0, asInteger, kPropInOut | kPropStyled}, + {kPropertyOnSubmit, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertyOnTextChange, Object::EMPTY_ARRAY(), asCommand, kPropIn}, + {kPropertySecureInput, false, asBoolean, kPropInOut | kPropStyled | kPropDynamic}, + {kPropertyKeyboardBehaviorOnFocus, kBehaviorOnFocusSystemDefault, sKeyboardBehaviorOnFocusMap, kPropIn | kPropStyled}, + {kPropertySelectOnFocus, false, asBoolean, kPropInOut | kPropStyled}, + {kPropertySize, 8, asPositiveInteger, kPropInOut | kPropStyled | kPropLayout}, + {kPropertySubmitKeyType, kSubmitKeyTypeDone, sSubmitKeyTypeMap, kPropInOut | kPropStyled}, + {kPropertyText, "", asString, kPropInOut | kPropDynamic | kPropVisualContext}, + {kPropertyValidCharacters, "", asString, kPropIn | kPropStyled}, // The width of the drawn border. If borderStrokeWith is set, the drawn border is the min of borderWidth // and borderStrokeWidth. If borderStrokeWidth is unset, the drawn border defaults to borderWidth - {kPropertyBorderStrokeWidth, Object::NULL_OBJECT(), asNonNegativeAbsoluteDimension, kPropIn | kPropStyled | kPropDynamic, resolveDrawnBorder}, - {kPropertyDrawnBorderWidth, Object::NULL_OBJECT(), asNonNegativeAbsoluteDimension, kPropOut}, + {kPropertyBorderStrokeWidth, Object::NULL_OBJECT(), asNonNegativeAbsoluteDimension, kPropIn | kPropStyled | kPropDynamic, resolveDrawnBorder}, + {kPropertyDrawnBorderWidth, Object::NULL_OBJECT(), asNonNegativeAbsoluteDimension, kPropOut}, }); @@ -204,4 +211,13 @@ EditTextComponent::processPointerEvent(const PointerEvent& event, apl_time_t tim return kPointerStatusNotCaptured; } +void +EditTextComponent::executeOnFocus() { + ActionableComponent::executeOnFocus(); + + if (getCalculated(kPropertyKeyboardBehaviorOnFocus) == kBehaviorOnFocusOpenKeyboard) { + getContext()->pushEvent(Event(kEventTypeOpenKeyboard, shared_from_this())); + } +} + } // namespace apl \ No newline at end of file diff --git a/aplcore/src/component/framecomponent.cpp b/aplcore/src/component/framecomponent.cpp index 4c99d9d..619b298 100644 --- a/aplcore/src/component/framecomponent.cpp +++ b/aplcore/src/component/framecomponent.cpp @@ -29,7 +29,7 @@ static void checkBorderRadii(Component& component) CoreComponentPtr FrameComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -38,7 +38,7 @@ FrameComponent::create(const ContextPtr& context, FrameComponent::FrameComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : CoreComponent(context, std::move(properties), path) { // TODO: Auto-sized Frame just wraps the children. Fix this for ScrollView and other containers? diff --git a/aplcore/src/component/gridsequencecomponent.cpp b/aplcore/src/component/gridsequencecomponent.cpp index c81a9c4..b153ddd 100644 --- a/aplcore/src/component/gridsequencecomponent.cpp +++ b/aplcore/src/component/gridsequencecomponent.cpp @@ -24,7 +24,7 @@ namespace apl { CoreComponentPtr GridSequenceComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -75,10 +75,10 @@ GridSequenceComponent::adjustAutoCrossAxisSize() * layout changes processing. */ void -GridSequenceComponent::processLayoutChanges(bool useDirtyFlag) +GridSequenceComponent::processLayoutChanges(bool useDirtyFlag, bool first) { // Process parent sizing first to have info for child calculation. - CoreComponent::processLayoutChanges(useDirtyFlag); + CoreComponent::processLayoutChanges(useDirtyFlag, first); //Calculate child sizes if needed. auto bounds = getCalculated(kPropertyBounds).getRect(); @@ -94,7 +94,7 @@ GridSequenceComponent::processLayoutChanges(bool useDirtyFlag) } // Process any lazy layouts. - MultiChildScrollableComponent::processLayoutChanges(useDirtyFlag); + MultiChildScrollableComponent::processLayoutChanges(useDirtyFlag, first); } const ComponentPropDefSet& @@ -141,18 +141,19 @@ GridSequenceComponent::handlePropertyChange(const ComponentPropDef& def, const O if (def.key == kPropertyChildHeight || def.key == kPropertyChildWidth) { auto gridComponent = std::dynamic_pointer_cast(shared_from_this()); - gridComponent->processLayoutChanges(true); + gridComponent->processLayoutChanges(true, false); } } void GridSequenceComponent::layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, - bool useDirtyFlag) + bool useDirtyFlag, + bool first) { // We need to apply forced size before layout. applyChildSize(child, childIdx); - MultiChildScrollableComponent::layoutChildIfRequired(child, childIdx, useDirtyFlag); + MultiChildScrollableComponent::layoutChildIfRequired(child, childIdx, useDirtyFlag, first); } void diff --git a/aplcore/src/component/imagecomponent.cpp b/aplcore/src/component/imagecomponent.cpp index 7213ae8..f2c81cc 100644 --- a/aplcore/src/component/imagecomponent.cpp +++ b/aplcore/src/component/imagecomponent.cpp @@ -18,21 +18,14 @@ #include "apl/component/yogaproperties.h" #include "apl/content/rootconfig.h" #include "apl/engine/event.h" -#include "apl/engine/mediamanager.h" +#include "apl/media/mediamanager.h" namespace apl { -inline void -inlineResetMediaState(Component &component) -{ - auto& comp = dynamic_cast(component); - comp.resetMediaFetchState(); -} - CoreComponentPtr ImageComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -41,7 +34,7 @@ ImageComponent::create(const ContextPtr& context, ImageComponent::ImageComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : CoreComponent(context, std::move(properties), path) { } @@ -49,6 +42,11 @@ ImageComponent::ImageComponent(const ContextPtr& context, const ComponentPropDefSet& ImageComponent::propDefSet() const { + static auto resetMediaState = [](Component& component) { + auto& comp = dynamic_cast(component); + comp.resetMediaFetchState(); + }; + static ComponentPropDefSet sImageComponentProperties = ComponentPropDefSet( CoreComponent::propDefSet(), MediaComponentTrait::propDefList()).add({ {kPropertyAlign, kImageAlignCenter, sAlignMap, kPropInOut | kPropStyled | kPropDynamic}, // Doesn't match 1.0 spec @@ -57,16 +55,16 @@ ImageComponent::propDefSet() const {kPropertyOverlayColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic}, {kPropertyOverlayGradient, Object::NULL_OBJECT(), asGradient, kPropInOut | kPropStyled | kPropDynamic}, {kPropertyScale, kImageScaleBestFit, sScaleMap, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertySource, "", asStringOrArray, kPropInOut | kPropDynamic, inlineResetMediaState} + {kPropertySource, "", asStringOrArray, kPropInOut | kPropDynamic, resetMediaState} }); return sImageComponentProperties; } -std::set +std::vector ImageComponent::getSources() { - std::set sources; + std::vector sources; auto& sourceProp = getCalculated(kPropertySource); // Check if there anything to fetch @@ -75,14 +73,14 @@ ImageComponent::getSources() } if (sourceProp.isString()) { // Single source - sources.emplace(sourceProp.getString()); + sources.emplace_back(sourceProp.getString()); } else if (sourceProp.isArray()) { auto& filters = getCalculated(kPropertyFilters); if (filters.empty()) { // If no filters use last - sources.emplace(sourceProp.at(sourceProp.size() - 1).getString()); + sources.emplace_back(sourceProp.at(sourceProp.size() - 1).getString()); } else { // Else request everything for (auto& source : sourceProp.getArray()) { - sources.emplace(source.getString()); + sources.emplace_back(source.getString()); } } } @@ -103,12 +101,14 @@ ImageComponent::eventPropertyMap() const } std::string -ImageComponent::getVisualContextType() { +ImageComponent::getVisualContextType() const +{ return getCalculated(kPropertySource).empty() ? VISUAL_CONTEXT_TYPE_EMPTY : VISUAL_CONTEXT_TYPE_GRAPHIC; } void -ImageComponent::postProcessLayoutChanges() { +ImageComponent::postProcessLayoutChanges() +{ CoreComponent::postProcessLayoutChanges(); MediaComponentTrait::postProcessLayoutChanges(); } diff --git a/aplcore/src/component/mediacomponenttrait.cpp b/aplcore/src/component/mediacomponenttrait.cpp index 8bfd6e2..8a5881a 100644 --- a/aplcore/src/component/mediacomponenttrait.cpp +++ b/aplcore/src/component/mediacomponenttrait.cpp @@ -17,7 +17,8 @@ #include "apl/component/componentpropdef.h" #include "apl/content/rootconfig.h" #include "apl/engine/event.h" -#include "apl/engine/mediamanager.h" +#include "apl/media/mediamanager.h" +#include "apl/media/mediaobject.h" namespace apl { @@ -36,9 +37,10 @@ MediaComponentTrait::resetMediaFetchState() { auto component = getComponent(); component->setCalculated(kPropertyMediaState, kMediaStateNotRequested); - if (component->getCalculated(kPropertyLaidOut).truthy()) { + mMediaObjects.clear(); + + if (component->getCalculated(kPropertyLaidOut).truthy()) ensureMediaRequested(); - } } void @@ -47,6 +49,7 @@ MediaComponentTrait::postProcessLayoutChanges() { ensureMediaRequested(); } + void MediaComponentTrait::ensureMediaRequested() { @@ -60,49 +63,61 @@ MediaComponentTrait::ensureMediaRequested() return; auto sources = getSources(); - if (!sources.empty()) { - auto stillPending = context->mediaManager().registerComponentMedia(component, sources); - if (stillPending > 0) { - component->setCalculated(kPropertyMediaState, kMediaStatePending); - } else { - component->setCalculated(kPropertyMediaState, kMediaStateReady); + if (sources.empty()) + return; + + component->setCalculated(kPropertyMediaState, kMediaStatePending); + component->setDirty(kPropertyMediaState); + + for (const auto& m : sources) { + mMediaObjects.push_back(context->mediaManager().request(m, mediaType())); + if (mMediaObjects.back()->state() == MediaObject::kPending) { + auto weak = std::weak_ptr(component); + mMediaObjects.back()->addCallback([weak](const MediaObjectPtr& mediaObjectPtr) { + auto self = weak.lock(); + if (self) + std::dynamic_pointer_cast(self)->pendingMediaReturned(mediaObjectPtr); + }); } - component->setDirty(kPropertyMediaState); } + + // Some of the media objects may have been ready or in error state + updateMediaState(); } void -MediaComponentTrait::pendingMediaLoaded(const std::string& source, int stillPending) +MediaComponentTrait::pendingMediaReturned(const MediaObjectPtr& source) { - auto component = getComponent(); - auto state = static_cast(component->getCalculated(kPropertyMediaState).getInteger()); - switch (state) { - case kMediaStatePending: - { - if (stillPending == 0) { - component->setCalculated(kPropertyMediaState, kMediaStateReady); - component->setDirty(kPropertyMediaState); - } - break; - } - case kMediaStateReady: // FALL_THROUGH - case kMediaStateNotRequested: - case kMediaStateError: - default: - // We are in state that can't produce any changes. - return; - } + updateMediaState(); } void -MediaComponentTrait::pendingMediaFailed(const std::string& source) +MediaComponentTrait::updateMediaState() { auto component = getComponent(); auto state = static_cast(component->getCalculated(kPropertyMediaState).getInteger()); - if (state != kMediaStateError) { + if (state != kMediaStatePending) + return; + + // Check for error conditions first + if (std::any_of(mMediaObjects.begin(), + mMediaObjects.end(), + [](const MediaObjectPtr& ptr) { return ptr->state() == MediaObject::kError; })) { component->setCalculated(kPropertyMediaState, kMediaStateError); component->setDirty(kPropertyMediaState); + return; + } + + // Check if all media objects are ready + if (std::all_of(mMediaObjects.begin(), + mMediaObjects.end(), + [](const MediaObjectPtr& ptr) { + return ptr->state() == MediaObject::kReady; + })) { + component->setCalculated(kPropertyMediaState, kMediaStateReady); + component->setDirty(kPropertyMediaState); } } + } // namespace apl diff --git a/aplcore/src/component/multichildscrollablecomponent.cpp b/aplcore/src/component/multichildscrollablecomponent.cpp index 3e88430..87cfdc7 100644 --- a/aplcore/src/component/multichildscrollablecomponent.cpp +++ b/aplcore/src/component/multichildscrollablecomponent.cpp @@ -18,6 +18,11 @@ #include "apl/component/multichildscrollablecomponent.h" #include "apl/component/yogaproperties.h" #include "apl/content/rootconfig.h" +#include "apl/engine/layoutmanager.h" +#include "apl/livedata/layoutrebuilder.h" +#include "apl/time/sequencer.h" +#include "apl/time/timemanager.h" +#include "apl/utils/tracing.h" namespace apl { @@ -142,12 +147,16 @@ MultiChildScrollableComponent::allowForward() const { auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); auto currentPosition = mCalculated.get(kPropertyScrollPosition).asNumber(); auto vertical = isVertical(); + bool isLTR = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; double scrollSize = vertical ? innerBounds.getBottom() - : innerBounds.getRight(); + : (isLTR ? innerBounds.getRight() : innerBounds.getLeft()); double innerScrollSize = vertical ? lastChildBounds.getBottom() - : lastChildBounds.getRight(); + : (isLTR ? lastChildBounds.getRight() : lastChildBounds.getLeft()); + if (isHorizontal() && !isLTR) { + return currentPosition + scrollSize > innerScrollSize; + } return currentPosition + scrollSize < innerScrollSize; } @@ -178,7 +187,7 @@ MultiChildScrollableComponent::setScrollPositionDirectlyByChild(const CoreCompon { assert(child); - child->ensureLayout(true); // TODO: Should this set dirty? With the initial expansion this should not be set... + 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(); @@ -203,10 +212,25 @@ MultiChildScrollableComponent::setScrollPositionDirectlyByChild(const CoreCompon setScrollPositionDirectly(scrollPosition); } +void +MultiChildScrollableComponent::ensureChildLayout(const CoreComponentPtr& child, bool useDirtyFlag) +{ + // TODO: Search for child and if direct - attach if required. + auto it = std::find(mChildren.begin(), mChildren.end(), child); + if (it != mChildren.end()) { + auto index = std::distance(mChildren.begin(), it); + layoutChildIfRequired(child, index, true, false); + child->markDisplayedChildrenStale(true); + } else { + child->ensureLayoutInternal(useDirtyFlag); + } +} + bool MultiChildScrollableComponent::allowBackwards() const { auto currentPosition = mCalculated.get(kPropertyScrollPosition).asNumber(); - return (currentPosition > 0); + bool isRTL = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL; + return (isHorizontal() && isRTL) ? currentPosition < 0 : currentPosition > 0; } void @@ -438,19 +462,46 @@ getSpacing(const CoreComponent& component) { return 0; } +void +MultiChildScrollableComponent::fixScrollPosition(const Rect& oldAnchorRect, const Rect& anchorRect) +{ + if (anchorRect != oldAnchorRect) { + auto layoutDirection = static_cast(getCalculated(kPropertyLayoutDirection).asInt()); + auto currentPosition = getCalculated(kPropertyScrollPosition).asNumber(); + float offset; + if (isHorizontal()) { + offset = layoutDirection == kLayoutDirectionLTR + ? anchorRect.getLeft() - oldAnchorRect.getLeft() + : anchorRect.getRight() - oldAnchorRect.getRight(); + } else { + offset = anchorRect.getTop() - oldAnchorRect.getTop(); + } + currentPosition += offset; + mCalculated.set(kPropertyScrollPosition, Dimension(DimensionType::Absolute, currentPosition)); + setDirty(kPropertyScrollPosition); + } +} + Point -MultiChildScrollableComponent::trimScroll(const Point& point) const +MultiChildScrollableComponent::trimScroll(const Point& point) { // Break out early. If there are no children - no scrolling possible if (shouldNotPropagateLayoutChanges() || getCalculated(kPropertyBounds).empty()) return Point(); + if (mDelayLayoutAction && mDelayLayoutAction->isPending()) { + mDelayLayoutAction->terminate(); + mDelayLayoutAction = nullptr; + } + auto innerBounds = mCalculated.get(kPropertyInnerBounds).getRect(); // We treat this component as 0 point of sequence. All calculation happens in relation to it. - auto zeroAnchor = mChildren.at(mEnsuredChildren.empty() ? 0 : mEnsuredChildren.lowerBound()); - if (!zeroAnchor->isAttached()) zeroAnchor->ensureLayout(true); - auto zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().getTopLeft() - innerBounds.getTopLeft(); + 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 zeroAnchorPos = oldZeroAnchorPos.getTopLeft() - innerBounds.getTopLeft(); // We should disregard spacing in limit calculation as it's not part of inner bounds really. auto spacing = mEnsuredChildren.lowerBound() > 0 ? getSpacing(*zeroAnchor) : 0; @@ -462,10 +513,12 @@ MultiChildScrollableComponent::trimScroll(const Point& point) const auto y = point.getY(); // Cover in back direction. Current code ensures from index 0 to requested component and any updates // asynchronous so no way it will happen twice. - while (y < zeroAnchorPos.getY() && mEnsuredChildren.lowerBound() > 0) { - mChildren.at(mEnsuredChildren.lowerBound() - 1)->ensureLayout(true); + 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(); } + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); y += zeroAnchorPos.getY() - spacing; @@ -479,7 +532,7 @@ MultiChildScrollableComponent::trimScroll(const Point& point) const int startingChild = std::max(mEnsuredChildren.upperBound(), 0); for (int i = startingChild ; iensureLayout(false); + layoutChildIfRequired(child, i, false, false); maxY = nonNegative(child->getCalculated(kPropertyBounds).getRect().getBottom() - bottom); if (y <= maxY) return Point(0,y); @@ -487,12 +540,14 @@ MultiChildScrollableComponent::trimScroll(const Point& point) const return Point(0, maxY); } - else { // Horizontal + else if (getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR) { // Horizontal LTR auto x = point.getX(); - while (x < zeroAnchorPos.getX() && mEnsuredChildren.lowerBound() > 0) { - mChildren.at(mEnsuredChildren.lowerBound() - 1)->ensureLayout(true); + 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(); } + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); x += zeroAnchorPos.getX() - spacing; @@ -506,12 +561,42 @@ MultiChildScrollableComponent::trimScroll(const Point& point) const int startingChild = std::max(mEnsuredChildren.upperBound(), 0); for (int i = startingChild ; iensureLayout(true); + layoutChildIfRequired(child, i, true, false); maxX = nonNegative(child->getCalculated(kPropertyBounds).getRect().getRight() - right); if (x <= maxX) return Point(x,0); } + return Point(maxX, 0); + } else { // Horizontal RTL + auto x = point.getX(); + zeroAnchorPos = zeroAnchor->getCalculated(kPropertyBounds).getRect().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(); + } + fixScrollPosition(oldZeroAnchorPos, zeroAnchor->getCalculated(kPropertyBounds).getRect()); + + x -= zeroAnchorPos.getX() - spacing; + + if (x >= 0) + return Point(); + + float left = innerBounds.getLeft(); + float maxX = 0; + + // Ensure children until they cover the sequence. + int startingChild = std::max(mEnsuredChildren.upperBound(), 0); + for (int i = startingChild ; igetCalculated(kPropertyBounds).getRect().getLeft() - left); + if (x >= maxX) + return Point(x,0); + } + return Point(maxX, 0); } } @@ -562,6 +647,9 @@ MultiChildScrollableComponent::ensureChildAttached(const CoreComponentPtr& child // Ensure from upperBound to target for (int index = mEnsuredChildren.empty() ? 0 : mEnsuredChildren.upperBound() + 1; index <= targetIdx ; index++) { const auto& c = mChildren.at(index); + if (mRebuilder) { + mRebuilder->inflateIfRequired(c); + } if (attachChild(c, mEnsuredChildren.size())) { mEnsuredChildren.expandTo(index); } @@ -570,11 +658,17 @@ MultiChildScrollableComponent::ensureChildAttached(const CoreComponentPtr& child // Ensure from lowerBound down to target for (int index = mEnsuredChildren.lowerBound() - 1; index >= targetIdx ; index--) { const auto& c = mChildren.at(index); + if (mRebuilder) { + mRebuilder->inflateIfRequired(c); + } if (attachChild(c, 0)) { mEnsuredChildren.expandTo(index); } } } else { + if (mRebuilder) { + mRebuilder->inflateIfRequired(child); + } // Just attach single one inside of ensured range if needed. attachChild(child, targetIdx - mEnsuredChildren.lowerBound()); } @@ -614,37 +708,29 @@ MultiChildScrollableComponent::attachYogaNode(const CoreComponentPtr& child) } void -MultiChildScrollableComponent::layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, bool useDirtyFlag) +MultiChildScrollableComponent::layoutChildIfRequired(const CoreComponentPtr& child, size_t childIdx, bool useDirtyFlag, bool first) { + APL_TRACE_BLOCK("MultiChildScrollableComponent:layoutChildIfRequired"); if (!child->isAttached() || child->getCalculated(kPropertyBounds).empty()) { ensureChildAttached(child, childIdx); - relayoutInPlace(useDirtyFlag); + relayoutInPlace(useDirtyFlag, first); } } void -MultiChildScrollableComponent::relayoutInPlace(bool useDirtyFlag) +MultiChildScrollableComponent::relayoutInPlace(bool useDirtyFlag, bool first) { - // First calculate the layout for this component (if dirty) - auto bounds = mCalculated.get(kPropertyBounds).getRect(); - YGNodeCalculateLayout( - mYGNodeRef, - bounds.getWidth(), - bounds.getHeight(), - YGDirection::YGDirectionLTR); - - // Then re-layout from the root if necessary. We don't expect any of components higher in - // hierarchy to change when we attach to multichild scrollable. We need to call to root - // here to avoid any problems with propagation of positions for "lazy" components in the - // chain. Assumed to be optimal from yoga side. It could happen only after initial layout - // so we have parent bounds. - auto root = getRootComponent(); - if (root && root != shared_from_corecomponent()) { - auto rootBounds = root->getCalculated(kPropertyBounds).getRect(); - YGNodeCalculateLayout(root->getNode(), rootBounds.getWidth(), rootBounds.getHeight(), YGDirection::YGDirectionLTR); - } - - CoreComponent::processLayoutChanges(useDirtyFlag); + APL_TRACE_BLOCK("MultiChildScrollableComponent:relayoutInPlace"); + auto root = getRootComponent(); + auto rootBounds = root->getCalculated(kPropertyBounds).getRect(); + APL_TRACE_BEGIN("MultiChildScrollableComponent:YGNodeCalculateLayout:root"); + YGNodeCalculateLayout(root->getNode(), rootBounds.getWidth(), rootBounds.getHeight(), getLayoutDirection()); + APL_TRACE_END("MultiChildScrollableComponent:YGNodeCalculateLayout:root"); + auto oldBounds = getCalculated(kPropertyBounds); + CoreComponent::processLayoutChanges(useDirtyFlag, first); + if (oldBounds != getCalculated(kPropertyBounds)) { + mContext->layoutManager().needToReProcessLayoutChanges(); + } } float @@ -711,10 +797,10 @@ MultiChildScrollableComponent::removeChild(const CoreComponentPtr& child, size_t * required children). */ void -MultiChildScrollableComponent::runLayoutHeuristics(size_t anchorIdx, float childCache, float pageSize, bool useDirtyFlag) +MultiChildScrollableComponent::runLayoutHeuristics(size_t anchorIdx, float childCache, float pageSize, bool useDirtyFlag, bool first) { // Estimate how many children is actually required based on available anchor dimensions - auto toCover = estimateChildrenToCover((childCache + 1) * pageSize, anchorIdx); + auto toCover = estimateChildrenToCover(first ? pageSize : (childCache + 1) * pageSize, anchorIdx); auto attached = false; for (int i = mEnsuredChildren.upperBound(); i < std::min(anchorIdx + toCover, mChildren.size()); i++) { auto child = mChildren.at(i); @@ -727,25 +813,33 @@ MultiChildScrollableComponent::runLayoutHeuristics(size_t anchorIdx, float child } } - toCover = estimateChildrenToCover(childCache * pageSize, anchorIdx); - for (int i = mEnsuredChildren.lowerBound(); i >= std::max(0, static_cast(anchorIdx - toCover)); i--) { - auto child = mChildren.at(i); - if (!child->isAttached() || child->getCalculated(kPropertyBounds).empty()) { - attached = true; - ensureChildAttached(child, i); - if (i > 0 && childrenUseSpacingProperty()) { - child->fixSpacing(); + if (!first) { + toCover = estimateChildrenToCover(childCache * pageSize, anchorIdx); + for (int i = mEnsuredChildren.lowerBound(); i >= std::max(0, static_cast(anchorIdx - toCover)); i--) { + auto child = mChildren.at(i); + if (!child->isAttached() || child->getCalculated(kPropertyBounds).empty()) { + attached = true; + ensureChildAttached(child, i); + if (i > 0 && childrenUseSpacingProperty()) { + child->fixSpacing(); + } } } } - if (attached) relayoutInPlace(useDirtyFlag); + + if (attached) relayoutInPlace(useDirtyFlag, first); } void -MultiChildScrollableComponent::processLayoutChanges(bool useDirtyFlag) { +MultiChildScrollableComponent::processLayoutChanges(bool useDirtyFlag, bool first) { + APL_TRACE_BLOCK("MultiChildScrollableComponent:processLayoutChanges"); // We need to account for padding as find function don't do that automatically. bool horizontal = isHorizontal(); - auto paddedScrollPosition = scrollPosition() + getCalculated(kPropertyInnerBounds).getRect().getTopLeft(); + auto oldLayoutDirection = static_cast(mCalculated.get(kPropertyLayoutDirection).asInt()); + auto startPoint = oldLayoutDirection == kLayoutDirectionLTR + ? getCalculated(kPropertyInnerBounds).getRect().getTopLeft() + : getCalculated(kPropertyInnerBounds).getRect().getTopRight(); + auto paddedScrollPosition = scrollPosition() + startPoint; auto anchor = std::static_pointer_cast(findDirectChildAtPosition(paddedScrollPosition)); Rect oldAnchorBounds; @@ -753,23 +847,37 @@ MultiChildScrollableComponent::processLayoutChanges(bool useDirtyFlag) { oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); } - CoreComponent::processLayoutChanges(useDirtyFlag); + CoreComponent::processLayoutChanges(useDirtyFlag, first); + + // If starting empty ask for loading for conditional cases + if (mChildren.empty() && mRebuilder) { + mRebuilder->notifyStartEdgeReached(); + mRebuilder->notifyEndEdgeReached(); + } if (shouldNotPropagateLayoutChanges()) { // Starting with empty or invalid sequence return; } + auto layoutDirection = static_cast(getCalculated(kPropertyLayoutDirection).asInt()); + // We have not laid-out anything before. Refer to first available attached child. Possible only on initial layout. size_t anchorIdx = 0; if (!anchor) { anchorIdx = mEnsuredChildren.empty() ? 0 : mEnsuredChildren.lowerBound(); anchor = mChildren.at(anchorIdx); - layoutChildIfRequired(anchor, anchorIdx, useDirtyFlag); + layoutChildIfRequired(anchor, anchorIdx, useDirtyFlag, first); oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); } else { auto it = std::find(mChildren.begin(), mChildren.end(), anchor); anchorIdx = std::distance(mChildren.begin(), it); + if (oldLayoutDirection != layoutDirection) { + oldAnchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); + auto mirroredScrollPosition = getCalculated(kPropertyScrollPosition).asNumber() * -1.0f; + mCalculated.set(kPropertyScrollPosition, Dimension(DimensionType::Absolute, mirroredScrollPosition)); + setDirty(kPropertyScrollPosition); + } } if (!mChildren.empty() && childrenUseSpacingProperty()) { @@ -791,55 +899,92 @@ MultiChildScrollableComponent::processLayoutChanges(bool useDirtyFlag) { float pageSize = horizontal ? sequenceBounds.getWidth() : sequenceBounds.getHeight(); // Try to figure majority of layout as a bulk - runLayoutHeuristics(anchorIdx, childCache, pageSize, useDirtyFlag); + runLayoutHeuristics(anchorIdx, childCache, pageSize, useDirtyFlag, first); // Anchor bounds may have shifted Rect anchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); - float anchorPosition = horizontal ? anchorBounds.getX() : anchorBounds.getY(); - - // Lay out children in positive direction until we hit cache limit. - float distanceToCover = (childCache + 1) * pageSize + anchorPosition; + float anchorPosition = horizontal + ? (layoutDirection == kLayoutDirectionLTR ? anchorBounds.getX() : anchorBounds.getRight()) + : anchorBounds.getY(); + + // Lay out children in positive order until we hit cache limit. + auto distanceToCover = first ? pageSize : (childCache + 1) * pageSize; + float positionToCover = layoutDirection == kLayoutDirectionLTR + ? anchorPosition + distanceToCover + : anchorPosition - distanceToCover; + bool targetCovered = false; int lastLoaded = mEnsuredChildren.lowerBound(); for (; lastLoaded < mChildren.size(); lastLoaded++) { auto child = mChildren.at(lastLoaded); - layoutChildIfRequired(child, lastLoaded, useDirtyFlag); + layoutChildIfRequired(child, lastLoaded, useDirtyFlag, first); auto childBounds = child->getCalculated(kPropertyBounds).getRect(); - float distance = horizontal ? childBounds.getRight() : childBounds.getBottom(); - if (distance > distanceToCover) { + float childCoveredPosition = horizontal + ? (layoutDirection == kLayoutDirectionLTR ? childBounds.getRight() + : childBounds.getLeft()) + : childBounds.getBottom(); + targetCovered = layoutDirection == kLayoutDirectionLTR ? childCoveredPosition > positionToCover + : childCoveredPosition < positionToCover; + if (targetCovered) { break; } } - reportLoaded(std::min(lastLoaded, mEnsuredChildren.upperBound())); + // In case if children of sequence highly conditional we need to ask for more data regardless of position - for + // cases when long distance exists between valid data indexes. + if ((mEnsuredChildren.upperBound() + 1 >= mChildren.size()) && !targetCovered && mRebuilder) { + mRebuilder->notifyEndEdgeReached(); + } else { + reportLoaded(std::min(lastLoaded, mEnsuredChildren.upperBound())); + } - // Lay out children in negative direction until we hit cache limit. - distanceToCover = childCache * pageSize; + // Lay out children in negative order until we hit cache limit. + positionToCover = layoutDirection == kLayoutDirectionLTR + ? childCache * pageSize + : childCache * pageSize * -1.0f; int firstLoaded = mEnsuredChildren.upperBound(); + targetCovered = false; for (; firstLoaded >= 0; firstLoaded--) { auto child = mChildren.at(firstLoaded); - layoutChildIfRequired(child, firstLoaded, useDirtyFlag); + layoutChildIfRequired(child, firstLoaded, useDirtyFlag, first); auto childBounds = child->getCalculated(kPropertyBounds).getRect(); anchorBounds = anchor->getCalculated(kPropertyBounds).getRect(); float distance = (horizontal ? anchorBounds.getLeft() : anchorBounds.getTop()) - (horizontal ? childBounds.getLeft() : childBounds.getTop()); - if (distance > distanceToCover) { + targetCovered = layoutDirection == kLayoutDirectionLTR ? distance > positionToCover : distance < positionToCover; + if (targetCovered) { break; } } - reportLoaded(std::max(firstLoaded, mEnsuredChildren.lowerBound())); + if ((mEnsuredChildren.lowerBound() == 0) && !targetCovered && mRebuilder) { + mRebuilder->notifyStartEdgeReached(); + } else { + reportLoaded(std::max(firstLoaded, mEnsuredChildren.lowerBound())); + } if (anchor) { // Adjust scroll position if changed at the beginning of sequence - if (anchorBounds != oldAnchorBounds) { - auto currentPosition = getCalculated(kPropertyScrollPosition).asNumber(); - currentPosition += horizontal ? anchorBounds.getLeft() - oldAnchorBounds.getLeft() - : anchorBounds.getTop() - oldAnchorBounds.getTop(); - mCalculated.set(kPropertyScrollPosition, Dimension(DimensionType::Absolute, currentPosition)); - setDirty(kPropertyScrollPosition); - } + fixScrollPosition(oldAnchorBounds, anchorBounds); } ensureChildrenVisibilityUpdated(); + + if (first) { + // Avoid yoga initiated re-layout that may be caused by attaching components that were already laid-out + mContext->layoutManager().remove(getRootComponent()); + + // Postpone to the next frame, if any + if (mEnsuredChildren.upperBound() + 1 >= mChildren.size()) return; + mDelayLayoutAction = Action::makeDelayed(getRootConfig().getTimeManager(), 1); + auto weak_self = std::weak_ptr( + std::static_pointer_cast(shared_from_corecomponent())); + mDelayLayoutAction->then([weak_self](const ActionPtr &) { + auto self = weak_self.lock(); + if (self) { + self->processLayoutChanges(true, false); + self->mDelayLayoutAction = nullptr; + } + }); + } } void @@ -856,7 +1001,7 @@ MultiChildScrollableComponent::onScrollPositionUpdated() { mChildrenVisibilityStale = true; // Force figuring out what is on screen. - processLayoutChanges(true); + processLayoutChanges(true, false); } float @@ -869,21 +1014,22 @@ MultiChildScrollableComponent::getSnapOffsetForChild( float scrollTo = 0; float childStart, childEnd, currentPosition; auto childBoundsInParent = child->getCalculated(kPropertyBounds).getRect(); + bool isLTR = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; if (isVertical()) { childStart = childBoundsInParent.getTop(); childEnd = childBoundsInParent.getBottom(); currentPosition = scrollPosition().getY(); } else { - childStart = childBoundsInParent.getLeft(); - childEnd = childBoundsInParent.getRight(); + childStart = isLTR ? childBoundsInParent.getLeft() : childBoundsInParent.getRight(); + childEnd = isLTR ? childBoundsInParent.getRight() : childBoundsInParent.getLeft(); currentPosition = scrollPosition().getX(); } switch (snap) { case kSnapStart: case kSnapForceStart: - scrollTo = childStart; + scrollTo = childStart - parentStart; break; case kSnapCenter: @@ -942,7 +1088,7 @@ MultiChildScrollableComponent::findChildCloseToPosition(const Point& position) c auto referencePosition = vertical ? bounds.getCenterY() : bounds.getCenterX(); auto distance = std::abs(referencePosition - directionalOffset); - if (distance < bestDistance) { + if (distance <= bestDistance) { bestCandidate = child; bestDistance = distance; } else { @@ -962,37 +1108,41 @@ 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 lastChildBottom = - mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).getRect().getBottomRight(); + auto lastChildBounds = mChildren.at(mEnsuredChildren.upperBound())->getCalculated(kPropertyBounds).getRect(); + bool isRTL = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL; + auto lastChildBottom = isRTL ? lastChildBounds.getBottomLeft() : lastChildBounds.getBottomRight(); // Figure to which position we should snap - float parentStart, parentEnd, parentSize, forwardScrollLimit, scrollOffset; + float parentStart, parentEnd, parentOffset, forwardScrollLimit, scrollOffset; if (vertical) { parentStart = parentInnerBounds.getTop(); parentEnd = parentInnerBounds.getBottom(); - parentSize = parentEnd - parentStart; - forwardScrollLimit = lastChildBottom.getY() - parentSize; + parentOffset = parentEnd - parentStart; + forwardScrollLimit = lastChildBottom.getY() - parentOffset; scrollOffset = scrollPosition().getY(); } else { - parentStart = parentInnerBounds.getLeft(); - parentEnd = parentInnerBounds.getRight(); - parentSize = parentEnd - parentStart; - forwardScrollLimit = lastChildBottom.getX() - parentSize; + parentStart = isRTL ? parentInnerBounds.getRight() : parentInnerBounds.getLeft(); + parentEnd = isRTL ? parentInnerBounds.getLeft() : parentInnerBounds.getRight(); + parentOffset = parentEnd - parentStart; + forwardScrollLimit = isRTL ? lastChildBottom.getX() : lastChildBottom.getX() - parentOffset; scrollOffset = scrollPosition().getX(); } - // If we currently at a limit - do not snap, we already snapped to limit. Unless forced to. - if (!shouldForceSnap() && (scrollOffset <= 0 || scrollOffset >= forwardScrollLimit)) return {}; + // If we currently at a limit - do not snap, we already snapped to limit. + bool atLimit = (isHorizontal() && isRTL) + ? (scrollOffset >= 0 || scrollOffset <= forwardScrollLimit) + : (scrollOffset <= 0 || scrollOffset >= forwardScrollLimit); + if (atLimit) return {}; float parentSnapOffset = 0; switch (snap) { case kSnapCenter: case kSnapForceCenter: - parentSnapOffset = parentSize / 2; + parentSnapOffset = parentOffset / 2; break; case kSnapEnd: case kSnapForceEnd: - parentSnapOffset = parentSize; + parentSnapOffset = parentOffset; break; default: break; @@ -1002,7 +1152,8 @@ MultiChildScrollableComponent::getSnapOffset() const if (vertical) { referencePoint = Point(parentInnerBounds.getX(), scrollOffset + parentSnapOffset); } else { - referencePoint = Point(scrollOffset + parentSnapOffset, parentInnerBounds.getY()); + auto startPoint = isRTL ? parentInnerBounds.getTopRight() : parentInnerBounds.getTopLeft(); + referencePoint = Point(scrollOffset + parentSnapOffset + startPoint.getX(), parentInnerBounds.getY()); } auto targetChild = findDirectChildAtPosition(referencePoint); @@ -1054,11 +1205,17 @@ MultiChildScrollableComponent::getSnapOffset() const auto offset = getSnapOffsetForChild(targetChild, snap, parentStart, parentEnd); // Check if adjustment will overshoot us over scrolling limits. If so - clip to scroll limit. - if (scrollOffset + offset <= 0) { + bool exceededBackwardScrollLimit = (isHorizontal() && isRTL) + ? scrollOffset + offset >= 0 + : scrollOffset + offset <= 0; + if (exceededBackwardScrollLimit) { offset = -scrollOffset; } - if (scrollOffset + offset >= forwardScrollLimit) { + bool exceededForwardScrollLimit = (isHorizontal() && isRTL) + ? scrollOffset + offset <= forwardScrollLimit + : scrollOffset + offset >= forwardScrollLimit; + if (exceededForwardScrollLimit) { offset = forwardScrollLimit - scrollOffset; } @@ -1080,4 +1237,12 @@ MultiChildScrollableComponent::attachYogaNodeIfRequired(const CoreComponentPtr& } } +bool +MultiChildScrollableComponent::shouldBeFullyInflated(int index) const +{ + return shouldAttachChildYogaNode(index) || + (mChildren.empty() && index == 0) || + (mEnsuredChildren.contains(index) && index > mEnsuredChildren.lowerBound()); +} + } // namespace apl diff --git a/aplcore/src/component/pagercomponent.cpp b/aplcore/src/component/pagercomponent.cpp index 111948f..56deac3 100644 --- a/aplcore/src/component/pagercomponent.cpp +++ b/aplcore/src/component/pagercomponent.cpp @@ -19,12 +19,14 @@ #include "apl/content/rootconfig.h" #include "apl/engine/layoutmanager.h" #include "apl/focus/focusmanager.h" +#include "apl/livedata/layoutrebuilder.h" +#include "apl/livedata/livearrayobject.h" #include "apl/primitives/keyboard.h" #include "apl/time/sequencer.h" #include "apl/touch/gestures/pagerflinggesture.h" +#include "apl/touch/utils/pagemovehandler.h" #include "apl/time/timemanager.h" -#include "apl/utils/pagemovehandler.h" namespace apl { @@ -33,7 +35,7 @@ static const bool DEBUG_PAGER = false; CoreComponentPtr PagerComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) { + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); return ptr; @@ -41,10 +43,17 @@ PagerComponent::create(const ContextPtr& context, PagerComponent::PagerComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : ActionableComponent(context, std::move(properties), path) {} +void +PagerComponent::release() +{ + mCurrentAnimation = nullptr; + ActionableComponent::release(); +} + inline Object defaultWidth(Component& component, const RootConfig& rootConfig) { return rootConfig.getDefaultComponentWidth(component.getType()); @@ -95,8 +104,8 @@ PagerComponent::propDefSet() const { {kPropertyHeight, Dimension(100), asNonAutoDimension, kPropIn, yn::setHeight, defaultHeight}, {kPropertyWidth, Dimension(100), asNonAutoDimension, kPropIn, yn::setWidth, defaultWidth}, {kPropertyInitialPage, 0, asInteger, kPropIn}, - {kPropertyNavigation, kNavigationWrap, sNavigationMap, kPropInOut | kPropVisualContext}, - {kPropertyPageDirection, kScrollDirectionHorizontal, sScrollDirectionMap, kPropIn}, + {kPropertyNavigation, kNavigationWrap, sNavigationMap, kPropInOut | kPropDynamic | kPropVisualContext}, + {kPropertyPageDirection, kScrollDirectionHorizontal, sScrollDirectionMap, kPropIn | kPropDynamic}, {kPropertyHandlePageMove, Object::EMPTY_ARRAY(), asArray, kPropIn}, {kPropertyOnPageChanged, Object::EMPTY_ARRAY(), asCommand, kPropIn}, {kPropertyCurrentPage, 0, asInteger, kPropRuntimeState | kPropVisualContext}, @@ -116,9 +125,7 @@ PagerComponent::initialize() { mCalculated.set(kPropertyCurrentPage, currentPage); // If native gestures enabled - register them. - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) { - mGestureHandlers.emplace_back(PagerFlingGesture::create(std::static_pointer_cast(shared_from_this()))); - } + mGestureHandlers.emplace_back(PagerFlingGesture::create(std::static_pointer_cast(shared_from_this()))); } void @@ -140,10 +147,9 @@ PagerComponent::setPage(int page) { mCalculated.set(kPropertyCurrentPage, page); setVisualContextDirty(); - attachCurrentAndReportLoaded(); + attachPageAndReportLoaded(page); - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) - setDirty(kPropertyCurrentPage); + setDirty(kPropertyCurrentPage); } /** @@ -159,7 +165,7 @@ PagerComponent::setPageImmediate(int pageIndex) mContext->sequencer().releaseResource({kExecutionResourcePosition, shared_from_this()}); mCalculated.set(kPropertyCurrentPage, pageIndex); setVisualContextDirty(); - attachCurrentAndReportLoaded(); + attachPageAndReportLoaded(pageIndex); setDirty(kPropertyCurrentPage); if (allowEventHandlers()) @@ -175,15 +181,7 @@ PagerComponent::setPageUtil( const ActionRef& ref, bool skipDefaultAnimation) { - if (context->getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) { - std::dynamic_pointer_cast(target)->handleSetPage(index, direction, ref, skipDefaultAnimation); - } else { - EventBag bag; - bag.emplace(kEventPropertyPosition, index); - bag.emplace(kEventPropertyDirection, direction == kPageDirectionForward ? - kEventDirectionForward : kEventDirectionBackward); - context->pushEvent(Event(kEventTypeSetPage, std::move(bag), target, ref)); - } + std::dynamic_pointer_cast(target)->handleSetPage(index, direction, ref, skipDefaultAnimation); } void @@ -191,9 +189,17 @@ PagerComponent::handleSetPage(int index, PageDirection direction, const ActionRe { auto currentPage = pagePosition(); - // Have to do that here for now in order to give viewhost possibility to load the next page - // if not there - setPage(index); + // Have to do that here for now in order to give viewhost possibility to load the next page if not there + attachPageAndReportLoaded(index); + + // We have current animation in progress, do what it asks and finish it up preemptively. + // Only ever can happen on <= 1.3 as resources will prevent it otherwise. + if (mPageMoveHandler && mCurrentAnimation) { + setPage(mPageMoveHandler->getTargetPage()); + mCurrentAnimation->resolve(); + if (!ref.isEmpty() && ref.isPending()) ref.resolve(); + return; + } // Set initial state startPageMove(direction, currentPage, index); @@ -223,6 +229,22 @@ PagerComponent::handleSetPage(int index, PageDirection direction, const ActionRe ref.resolve(); } }); + + if (!ref.isEmpty() && ref.isPending()) { + ref.addTerminateCallback([weak_ptr](const TimersPtr&) { + auto self = weak_ptr.lock(); + if (self) { + if (self->mCurrentAnimation) { + self->mCurrentAnimation->terminate(); + self->mCurrentAnimation = nullptr; + } + if (self->mPageMoveHandler) { + self->mPageMoveHandler->reset(*self); + self->mPageMoveHandler = nullptr; + } + } + }); + } } ActionPtr @@ -243,21 +265,17 @@ PagerComponent::startPageMove(PageDirection direction, int currentPage, int targ (direction == kPageDirectionForward ? kSwipeDirectionLeft : kSwipeDirectionRight) : (direction == kPageDirectionForward ? kSwipeDirectionUp : kSwipeDirectionDown); - auto currentChild = getCoreChildAt(currentPage); - auto nextChild = getCoreChildAt(targetPage); - - // Reset opacity, transforms and interactivity here - currentChild->setProperty(kPropertyOpacity, 1.0f); - currentChild->setProperty(kPropertyTransformAssigned, Object::EMPTY_ARRAY()); - nextChild->setProperty(kPropertyOpacity, 1.0f); - nextChild->setProperty(kPropertyTransformAssigned, Object::EMPTY_ARRAY()); + if (isHorizontal() && getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL) { + // Switch direction for RTL layouts + swipeDirection = (swipeDirection == kSwipeDirectionLeft ? kSwipeDirectionRight : kSwipeDirectionLeft); + } auto handlerObject = getCalculated(kPropertyHandlePageMove); // We cache it here. It makes no sense to change handler while it's in animation . mPageMoveHandler = PageMoveHandler::create(shared_from_corecomponent(), handlerObject, swipeDirection, direction, currentPage, targetPage); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(true); } void @@ -293,8 +311,12 @@ PagerComponent::endPageMove(bool fulfilled, const ActionRef& ref, bool fast) ref.resolve(); } } - markDisplayedChildrenStale(); - mPageMoveHandler = nullptr; + + markDisplayedChildrenStale(true); + if (mPageMoveHandler) { + mPageMoveHandler->reset(*this); + mPageMoveHandler = nullptr; + } mCurrentAnimation = nullptr; } @@ -328,11 +350,8 @@ PagerComponent::eventPropertyMap() const ScrollType PagerComponent::scrollType() const { - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) { - return getCalculated(kPropertyPageDirection) == kScrollDirectionVertical ? kScrollTypeVerticalPager - : kScrollTypeHorizontalPager; - } - return kScrollTypeHorizontalPager; + return getCalculated(kPropertyPageDirection) == kScrollDirectionVertical ? kScrollTypeVerticalPager + : kScrollTypeHorizontalPager; } PageDirection @@ -367,6 +386,20 @@ PagerComponent::pageDirection() const { return kPageDirectionNone; } +bool +PagerComponent::allowForward() const +{ + auto allowedDirection = pageDirection(); + return allowedDirection == kPageDirectionBoth || allowedDirection == kPageDirectionForward; +} + +bool +PagerComponent::allowBackwards() const +{ + auto allowedDirection = pageDirection(); + return allowedDirection == kPageDirectionBoth || allowedDirection == kPageDirectionBack; +} + void PagerComponent::ensureDisplayedChildren() { @@ -398,6 +431,10 @@ PagerComponent::ensureDisplayedChildren() { ? (swipeDirection == kSwipeDirectionLeft? kPageDirectionForward : kPageDirectionBack) : (swipeDirection == kSwipeDirectionUp? kPageDirectionForward : kPageDirectionBack); + if (isHorizontal() && getCalculated(kPropertyLayoutDirection) == kLayoutDirectionRTL) { + // swipe directions are flipped for RTL layout + direction = (direction == kPageDirectionForward) ? kPageDirectionBack : kPageDirectionForward; + } // compare user assigned draw order to direction to calculate // relative "ZOrder" of next page bool nextAbove = false; @@ -528,9 +565,39 @@ PagerComponent::removeChild(const CoreComponentPtr& child, size_t index, bool us } void -PagerComponent::processLayoutChanges(bool useDirtyFlag) { - attachCurrentAndReportLoaded(); - CoreComponent::processLayoutChanges(useDirtyFlag); +PagerComponent::reportLoadedInternal(size_t index) +{ + if (index == 0 && mRebuilder) mRebuilder->notifyStartEdgeReached(); + else if (index >= mChildren.size() - 1 && mRebuilder) mRebuilder->notifyEndEdgeReached(); + else reportLoaded(index); +} + +void +PagerComponent::processLayoutChanges(bool useDirtyFlag, bool first) +{ + auto currentPage = getCalculated(kPropertyCurrentPage).asInt(); + if (first && !mChildren.empty()) { + const auto& c = mChildren.at(currentPage); + if (mRebuilder) { + mRebuilder->inflateIfRequired(c); + } + mContext->layoutManager().requestLayout(c, false); + reportLoadedInternal(currentPage); + + mDelayLayoutAction = Action::makeDelayed(getRootConfig().getTimeManager(), 1); + auto weak_self = std::weak_ptr( + std::static_pointer_cast(shared_from_corecomponent())); + mDelayLayoutAction->then([weak_self](const ActionPtr &) { + auto self = weak_self.lock(); + if (self) { + self->processLayoutChanges(true, false); + self->mDelayLayoutAction = nullptr; + } + }); + } else { + attachPageAndReportLoaded(currentPage); + } + CoreComponent::processLayoutChanges(useDirtyFlag, first); } bool @@ -546,30 +613,36 @@ PagerComponent::finalizePopulate() std::min(getCalculated(kPropertyInitialPage).getInteger(), static_cast(mChildren.size()) - 1)); mCalculated.set(kPropertyCurrentPage, initialPage); - attachCurrentAndReportLoaded(); - auto navigation = static_cast(mCalculated.get(kPropertyNavigation).asInt()); + // One using it should be careful in cases when DDS is infinite - it may lead to excessive load requests. + if (mContext->getRequestedAPLVersion().compare("1.7") < 0) { + auto navigation = static_cast(mCalculated.get(kPropertyNavigation).asInt()); - // Override wrap to normal if dynamic data. - if (mRebuilder && navigation == kNavigationWrap) { - mCalculated.set(kPropertyNavigation, kNavigationNormal); + // Override wrap to normal if populated by DynamicDatasource. LiveArray != DDS. + if (mRebuilder && navigation == kNavigationWrap) { + auto rebuilderArray = mRebuilder->getBackingArray(); + if (rebuilderArray && rebuilderArray->isPaginating()) { + mCalculated.set(kPropertyNavigation, kNavigationNormal); + } + } } } void -PagerComponent::attachCurrentAndReportLoaded() { +PagerComponent::attachPageAndReportLoaded(int page) { LOG_IF(DEBUG_PAGER) << this->toDebugSimpleString(); - - if (mChildren.empty()) + if (mChildren.empty() && mRebuilder) { + // Force loading if possible + mRebuilder->notifyStartEdgeReached(); + mRebuilder->notifyEndEdgeReached(); return; + } /** - * Ensure that the current page and some number of pages about - * the current page have been laid out. This avoids stutters when - * switching pages in case the next page needs to lay out complicated - * text blocks. + * Ensure that the requested page and some number of pages about it + * the have been laid out. This avoids stutters when switching pages + * in case the next page needs to lay out complicated text blocks. */ - const auto currentPage = getCalculated(kPropertyCurrentPage).asInt(); const auto childCount = static_cast(mChildren.size()); const auto pagerChildCache = mContext->getRootConfig().getPagerChildCache(); const auto navigation = static_cast(getCalculated(kPropertyNavigation).getInteger()); @@ -580,21 +653,21 @@ PagerComponent::attachCurrentAndReportLoaded() { switch (navigation) { case kNavigationNormal: // Allow forwards and backwards; bound to (0, childCount) case kNavigationNone: // We don't know how it will behave, so use the "normal" page-cache algorithm - start = std::max(0, currentPage - pagerChildCache); - count = std::min(currentPage + pagerChildCache + 1, childCount) - start; + start = std::max(0, page - pagerChildCache); + count = std::min(page + pagerChildCache + 1, childCount) - start; break; case kNavigationWrap: // Allow forwards and backwards; may wrap around if (pagerChildCache * 2 + 1 >= childCount) { // All pages should be cached count = childCount; } else { // Some pages won't be cached - start = (currentPage - pagerChildCache + childCount) % childCount; + start = (page - pagerChildCache + childCount) % childCount; count = pagerChildCache * 2 + 1; } break; case kNavigationForwardOnly: // Allow forward only; don't allow wrapping - start = currentPage; - count = std::min(currentPage + pagerChildCache + 1, childCount) - start; + start = page; + count = std::min(page + pagerChildCache + 1, childCount) - start; break; } @@ -602,10 +675,13 @@ PagerComponent::attachCurrentAndReportLoaded() { auto& lm = mContext->layoutManager(); for (int i = 0 ; i < count ; i++) { auto index = (start + i) % childCount; - lm.requestLayout(mChildren.at(index), false); + const auto& c = mChildren.at(index); + if (mRebuilder) { + mRebuilder->inflateIfRequired(c); + } + lm.requestLayout(c, false); + reportLoadedInternal(index); } - - reportLoaded(currentPage); } PageDirection @@ -678,6 +754,8 @@ PagerComponent::takeFocusFromChild(FocusDirection direction, const Rect& origin) // 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)); + // Need to have that to base search on what it will be. + setPage(targetPage); auto pager = shared_from_corecomponent(); auto next = getContext()->focusManager().find(direction, pager, offsetRect, pager); if (next) { diff --git a/aplcore/src/component/scrollablecomponent.cpp b/aplcore/src/component/scrollablecomponent.cpp index 84c177b..0e4837d 100644 --- a/aplcore/src/component/scrollablecomponent.cpp +++ b/aplcore/src/component/scrollablecomponent.cpp @@ -22,9 +22,15 @@ #include "apl/content/rootconfig.h" #include "apl/touch/gestures/scrollgesture.h" #include "apl/time/timemanager.h" +#include "apl/utils/stickychildrentree.h" namespace apl { +ScrollableComponent::ScrollableComponent(const ContextPtr& context, Properties&& properties, + const Path& path) : + ActionableComponent(context, std::move(properties), path), + mStickyTree(std::make_shared(*this)) {}; + const ComponentPropDefSet& ScrollableComponent::propDefSet() const { @@ -41,8 +47,13 @@ ScrollableComponent::propDefSet() const }; static auto setScrollPercent = [](CoreComponent& component, const Object& value) -> void { - auto height = YGNodeLayoutGetHeight(component.getNode()); - dynamic_cast(component).setScrollPositionDirectly(height * value.asNumber()); + float scrollSize; + if (component.scrollType() == kScrollTypeHorizontal) { + scrollSize = YGNodeLayoutGetWidth(component.getNode()); + } else { + scrollSize = YGNodeLayoutGetHeight(component.getNode()); + } + dynamic_cast(component).setScrollPositionDirectly(scrollSize * value.asNumber()); }; static ComponentPropDefSet sScrollableComponentProperties(ActionableComponent::propDefSet(), { @@ -130,18 +141,17 @@ ScrollableComponent::getTags(rapidjson::Value& outMap, rapidjson::Document::Allo void ScrollableComponent::initialize() { ActionableComponent::initialize(); - // If native gestures enabled - register them. - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) - mGestureHandlers.emplace_back(ScrollGesture::create(std::static_pointer_cast(shared_from_this()))); + mGestureHandlers.emplace_back(ScrollGesture::create(std::static_pointer_cast(shared_from_this()))); } void ScrollableComponent::onScrollPositionUpdated() { setVisualContextDirty(); - markDisplayedChildrenStale(); - if (getRootConfig().experimentalFeatureEnabled(RootConfig::kExperimentalFeatureHandleScrollingAndPagingInCore)) - setDirty(kPropertyScrollPosition); + markDisplayedChildrenStale(true); + setDirty(kPropertyScrollPosition); + + mStickyTree->updateStickyOffsets(); } bool @@ -158,11 +168,16 @@ bool ScrollableComponent::canScroll(FocusDirection direction) { auto sp = scrollType(); - return (((direction == kFocusDirectionUp && sp == kScrollTypeVertical) || - (direction == kFocusDirectionLeft && sp == kScrollTypeHorizontal) || + bool isLTR = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR; + bool horizontalBackwards = isLTR + ? (direction == kFocusDirectionLeft && sp == kScrollTypeHorizontal) + : (direction == kFocusDirectionRight && sp == kScrollTypeHorizontal); + bool horizontalForwards = isLTR + ? (direction == kFocusDirectionRight && sp == kScrollTypeHorizontal) + : (direction == kFocusDirectionLeft && sp == kScrollTypeHorizontal); + return (((direction == kFocusDirectionUp && sp == kScrollTypeVertical) || horizontalBackwards || (direction == kFocusDirectionBackwards)) && allowBackwards()) || - (((direction == kFocusDirectionDown && sp == kScrollTypeVertical) || - (direction == kFocusDirectionRight && sp == kScrollTypeHorizontal) || + (((direction == kFocusDirectionDown && sp == kScrollTypeVertical) || horizontalForwards || (direction == kFocusDirectionForward)) && allowForward()); } @@ -205,7 +220,10 @@ ScrollableComponent::takeFocusFromChild(FocusDirection direction, const Rect& or if (canTravel) { // Shift in % float targetShift = 0; - if (direction == kFocusDirectionRight || + bool horizontalForward = getCalculated(kPropertyLayoutDirection) == kLayoutDirectionLTR + ? (scrollType() == kScrollTypeHorizontal && direction == kFocusDirectionRight) + : (scrollType() == kScrollTypeHorizontal && direction == kFocusDirectionLeft); + if (horizontalForward || direction == kFocusDirectionDown || direction == kFocusDirectionForward) { targetShift = 100; diff --git a/aplcore/src/component/scrollviewcomponent.cpp b/aplcore/src/component/scrollviewcomponent.cpp index 85bd568..0b2f0d7 100644 --- a/aplcore/src/component/scrollviewcomponent.cpp +++ b/aplcore/src/component/scrollviewcomponent.cpp @@ -25,7 +25,7 @@ namespace apl { CoreComponentPtr ScrollViewComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) { + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); return ptr; @@ -33,7 +33,7 @@ ScrollViewComponent::create(const ContextPtr& context, ScrollViewComponent::ScrollViewComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : ScrollableComponent(context, std::move(properties), path) { YGNodeStyleSetOverflow(mYGNodeRef, YGOverflowScroll); } @@ -62,7 +62,7 @@ ScrollViewComponent::scrollPosition() const } Point -ScrollViewComponent::trimScroll(const Point& point) const { +ScrollViewComponent::trimScroll(const Point& point) { auto y = point.getY(); if (y <= 0 || mChildren.empty()) return Point(); @@ -103,7 +103,7 @@ ScrollViewComponent::allowBackwards() const { void ScrollViewComponent::onScrollPositionUpdated() { ScrollableComponent::onScrollPositionUpdated(); - markDisplayedChildrenStale(); + markDisplayedChildrenStale(true); if (getChildCount() > 0) { auto child = getCoreChildAt(0); diff --git a/aplcore/src/component/sequencecomponent.cpp b/aplcore/src/component/sequencecomponent.cpp index 9462aed..5a2f3cf 100644 --- a/aplcore/src/component/sequencecomponent.cpp +++ b/aplcore/src/component/sequencecomponent.cpp @@ -23,7 +23,7 @@ namespace apl { CoreComponentPtr SequenceComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -32,7 +32,7 @@ SequenceComponent::create(const ContextPtr& context, SequenceComponent::SequenceComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : MultiChildScrollableComponent(context, std::move(properties), path) {} const ComponentPropDefSet& @@ -65,7 +65,7 @@ SequenceComponent::estimateChildrenToCover(float distance, size_t baseChild) { auto size = mChildren.at(baseChild)->getCalculated(kPropertyBounds).getRect(); auto vertical = isVertical(); - return std::floor(std::abs(distance) / (vertical ? size.getHeight() : size.getWidth())); + return std::ceil(std::abs(distance) / (vertical ? size.getHeight() : size.getWidth())); } } // namespace apl diff --git a/aplcore/src/component/textcomponent.cpp b/aplcore/src/component/textcomponent.cpp index b8b140c..34b2c84 100644 --- a/aplcore/src/component/textcomponent.cpp +++ b/aplcore/src/component/textcomponent.cpp @@ -23,7 +23,7 @@ namespace apl { CoreComponentPtr TextComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -32,7 +32,7 @@ TextComponent::create(const ContextPtr& context, TextComponent::TextComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : CoreComponent(context, std::move(properties), path) { YGNodeSetMeasureFunc(mYGNodeRef, textMeasureFunc); @@ -41,25 +41,35 @@ TextComponent::TextComponent(const ContextPtr& context, } -inline void internalCheckKaraokeTargetColor(Component& component) +static inline void internalCheckKaraokeTargetColor(Component& component) { auto& text = static_cast(component); text.checkKaraokeTargetColor(); } -inline Object defaultFontColor(Component& component, const RootConfig& rootConfig) +static inline Object defaultFontColor(Component& component, const RootConfig& rootConfig) { return Object(rootConfig.getDefaultFontColor(component.getContext()->getTheme())); } -inline Object defaultFontFamily(Component&, const RootConfig& rootConfig) +static inline Object defaultFontFamily(Component&, const RootConfig& rootConfig) { return Object(rootConfig.getDefaultFontFamily()); } +static inline Object inheritLang(Component& comp, const RootConfig& rconfig) +{ + return Object(comp.getContext()->getLang()); +}; + const ComponentPropDefSet& TextComponent::propDefSet() const { + auto fixTextAlign = [] (Component& comp) { + auto& coreComp = dynamic_cast(comp); + coreComp.updateTextAlign(true); + }; + static ComponentPropDefSet sTextComponentProperties(CoreComponent::propDefSet(), { {kPropertyColor, Color(), asColor, kPropInOut | kPropStyled | kPropDynamic, internalCheckKaraokeTargetColor, defaultFontColor}, @@ -69,11 +79,13 @@ TextComponent::propDefSet() const {kPropertyFontSize, Dimension(40), asAbsoluteDimension, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, {kPropertyFontStyle, kFontStyleNormal, sFontStyleMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, {kPropertyFontWeight, 400, sFontWeightMap, kPropInOut | kPropLayout | kPropStyled | kPropDynamic}, + {kPropertyLang, "", asString, kPropInOut | kPropLayout | kPropStyled | kPropDynamic, inheritLang}, {kPropertyLetterSpacing, Dimension(0), asAbsoluteDimension, kPropInOut | kPropLayout | kPropStyled}, {kPropertyLineHeight, 1.25, asNonNegativeNumber, kPropInOut | kPropLayout | kPropStyled}, {kPropertyMaxLines, 0, asInteger, kPropInOut | kPropLayout | kPropStyled}, {kPropertyText, StyledText::EMPTY(), asStyledText, kPropInOut | kPropLayout | kPropDynamic | kPropVisualContext } , - {kPropertyTextAlign, kTextAlignAuto, sTextAlignMap, kPropInOut | kPropLayout | kPropStyled}, + {kPropertyTextAlign, kTextAlignAuto, sTextAlignMap, kPropOut}, + {kPropertyTextAlignAssigned, kTextAlignAuto, sTextAlignMap, kPropIn | kPropLayout | kPropStyled | kPropDynamic, fixTextAlign}, {kPropertyTextAlignVertical, kTextAlignVerticalAuto, sTextAlignVerticalMap, kPropInOut | kPropLayout | kPropStyled} }); @@ -173,6 +185,24 @@ TextComponent::checkKaraokeTargetColor() } } +void +TextComponent::updateTextAlign(bool useDirtyFlag) +{ + auto layoutDirection = static_cast(mCalculated.get(kPropertyLayoutDirection).asInt()); + auto textAlign = static_cast(mCalculated.get(kPropertyTextAlignAssigned).asInt()); + + if (textAlign == kTextAlignStart) { + textAlign = layoutDirection == kLayoutDirectionRTL ? kTextAlignRight : kTextAlignLeft; + } else if (textAlign == kTextAlignEnd) { + textAlign = layoutDirection == kLayoutDirectionRTL ? kTextAlignLeft : kTextAlignRight; + } + if (textAlign != mCalculated.get(kPropertyTextAlign).asInt()) { + mCalculated.set(kPropertyTextAlign, textAlign); + if (useDirtyFlag) + setDirty(kPropertyTextAlign); + } +} + /* * Initial assignment of properties. Don't set any dirty flags here. * @@ -187,6 +217,7 @@ TextComponent::assignProperties(const ComponentPropDefSet& propDefSet) // components cannot be created with the karaoke state active. mCalculated.set(kPropertyColorKaraokeTarget, mCalculated.get(kPropertyColor)); mCalculated.set(kPropertyColorNonKaraoke, mCalculated.get(kPropertyColor)); + updateTextAlign(false); } rapidjson::Value @@ -208,10 +239,11 @@ TextComponent::serializeMeasure(rapidjson::Document::AllocatorType& allocator) c } std::string -TextComponent::getVisualContextType() { +TextComponent::getVisualContextType() const +{ return getValue().empty() ? VISUAL_CONTEXT_TYPE_EMPTY : VISUAL_CONTEXT_TYPE_TEXT; } -} // namespace apl \ No newline at end of file +} // namespace apl diff --git a/aplcore/src/component/textmeasurement.cpp b/aplcore/src/component/textmeasurement.cpp index 8399c36..8653495 100644 --- a/aplcore/src/component/textmeasurement.cpp +++ b/aplcore/src/component/textmeasurement.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -20,16 +20,16 @@ namespace apl { class DummyTextMeasurement : public TextMeasurement { public: LayoutSize measure( Component *component, - float width, - MeasureMode widthMode, - float height, - MeasureMode heightMode ) override { + float width, + MeasureMode widthMode, + float height, + MeasureMode heightMode ) override { return { 10, 10 }; } - virtual float baseline( Component *component, - float width, - float height ) override { + float baseline( Component *component, + float width, + float height ) override { return height * 0.5; } }; diff --git a/aplcore/src/component/touchwrappercomponent.cpp b/aplcore/src/component/touchwrappercomponent.cpp index 32cde4e..ff834e1 100644 --- a/aplcore/src/component/touchwrappercomponent.cpp +++ b/aplcore/src/component/touchwrappercomponent.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -24,7 +24,7 @@ namespace apl { CoreComponentPtr TouchWrapperComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) { + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); return ptr; @@ -32,7 +32,7 @@ TouchWrapperComponent::create(const ContextPtr& context, TouchWrapperComponent::TouchWrapperComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : TouchableComponent(context, std::move(properties), path) { } diff --git a/aplcore/src/component/vectorgraphiccomponent.cpp b/aplcore/src/component/vectorgraphiccomponent.cpp index 0861a9c..d10f050 100644 --- a/aplcore/src/component/vectorgraphiccomponent.cpp +++ b/aplcore/src/component/vectorgraphiccomponent.cpp @@ -26,7 +26,7 @@ namespace apl { CoreComponentPtr VectorGraphicComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -35,7 +35,7 @@ VectorGraphicComponent::create(const ContextPtr& context, VectorGraphicComponent::VectorGraphicComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : TouchableComponent(context, std::move(properties), path) { } @@ -53,12 +53,13 @@ VectorGraphicComponent::release() const ComponentPropDefSet& VectorGraphicComponent::propDefSet() const { - static ComponentPropDefSet sVectorGraphicComponentProperties(TouchableComponent::propDefSet(), { - {kPropertyAlign, kVectorGraphicAlignCenter, sVectorGraphicAlignMap, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertyGraphic, Object::NULL_OBJECT(), nullptr, kPropOut}, - {kPropertyMediaBounds, Object::NULL_OBJECT(), nullptr, kPropOut}, - {kPropertyScale, kVectorGraphicScaleNone, sVectorGraphicScaleMap, kPropInOut | kPropStyled | kPropDynamic}, - {kPropertySource, "", asString, kPropInOut}, + static auto sVectorGraphicComponentProperties = ComponentPropDefSet(TouchableComponent::propDefSet(), MediaComponentTrait::propDefList()) + .add({ + {kPropertyAlign, kVectorGraphicAlignCenter, sVectorGraphicAlignMap, kPropInOut | kPropStyled | kPropDynamic}, + {kPropertyGraphic, Object::NULL_OBJECT(), nullptr, kPropOut}, + {kPropertyMediaBounds, Object::NULL_OBJECT(), nullptr, kPropOut}, + {kPropertyScale, kVectorGraphicScaleNone, sVectorGraphicScaleMap, kPropInOut | kPropStyled | kPropDynamic}, + {kPropertySource, "", asString, kPropInOut}, }); return sVectorGraphicComponentProperties; @@ -115,7 +116,7 @@ VectorGraphicComponent::updateStyle() setDirty(kPropertyGraphic); // Changing the style may result in a size change or a position change - processLayoutChanges(true); + processLayoutChanges(true, false); } const EventPropertyMap& @@ -159,15 +160,15 @@ VectorGraphicComponent::updateGraphic(const GraphicContentPtr& json) setDirty(kPropertyGraphic); // Recalculate the media bounds. This will internally do a layout - processLayoutChanges(true); + processLayoutChanges(true, false); g->clearDirty(); // Some flags may have been set; we clear them here because this is the first use of the graphic return true; } void -VectorGraphicComponent::processLayoutChanges(bool useDirtyFlag) +VectorGraphicComponent::processLayoutChanges(bool useDirtyFlag, bool first) { - CoreComponent::processLayoutChanges(useDirtyFlag); + CoreComponent::processLayoutChanges(useDirtyFlag, first); auto graphic = mCalculated.get(kPropertyGraphic); if (graphic.isGraphic()) { @@ -265,7 +266,7 @@ VectorGraphicComponent::processLayoutChanges(bool useDirtyFlag) } std::string -VectorGraphicComponent::getVisualContextType() +VectorGraphicComponent::getVisualContextType() const { return getCalculated(kPropertyGraphic).isNull() ? VISUAL_CONTEXT_TYPE_EMPTY : VISUAL_CONTEXT_TYPE_GRAPHIC; } @@ -348,4 +349,32 @@ VectorGraphicComponent::isTouchable() const return isFocusable(); } +std::vector +VectorGraphicComponent::getSources() { + std::vector sources; + + auto graphic = mCalculated.get(kPropertyGraphic); + if (graphic.isGraphic()) { + // Graphic is already present, nothing to load + return sources; + } + + auto source = mCalculated.get(kPropertySource); + if (source.isString()) { + auto graphicResource = mContext->getGraphic(source.getString()); + if (graphicResource.empty()) { + // Graphic is not a local resource, treat as URI + sources.emplace_back(source.getString()); + } + } + + return sources; +} + +void +VectorGraphicComponent::postProcessLayoutChanges() { + CoreComponent::postProcessLayoutChanges(); + MediaComponentTrait::postProcessLayoutChanges(); +} + } // namespace apl diff --git a/aplcore/src/component/videocomponent.cpp b/aplcore/src/component/videocomponent.cpp index ad567bd..7526d4e 100644 --- a/aplcore/src/component/videocomponent.cpp +++ b/aplcore/src/component/videocomponent.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -17,21 +17,16 @@ #include "apl/component/componentpropdef.h" #include "apl/component/videocomponent.h" #include "apl/component/yogaproperties.h" +#include "apl/media/mediamanager.h" +#include "apl/media/mediaobject.h" #include "apl/time/sequencer.h" namespace apl { -inline void -inlineResetMediaState(Component &component) -{ - auto& comp = dynamic_cast(component); - comp.resetMediaFetchState(); -} - CoreComponentPtr VideoComponent::create(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) { auto ptr = std::make_shared(context, std::move(properties), path); ptr->initialize(); @@ -40,7 +35,7 @@ VideoComponent::create(const ContextPtr& context, VideoComponent::VideoComponent(const ContextPtr& context, Properties&& properties, - const std::string& path) + const Path& path) : CoreComponent(context, std::move(properties), path) { } @@ -79,12 +74,17 @@ VideoComponent::propDefSet() const self.mCalculated.set(PLAYING_STATE.at(index), value.at(index)); }; + static auto resetMediaState = [](Component& component) { + auto& comp = dynamic_cast(component); + comp.resetMediaFetchState(); + }; + static ComponentPropDefSet sVideoComponentProperties = ComponentPropDefSet( CoreComponent::propDefSet(), MediaComponentTrait::propDefList()).add({ { kPropertyAudioTrack, kAudioTrackForeground, sAudioTrackMap, kPropInOut }, { kPropertyAutoplay, false, asOldBoolean, kPropInOut }, { kPropertyScale, kVideoScaleBestFit, sVideoScaleMap, kPropInOut }, - { kPropertySource, Object::EMPTY_ARRAY(), asMediaSourceArray, kPropDynamic | kPropInOut | kPropVisualContext, inlineResetMediaState }, + { kPropertySource, Object::EMPTY_ARRAY(), asMediaSourceArray, kPropDynamic | kPropInOut | kPropVisualContext, resetMediaState }, { kPropertyOnEnd, Object::EMPTY_ARRAY(), asCommand, kPropIn }, { kPropertyOnPause, Object::EMPTY_ARRAY(), asCommand, kPropIn }, { kPropertyOnPlay, Object::EMPTY_ARRAY(), asCommand, kPropIn }, @@ -288,26 +288,27 @@ VideoComponent::getTags(rapidjson::Value& outMap, rapidjson::Document::Allocator } std::string -VideoComponent::getVisualContextType() { +VideoComponent::getVisualContextType() const +{ return getCalculated(kPropertySource).empty() ? VISUAL_CONTEXT_TYPE_EMPTY : VISUAL_CONTEXT_TYPE_VIDEO; } -std::set +std::vector VideoComponent::getSources() { - std::set sources; + std::vector sources; for (auto& source : getCalculated(kPropertySource).getArray()) { - sources.emplace(source.getMediaSource().getUrl()); + sources.emplace_back(source.getMediaSource().getUrl()); } return sources; } void -VideoComponent::pendingMediaLoaded(const std::string& source, int stillPending) +VideoComponent::pendingMediaReturned(const MediaObjectPtr& object) { - MediaComponentTrait::pendingMediaLoaded(source, stillPending); + MediaComponentTrait::pendingMediaReturned(object); // Give viewhost a chance to start rendering this component if current track loaded. - if (source == getCurrentUrl()) { + if (object->state() == MediaObject::kReady && object->url() == getCurrentUrl()) { setDirty(kPropertyMediaState); } } diff --git a/aplcore/src/component/yogaproperties.cpp b/aplcore/src/component/yogaproperties.cpp index 3ff17d0..49ac206 100644 --- a/aplcore/src/component/yogaproperties.cpp +++ b/aplcore/src/component/yogaproperties.cpp @@ -14,6 +14,7 @@ */ #include "apl/component/yogaproperties.h" +#include "apl/component/corecomponent.h" #include "apl/primitives/dimension.h" #include "apl/primitives/object.h" #include "apl/utils/log.h" @@ -39,7 +40,7 @@ void setPositionType(YGNodeRef nodeRef, const Object& value, const Context&) { LOG_IF(DEBUG_FLEXBOX) << value << " [" << nodeRef << "]"; auto positionType = static_cast(value.asInt()); - if (positionType == kPositionRelative) + if (positionType == kPositionRelative || positionType == kPositionSticky) YGNodeStyleSetPositionType(nodeRef, YGPositionTypeRelative); else if (positionType == kPositionAbsolute) YGNodeStyleSetPositionType(nodeRef, YGPositionTypeAbsolute); @@ -157,10 +158,18 @@ void setBorder(YGNodeRef nodeRef, YGEdge edge, const Object& value, const Contex void setPosition(YGNodeRef nodeRef, YGEdge edge, const Object& value, const Context& context) { LOG_IF(DEBUG_FLEXBOX) << sEdgeToString[edge] << "->" << value << " [" << nodeRef << "]"; - if(value.isNull()) return; + + CoreComponent *component = static_cast(nodeRef->getContext()); + if (component && component->getCalculated(kPropertyPosition) == kPositionSticky) { + YGNodeStyleSetPosition(nodeRef, edge, NAN); + return; + } + Dimension position = value.asDimension(context); if (position.isRelative()) YGNodeStyleSetPositionPercent(nodeRef, edge, position.getValue()); + else if (value.isNull() || value.isAutoDimension()) + YGNodeStyleSetPosition(nodeRef, edge, NAN); else if (position.isAbsolute()) YGNodeStyleSetPosition(nodeRef, edge, position.getValue()); } @@ -305,6 +314,22 @@ setDisplay(YGNodeRef nodeRef, const Object& value, const Context&) { YGNodeStyleSetDisplay(nodeRef, display == kDisplayNone ? YGDisplayNone : YGDisplayFlex); } +void +setLayoutDirection(YGNodeRef nodeRef, const Object& value, const Context&) { + LOG_IF(DEBUG_FLEXBOX) << sLayoutDirectionMap.at(value.asInt()) << " [" << nodeRef << "]"; + auto layoutDirection = static_cast(value.asInt()); + switch (layoutDirection) { + case kLayoutDirectionLTR: + YGNodeStyleSetDirection(nodeRef, YGDirection::YGDirectionLTR); + break; + case kLayoutDirectionRTL: + YGNodeStyleSetDirection(nodeRef, YGDirection::YGDirectionRTL); + break; + case kLayoutDirectionInherit: // FALL_THROUGH + default: + YGNodeStyleSetDirection(nodeRef, YGDirection::YGDirectionInherit); + } +} } // namespace yn } // namespace apl diff --git a/aplcore/src/content/aplversion.cpp b/aplcore/src/content/aplversion.cpp index 73edf40..d8309c4 100644 --- a/aplcore/src/content/aplversion.cpp +++ b/aplcore/src/content/aplversion.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -27,7 +27,8 @@ static Bimap sVersionMap = { { APLVersion::kAPLVersion13, "1.3" }, { APLVersion::kAPLVersion14, "1.4" }, { APLVersion::kAPLVersion15, "1.5" }, - { APLVersion::kAPLVersion16, "1.6" } + { APLVersion::kAPLVersion16, "1.6" }, + { APLVersion::kAPLVersion17, "1.7" } }; bool @@ -47,4 +48,10 @@ APLVersion::isValid(const std::string& other) const return !(kAPLVersionIgnore == version) && isValid(version); } -} // namespace apl \ No newline at end of file +std::string +APLVersion::getDefaultReportedVersionString() +{ + return sVersionMap.at(kAPLVersionReported); +} + +} // namespace apl diff --git a/aplcore/src/content/content.cpp b/aplcore/src/content/content.cpp index 6ed3336..c7f9148 100644 --- a/aplcore/src/content/content.cpp +++ b/aplcore/src/content/content.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/directive.cpp b/aplcore/src/content/directive.cpp index 411196b..cf01902 100644 --- a/aplcore/src/content/directive.cpp +++ b/aplcore/src/content/directive.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/importrequest.cpp b/aplcore/src/content/importrequest.cpp index fe362de..610ad5d 100644 --- a/aplcore/src/content/importrequest.cpp +++ b/aplcore/src/content/importrequest.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/jsondata.cpp b/aplcore/src/content/jsondata.cpp index 8eb97e4..9b31d09 100644 --- a/aplcore/src/content/jsondata.cpp +++ b/aplcore/src/content/jsondata.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/metrics.cpp b/aplcore/src/content/metrics.cpp index c335f21..bee188b 100644 --- a/aplcore/src/content/metrics.cpp +++ b/aplcore/src/content/metrics.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/package.cpp b/aplcore/src/content/package.cpp index e379edb..2c5b149 100644 --- a/aplcore/src/content/package.cpp +++ b/aplcore/src/content/package.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/content/rootconfig.cpp b/aplcore/src/content/rootconfig.cpp index 048d79e..eabd12f 100644 --- a/aplcore/src/content/rootconfig.cpp +++ b/aplcore/src/content/rootconfig.cpp @@ -19,6 +19,7 @@ #include "apl/animation/coreeasing.h" #include "apl/component/textmeasurement.h" +#include "apl/media/coremediamanager.h" #include "apl/time/coretimemanager.h" #include "apl/utils/corelocalemethods.h" #include "apl/content/rootpropdef.h" @@ -42,6 +43,7 @@ Bimap sAnimationQualityBimap = { RootConfig::RootConfig() : mTextMeasurement( TextMeasurement::instance() ), + mMediaManager(std::static_pointer_cast(std::make_shared())), mTimeManager(std::static_pointer_cast(std::make_shared(0))), mLocaleMethods(std::static_pointer_cast(std::make_shared())), mDefaultComponentSize({ @@ -80,54 +82,56 @@ RootConfig::propDefSet() const { static RootPropDefSet sRootProperties = RootPropDefSet() .add({ - {RootProperty::kAgentName, "Default agent", asString}, - {RootProperty::kAgentVersion, "1.0", asString}, - {RootProperty::kAllowOpenUrl, false, asBoolean}, - {RootProperty::kDisallowVideo, false, asBoolean}, - {RootProperty::kAnimationQuality, kAnimationQualityNormal, sAnimationQualityBimap}, - {RootProperty::kDefaultIdleTimeout, 30000, asNumber}, - {RootProperty::kReportedVersion, "1.6", asString}, - {RootProperty::kEnforceTypeField, false, asBoolean}, - {RootProperty::kDefaultFontColor, Color(0xfafafaff), asColor}, - {RootProperty::kDefaultHighlightColor, Color(0x00caff4d), asColor}, - {RootProperty::kDefaultFontFamily, "sans-serif", asString}, - {RootProperty::kTrackProvenance, true, asBoolean}, - {RootProperty::kPagerChildCache, 1, asInteger}, - {RootProperty::kSequenceChildCache, 1, asInteger}, - {RootProperty::kUTCTime, 0, asNumber}, - {RootProperty::kLocalTimeAdjustment, 0, asNumber}, - {RootProperty::kDoublePressTimeout, 500, asNumber}, - {RootProperty::kLongPressTimeout, 1000, asNumber}, - {RootProperty::kPressedDuration, 64, asNumber}, - {RootProperty::kTapOrScrollTimeout, 100, asNumber}, - {RootProperty::kSwipeAwayFulfillDistancePercentageThreshold, 0.5, asNumber}, - {RootProperty::kSwipeAwayAnimationEasing, CoreEasing::bezier(0,0,0.58,1), asEasing}, - {RootProperty::kSwipeVelocityThreshold, 500, asNumber}, - {RootProperty::kSwipeMaxVelocity, 2000, asNumber}, - {RootProperty::kSwipeAngleTolerance, angleToSlope(40), asSlope}, - {RootProperty::kDefaultSwipeAnimationDuration, 200, asNumber}, - {RootProperty::kMaxSwipeAnimationDuration, 400, asNumber}, - {RootProperty::kMinimumFlingVelocity, 50, asNumber}, - {RootProperty::kMaximumFlingVelocity, 1200, asNumber}, - {RootProperty::kTickHandlerUpdateLimit, 16, asPositiveInteger}, - {RootProperty::kFontScale, 1.0, asNumber}, - {RootProperty::kScreenMode, kScreenModeNormal, sScreenModeBimap}, - {RootProperty::kScreenReader, false, asBoolean}, - {RootProperty::kPointerInactivityTimeout, 200, asNumber}, - {RootProperty::kPointerSlopThreshold, 40, asNumber}, - {RootProperty::kScrollCommandDuration, 1000, asNumber}, - {RootProperty::kScrollOnFocusDuration, 200, asNumber}, - {RootProperty::kScrollSnapDuration, 500, asNumber}, - {RootProperty::kDefaultPagerAnimationDuration, 600, asNumber}, - {RootProperty::kDefaultPagerAnimationEasing, CoreEasing::bezier(.42,0,.58,1), asEasing}, - {RootProperty::kScrollAngleSlopeVertical, angleToSlope(56), asSlope}, - {RootProperty::kScrollAngleSlopeHorizontal, angleToSlope(33), asSlope}, - {RootProperty::kScrollFlingVelocityLimitEasingVertical, CoreEasing::bezier(.6,.4,.35,.6), asEasing}, - {RootProperty::kScrollFlingVelocityLimitEasingHorizontal, CoreEasing::bezier(.42,.66,.5,1), asEasing}, - {RootProperty::kUEScrollerVelocityEasing, CoreEasing::bezier(.25,1,.5,1), asEasing}, - {RootProperty::kUEScrollerDurationEasing, CoreEasing::bezier(.65,0,.35,1), asEasing}, - {RootProperty::kUEScrollerMaxDuration, 3000, asNumber}, - {RootProperty::kUEScrollerDeceleration, 0.175, asNumber}, + {RootProperty::kAgentName, "Default agent", asString}, + {RootProperty::kAgentVersion, "1.0", asString}, + {RootProperty::kAllowOpenUrl, false, asBoolean}, + {RootProperty::kDisallowVideo, false, asBoolean}, + {RootProperty::kAnimationQuality, kAnimationQualityNormal, sAnimationQualityBimap}, + {RootProperty::kDefaultIdleTimeout, 30000, asNumber}, + {RootProperty::kReportedVersion, APLVersion::getDefaultReportedVersionString(), asString}, + {RootProperty::kEnforceTypeField, false, asBoolean}, + {RootProperty::kDefaultFontColor, Color(0xfafafaff), asColor}, + {RootProperty::kDefaultHighlightColor, Color(0x00caff4d), asColor}, + {RootProperty::kDefaultFontFamily, "sans-serif", asString}, + {RootProperty::kTrackProvenance, true, asBoolean}, + {RootProperty::kPagerChildCache, 1, asInteger}, + {RootProperty::kSequenceChildCache, 1, asInteger}, + {RootProperty::kUTCTime, 0, asNumber}, + {RootProperty::kLang, "", asString}, + {RootProperty::kLayoutDirection, kLayoutDirectionLTR, sLayoutDirectionMap}, + {RootProperty::kLocalTimeAdjustment, 0, asNumber}, + {RootProperty::kDoublePressTimeout, 500, asNumber}, + {RootProperty::kLongPressTimeout, 1000, asNumber}, + {RootProperty::kPressedDuration, 64, asNumber}, + {RootProperty::kTapOrScrollTimeout, 100, asNumber}, + {RootProperty::kSwipeAwayFulfillDistancePercentageThreshold, 0.5, asNumber}, + {RootProperty::kSwipeAwayAnimationEasing, CoreEasing::bezier(0,0,0.58,1), asEasing}, + {RootProperty::kSwipeVelocityThreshold, 500, asNumber}, + {RootProperty::kSwipeMaxVelocity, 2000, asNumber}, + {RootProperty::kSwipeAngleTolerance, angleToSlope(40), asSlope}, + {RootProperty::kDefaultSwipeAnimationDuration, 200, asNumber}, + {RootProperty::kMaxSwipeAnimationDuration, 400, asNumber}, + {RootProperty::kMinimumFlingVelocity, 50, asNumber}, + {RootProperty::kMaximumFlingVelocity, 1200, asNumber}, + {RootProperty::kTickHandlerUpdateLimit, 16, asPositiveInteger}, + {RootProperty::kFontScale, 1.0, asNumber}, + {RootProperty::kScreenMode, kScreenModeNormal, sScreenModeBimap}, + {RootProperty::kScreenReader, false, asBoolean}, + {RootProperty::kPointerInactivityTimeout, 200, asNumber}, + {RootProperty::kPointerSlopThreshold, 40, asNumber}, + {RootProperty::kScrollCommandDuration, 1000, asNumber}, + {RootProperty::kScrollOnFocusDuration, 200, asNumber}, + {RootProperty::kScrollSnapDuration, 500, asNumber}, + {RootProperty::kDefaultPagerAnimationDuration, 600, asNumber}, + {RootProperty::kDefaultPagerAnimationEasing, CoreEasing::bezier(.42,0,.58,1), asEasing}, + {RootProperty::kScrollAngleSlopeVertical, angleToSlope(56), asSlope}, + {RootProperty::kScrollAngleSlopeHorizontal, angleToSlope(33), asSlope}, + {RootProperty::kScrollFlingVelocityLimitEasingVertical, CoreEasing::bezier(.6,.4,.35,.6), asEasing}, + {RootProperty::kScrollFlingVelocityLimitEasingHorizontal, CoreEasing::bezier(.42,.66,.5,1), asEasing}, + {RootProperty::kUEScrollerVelocityEasing, CoreEasing::bezier(.25,1,.5,1), asEasing}, + {RootProperty::kUEScrollerDurationEasing, CoreEasing::bezier(.65,0,.35,1), asEasing}, + {RootProperty::kUEScrollerMaxDuration, 3000, asNumber}, + {RootProperty::kUEScrollerDeceleration, 0.175, asNumber}, }); return sRootProperties; } diff --git a/aplcore/src/content/rootproperties.cpp b/aplcore/src/content/rootproperties.cpp index 271b905..9969e5e 100644 --- a/aplcore/src/content/rootproperties.cpp +++ b/aplcore/src/content/rootproperties.cpp @@ -33,6 +33,7 @@ Bimap sRootPropertyBimap = { { RootProperty::kPagerChildCache, "pagerChildCache" }, { RootProperty::kSequenceChildCache, "sequenceChildCache" }, { RootProperty::kUTCTime, "utcTime" }, + { RootProperty::kLang, "lang" }, { RootProperty::kLocalTimeAdjustment, "localTimeAdjustment" }, { RootProperty::kDoublePressTimeout, "doublePressTimeout" }, { RootProperty::kLongPressTimeout, "longPressTimeout" }, diff --git a/aplcore/src/content/viewport.cpp b/aplcore/src/content/viewport.cpp index 8afd0b4..fe1f93b 100644 --- a/aplcore/src/content/viewport.cpp +++ b/aplcore/src/content/viewport.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. diff --git a/aplcore/src/datagrammar/CMakeLists.txt b/aplcore/src/datagrammar/CMakeLists.txt index 54a0263..980eb8c 100644 --- a/aplcore/src/datagrammar/CMakeLists.txt +++ b/aplcore/src/datagrammar/CMakeLists.txt @@ -14,6 +14,10 @@ target_sources_local(apl PRIVATE boundsymbol.cpp + bytecode.cpp + bytecodeassembler.cpp + bytecodeevaluator.cpp + bytecodeoptimizer.cpp functions.cpp - node.cpp + grammarerror.cpp ) \ No newline at end of file diff --git a/aplcore/src/datagrammar/boundsymbol.cpp b/aplcore/src/datagrammar/boundsymbol.cpp index 0a63cfd..06d5889 100644 --- a/aplcore/src/datagrammar/boundsymbol.cpp +++ b/aplcore/src/datagrammar/boundsymbol.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * 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. @@ -31,6 +31,14 @@ BoundSymbol::toDebugString() const { return "BoundSymbol<" + mName + ">"; } +bool +BoundSymbol::operator==(const BoundSymbol& rhs) const +{ + return !mContext.owner_before(rhs.mContext) && + !rhs.mContext.owner_before(mContext) && + mName == rhs.mName; +} + streamer& operator<<(streamer& os, const BoundSymbol& boundSymbol) { os << boundSymbol.toDebugString(); return os; diff --git a/aplcore/src/datagrammar/bytecode.cpp b/aplcore/src/datagrammar/bytecode.cpp new file mode 100644 index 0000000..91490f3 --- /dev/null +++ b/aplcore/src/datagrammar/bytecode.cpp @@ -0,0 +1,264 @@ +/** + * 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/datagrammar/bytecode.h" +#include "apl/datagrammar/bytecodeoptimizer.h" +#include "apl/datagrammar/bytecodeevaluator.h" +#include "apl/datagrammar/boundsymbol.h" +#include "apl/engine/context.h" +#include "apl/utils/session.h" + +namespace apl { +namespace datagrammar { + +Object +ByteCode::eval() const +{ + // Check for a trivial instruction + if (mInstructions.size() == 1) { + const auto& cmd = mInstructions.at(0); + switch (cmd.type) { + case BC_OPCODE_LOAD_BOUND_SYMBOL: + return mData.at(cmd.value).eval(); + + case BC_OPCODE_LOAD_DATA: + return mData.at(cmd.value); + + case BC_OPCODE_LOAD_CONSTANT: + return getConstant(static_cast(cmd.value)); + + case BC_OPCODE_LOAD_IMMEDIATE: + return cmd.value; + + default: + CONSOLE_CTP(mContext) << "Unexpected trivial instruction " << cmd.type; + return Object::NULL_OBJECT(); + } + } + + ByteCodeEvaluator evaluator(*this); + evaluator.advance(); + + if (evaluator.isDone()) + return evaluator.getResult(); + + CONSOLE_CTP(mContext) << "Unable to evaluate byte code data"; + return Object::NULL_OBJECT(); +} + +Object +ByteCode::simplify() +{ + ByteCodeEvaluator evaluator(*this); + evaluator.advance(); + if (evaluator.isDone() && evaluator.isConstant()) + return evaluator.getResult(); + + return shared_from_this(); +} + +void +ByteCode::symbols(SymbolReferenceMap& symbols) +{ + auto context = mContext.lock(); + if (!context) + return; + + if (!mOptimized) { + ByteCodeOptimizer::optimize(*this); + mOptimized = true; + } + + // Find all symbol references by searching the opcodes. + SymbolReference ref; + Object operand; + + for (auto &cmd : mInstructions) { + switch (cmd.type) { + case BC_OPCODE_LOAD_DATA: + operand = mData[cmd.value].asString(); + break; + + case BC_OPCODE_LOAD_IMMEDIATE: + operand = cmd.value; + break; + + case BC_OPCODE_LOAD_BOUND_SYMBOL: + // Store the old symbol + if (!ref.first.empty()) + symbols.emplace(ref); + + ref = mData[cmd.value].getBoundSymbol()->getSymbol(); + operand = Object::NULL_OBJECT(); + break; + + case BC_OPCODE_ATTRIBUTE_ACCESS: + if (!ref.first.empty()) + ref.first += mData[cmd.value].asString() + "/"; + operand = Object::NULL_OBJECT(); + break; + + case BC_OPCODE_ARRAY_ACCESS: + if (!ref.first.empty()) { + if (operand.isString() || operand.isNumber()) + ref.first += operand.asString() + "/"; + else { + symbols.emplace(ref); + ref.first.clear(); + } + } + operand = Object::NULL_OBJECT(); + break; + + default: + if (!ref.first.empty()) { + symbols.emplace(ref); + ref.first.clear(); + } + operand = Object::NULL_OBJECT(); + break; + } + } + + if (!ref.first.empty()) + symbols.emplace(ref); +} + +ContextPtr +ByteCode::getContext() const +{ + return mContext.lock(); +} + +void +ByteCode::dump() const +{ + LOG(LogLevel::kDebug) << "Data"; + for (int i = 0; i < mData.size(); i++) + LOG(LogLevel::kDebug) << " [" << i << "] " << mData.at(i).toDebugString(); + + LOG(LogLevel::kDebug) << "Instructions"; + for (int pc = 0; pc < mInstructions.size(); pc++) + LOG(LogLevel::kDebug) << instructionAsString(pc); +} + + +// This must match ByteCodeOpcode +static const char *BYTE_CODE_COMMAND_STRING[] = { + "NOP ", + "CALL_FUNCTION ", // value = # of arguments to pass + "LOAD_CONSTANT ", // value = constant enum + "LOAD_IMMEDIATE ", // value = number to load + "LOAD_DATA ", // value = index of operand + "LOAD_BOUND_SYMBOL ", // value = index of operand containing bound symbol + "ATTRIBUTE_ACCESS ", // value = index of operand containing attribute name + "ARRAY_ACCESS ", // Load an element from an array or map + "UNARY_PLUS ", + "UNARY_MINUS ", + "UNARY_NOT ", + "BINARY_MULTIPLY ", + "BINARY_DIVIDE ", + "BINARY_REMAINDER ", + "BINARY_ADD ", + "BINARY_SUBTRACT ", + "COMPARE_OP ", // value = comparison enum (<, <=, ==, !=, >, >=) + "JUMP ", // value = offset + "JUMP_IF_FALSE_OR_POP ", // value = offset + "JUMP_IF_TRUE_OR_POP ", // value = offset + "JUMP_IF_NOT_NULL_OR_POP", // value = offset + "POP_JUMP_IF_FALSE ", // value = offset + "MERGE_AS_STRING ", + "APPEND_ARRAY ", + "APPEND_MAP ", +}; + +// This must match the enumerated ByteCodeComparison values +static const char *BYTE_CODE_COMPARE_STRING[] = { + "<", + "<=", + "==", + "!=", + ">", + ">=" +}; + +// This must match the enumerated ByteCodeConstant values +static const char *BYTE_CODE_CONSTANT_STRING[] = { + "null", + "false", + "true", + "empty_string", + "empty_array", + "empty_map" +}; + + +static std::string +lineNumber(int i, int num) +{ + auto result = std::to_string(i); + int offset = std::max(num - static_cast(result.size()), 0); + return std::string(offset, ' ') + result; +} + + +std::string +ByteCode::instructionAsString(int pc) const +{ + if (pc < 0 || pc >= mInstructions.size()) + return lineNumber(pc, 6); + + auto& cmd = mInstructions.at(pc); + auto result = lineNumber(pc, 6) + " " + cmd.toString(); + + switch (cmd.type) { + case BC_OPCODE_CALL_FUNCTION: + result += " argument_count=" + std::to_string(cmd.value); + break; + case BC_OPCODE_LOAD_CONSTANT: + result += std::string(" ") + BYTE_CODE_CONSTANT_STRING[cmd.value]; + break; + case BC_OPCODE_LOAD_DATA: + case BC_OPCODE_ATTRIBUTE_ACCESS: + case BC_OPCODE_LOAD_BOUND_SYMBOL: + result += " [" + mData.at(cmd.value).toDebugString() + "]"; + break; + case BC_OPCODE_COMPARE_OP: + result += std::string(" ") + BYTE_CODE_COMPARE_STRING[cmd.value]; + break; + case BC_OPCODE_JUMP: + case BC_OPCODE_JUMP_IF_FALSE_OR_POP: + case BC_OPCODE_JUMP_IF_TRUE_OR_POP: + case BC_OPCODE_JUMP_IF_NOT_NULL_OR_POP: + case BC_OPCODE_POP_JUMP_IF_FALSE: + result += " GOTO " + std::to_string(pc + cmd.value + 1); + break; + default: + break; + } + + return result; +} + + +std::string +ByteCodeInstruction::toString() const +{ + return std::string(BYTE_CODE_COMMAND_STRING[type]) + " (" + std::to_string(value) + ")"; +} + + +} // namespace datagrammar +} // namespace apl \ No newline at end of file diff --git a/aplcore/src/datagrammar/bytecodeassembler.cpp b/aplcore/src/datagrammar/bytecodeassembler.cpp new file mode 100644 index 0000000..d8cea62 --- /dev/null +++ b/aplcore/src/datagrammar/bytecodeassembler.cpp @@ -0,0 +1,496 @@ +/** + * 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 "apl/datagrammar/boundsymbol.h" +#include "apl/datagrammar/bytecodeassembler.h" +#include "apl/datagrammar/bytecode.h" +#include "apl/datagrammar/bytecodeevaluator.h" +#include "apl/datagrammar/databindingrules.h" +#include "apl/datagrammar/databindingerrors.h" + +#include "apl/engine/context.h" +#include "apl/utils/log.h" +#include "apl/utils/session.h" + +namespace pegtl = tao::TAO_PEGTL_NAMESPACE; + +namespace apl { +namespace datagrammar { + + +// NOTE: This might be faster without the lookup - but we'd have to extend the +// PEGTL grammar. +static const std::map sByteCodeBinaryOperators = { + {"*", {BC_ORDER_MULTIPLICATIVE, BC_OPCODE_BINARY_MULTIPLY, 0}}, + {"/", {BC_ORDER_MULTIPLICATIVE, BC_OPCODE_BINARY_DIVIDE, 0}}, + {"%", {BC_ORDER_MULTIPLICATIVE, BC_OPCODE_BINARY_REMAINDER, 0}}, + {"+", {BC_ORDER_ADDITIVE, BC_OPCODE_BINARY_ADD, 0}}, + {"-", {BC_ORDER_ADDITIVE, BC_OPCODE_BINARY_SUBTRACT, 0}}, + {"<", {BC_ORDER_COMPARISON, BC_OPCODE_COMPARE_OP, BC_COMPARE_LESS_THAN}}, + {">", {BC_ORDER_COMPARISON, BC_OPCODE_COMPARE_OP, BC_COMPARE_GREATER_THAN}}, + {"<=", {BC_ORDER_COMPARISON, BC_OPCODE_COMPARE_OP, BC_COMPARE_LESS_THAN_OR_EQUAL}}, + {">=", {BC_ORDER_COMPARISON, BC_OPCODE_COMPARE_OP, BC_COMPARE_GREATER_THAN_OR_EQUAL}}, + {"==", {BC_ORDER_EQUALITY, BC_OPCODE_COMPARE_OP, BC_COMPARE_EQUAL}}, + {"!=", {BC_ORDER_EQUALITY, BC_OPCODE_COMPARE_OP, BC_COMPARE_NOT_EQUAL}}, + {"[", {BC_ORDER_FIELD_OR_FUNCTION, BC_OPCODE_ARRAY_ACCESS, 0}} +}; + +static const std::map sByteCodeUnaryOperators = { + {'+', {BC_ORDER_UNARY, BC_OPCODE_UNARY_PLUS, 0}}, + {'-', {BC_ORDER_UNARY, BC_OPCODE_UNARY_MINUS, 0}}, + {'!', {BC_ORDER_UNARY, BC_OPCODE_UNARY_NOT, 0}}, +}; + +// Uncomment this line to get line-by-line tracing in the PEGTL grammar +// #define PEGTL_ERROR_CTRL datagrammar::traced_error_control + +#ifndef PEGTL_ERROR_CTRL +#define PEGTL_ERROR_CTRL datagrammar::error_control +#endif + + +Object +ByteCodeAssembler::parse(const Context& context, const std::string& value) +{ + // Short-circuit the parser if there are no embedded expressions + if (value.find("${") == std::string::npos) + return value; + + pegtl::string_input<> in(value, ""); + try { + datagrammar::ByteCodeAssembler assembler(context); + + pegtl::parse(in, assembler); + return assembler.retrieve(); + } + catch (const pegtl::parse_error& e) { + const auto p = e.positions.front(); + CONSOLE_CTX(context) << "Syntax error: " << e.what(); + CONSOLE_CTX(context) << in.line_at(p); + CONSOLE_CTX(context) << std::string(p.byte_in_line, ' ') << "^"; + } + + return value; +} + +ByteCodeAssembler::ByteCodeAssembler(const Context& context) + : mContext(std::const_pointer_cast(context.shared_from_this())), + mCode{CodeUnit(mContext)} +{ + mInstructionRef = &mCode.byteCode->mInstructions; + mDataRef = &mCode.byteCode->mData; + mOperatorsRef = &mCode.operators; +} + + +Object +ByteCodeAssembler::retrieve() const +{ + return mCode.byteCode; +} + +void +ByteCodeAssembler::loadOperand(const apl::Object& value) +{ + // The "value" of the command is the location in the operand list + auto len = mDataRef->size(); + mDataRef->emplace_back(value); + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_LOAD_DATA, asBCI(len)}); +} + +void +ByteCodeAssembler::loadConstant(ByteCodeConstant value) +{ + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_LOAD_CONSTANT, value}); +} + +void +ByteCodeAssembler::loadImmediate(bciValueType value) +{ + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_LOAD_IMMEDIATE, value}); +} + +void +ByteCodeAssembler::loadGlobal(const std::string& name) +{ + auto cr = mContext->find(name); + if (cr.empty()) { // Not found -> load NULL + mInstructionRef->emplace_back( + ByteCodeInstruction{BC_OPCODE_LOAD_CONSTANT, BC_CONSTANT_NULL}); + return; + } + + auto len = asBCI(mDataRef->size()); + // Immutable globals can be replaced by a constant value + if (!cr.object().isMutable()) { + mDataRef->emplace_back(cr.object().value()); + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_LOAD_DATA, len}); + return; + } + + // Mutable globals have a bound symbol + mDataRef->emplace_back(Object(std::make_shared(cr.context(), name))); + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_LOAD_BOUND_SYMBOL, len}); +} + +void +ByteCodeAssembler::pushAttributeName(const std::string &name) +{ + // The "value" of the command is the location in the operand list + auto len = mDataRef->size(); + mDataRef->emplace_back(name); + mOperatorsRef->push_back({BC_ORDER_ATTRIBUTE, BC_OPCODE_ATTRIBUTE_ACCESS, asBCI(len)}); +} + +void +ByteCodeAssembler::loadAttribute() +{ + // The "value" of the command is the location in the operand list + auto& back = mOperatorsRef->back(); + assert(back.order == BC_ORDER_ATTRIBUTE); + mInstructionRef->emplace_back(ByteCodeInstruction{BC_OPCODE_ATTRIBUTE_ACCESS, asBCI(back.value)}); + mOperatorsRef->pop_back(); +} + +void +ByteCodeAssembler::pushUnaryOperator(char ch) +{ + auto iter = sByteCodeUnaryOperators.find(ch); + assert(iter != sByteCodeUnaryOperators.end()); + + // TODO: Should we just hold a pointer instead of copying the entire object? + mOperatorsRef->push_back(iter->second); +} + +void +ByteCodeAssembler::reduceUnary() +{ + while (!mOperatorsRef->empty()) { + auto& back = mOperatorsRef->back(); + if (back.order != BC_ORDER_UNARY) + return; + + mInstructionRef->emplace_back(ByteCodeInstruction{back.command, asBCI(back.value)}); + mOperatorsRef->pop_back(); + } +} + +void +ByteCodeAssembler::pushBinaryOperator(const std::string& op) +{ + auto iter = sByteCodeBinaryOperators.find(op); + assert(iter != sByteCodeBinaryOperators.end()); + + reduceBinary(iter->second.order); + mOperatorsRef->push_back(iter->second); +} + +void +ByteCodeAssembler::reduceBinary(ByteCodeOrder order) +{ + // Reduce the top operator on the stack if it matches this order + auto back = mOperatorsRef->rbegin(); + if (back == mOperatorsRef->rend() || back->order != order) + return; + + mInstructionRef->emplace_back(ByteCodeInstruction{back->command, asBCI(back->value)}); + mOperatorsRef->pop_back(); +} + + +/** + * Called once per AND statement on the stack (that's a '&&') + * Each AND statement gets a single JUMP_IF_FALSE_OR_POP statement + * with the value set to the offset to be added to the stack counter. + * This creates a short-circuiting AND operation. + * + * We push the AND operator on the operators stack and use the value field + * of that operator to store the absolute position of the JUMP_IF_FALSE_OR_POP + * statement in the command stack. When all AND statements have been + * gathered, walk backwards through the operator stack and fix up all of the + * JUMP locations. + * + * JUMP_IF_FALSE_OR_POP