From c2d1329e1e2211c372f8a696f7afd69edcdf23df Mon Sep 17 00:00:00 2001 From: Richard Huang Date: Thu, 7 Jan 2016 16:05:12 +0000 Subject: [PATCH] JavaScript binding for ItemView Change-Id: If4ba2434a2ea7a22cacd13d431dbe54bf56540b9 --- .../scrollable/item-view/item-view-impl.cpp | 116 ++++- .../controls/scrollable/item-view/item-view-impl.h | 18 + .../controls/scrollable/scrollable-impl.cpp | 13 +- .../controls/scrollable/item-view/item-view.h | 23 +- .../public-api/controls/scrollable/scrollable.h | 5 +- docs/content/images/item-view/list.png | Bin 0 -> 23136 bytes node-addon/binding.gyp | 3 + node-addon/examples/images/icon-0.png | Bin 0 -> 2465 bytes node-addon/examples/images/icon-1.png | Bin 0 -> 2146 bytes node-addon/examples/images/icon-2.png | Bin 0 -> 1906 bytes node-addon/{ => examples/images}/image-1.jpg | Bin node-addon/{ => examples/images}/image-2.jpg | Bin node-addon/examples/item-view.js | 142 +++++++ node-addon/{ => examples}/line-mesh.js | 2 +- node-addon/{ => examples}/mesh-morph.js | 3 +- node-addon/{ => examples}/point-mesh.js | 4 +- node-addon/examples/scripts/item-template.json | 92 ++++ node-addon/{ => examples}/texture-mesh.js | 4 +- node-addon/item-template.json | 92 ++++ packaging/dali-addon.spec | 2 +- plugins/dali-script-v8/docs/content/constants.js | 7 + .../dali-script-v8/docs/content/item-factory.js | 177 ++++++++ plugins/dali-script-v8/docs/content/item-view.js | 62 +++ plugins/dali-script-v8/file.list | 3 + plugins/dali-script-v8/src/actors/actor-api.cpp | 13 +- plugins/dali-script-v8/src/actors/actor-api.h | 8 - .../dali-script-v8/src/actors/actor-wrapper.cpp | 42 +- plugins/dali-script-v8/src/actors/actor-wrapper.h | 13 +- .../src/constants/constants-wrapper.cpp | 7 + .../src/controls/control-wrapper.cpp | 340 +++++++++++++++ .../dali-script-v8/src/controls/control-wrapper.h | 127 ++++++ .../src/controls/item-factory-wrapper.cpp | 467 +++++++++++++++++++++ .../src/controls/item-factory-wrapper.h | 146 +++++++ .../dali-script-v8/src/controls/item-view-api.cpp | 412 ++++++++++++++++++ .../dali-script-v8/src/controls/item-view-api.h | 59 +++ plugins/dali-script-v8/src/dali-wrapper.cpp | 5 +- .../dali-script-v8/src/object/handle-wrapper.cpp | 74 +++- plugins/dali-script-v8/src/object/handle-wrapper.h | 7 + .../src/shared/base-wrapped-object.h | 2 + plugins/dali-script-v8/src/utils/v8-utils.cpp | 114 ++++- plugins/dali-script-v8/src/utils/v8-utils.h | 8 + 41 files changed, 2509 insertions(+), 103 deletions(-) create mode 100755 docs/content/images/item-view/list.png create mode 100755 node-addon/examples/images/icon-0.png create mode 100755 node-addon/examples/images/icon-1.png create mode 100755 node-addon/examples/images/icon-2.png rename node-addon/{ => examples/images}/image-1.jpg (100%) rename node-addon/{ => examples/images}/image-2.jpg (100%) create mode 100644 node-addon/examples/item-view.js rename node-addon/{ => examples}/line-mesh.js (98%) rename node-addon/{ => examples}/mesh-morph.js (99%) rename node-addon/{ => examples}/point-mesh.js (98%) create mode 100644 node-addon/examples/scripts/item-template.json rename node-addon/{ => examples}/texture-mesh.js (98%) create mode 100644 node-addon/item-template.json create mode 100644 plugins/dali-script-v8/docs/content/item-factory.js create mode 100644 plugins/dali-script-v8/docs/content/item-view.js create mode 100644 plugins/dali-script-v8/src/controls/control-wrapper.cpp create mode 100644 plugins/dali-script-v8/src/controls/control-wrapper.h create mode 100644 plugins/dali-script-v8/src/controls/item-factory-wrapper.cpp create mode 100644 plugins/dali-script-v8/src/controls/item-factory-wrapper.h create mode 100644 plugins/dali-script-v8/src/controls/item-view-api.cpp create mode 100644 plugins/dali-script-v8/src/controls/item-view-api.h diff --git a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp index aa6443d..86a869e 100644 --- a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.cpp @@ -42,21 +42,6 @@ using namespace Dali; namespace // Unnamed namespace { -//Type registration - -DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL) - -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutPosition", FLOAT, LAYOUT_POSITION) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollSpeed", FLOAT, SCROLL_SPEED) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "overshoot", FLOAT, OVERSHOOT) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollDirection", VECTOR2, SCROLL_DIRECTION) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutOrientation", INTEGER, LAYOUT_ORIENTATION) -DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollContentSize", FLOAT, SCROLL_CONTENT_SIZE) - -DALI_SIGNAL_REGISTRATION( Toolkit, ItemView, "layoutActivated", LAYOUT_ACTIVATED_SIGNAL ) - -DALI_TYPE_REGISTRATION_END() - const float DEFAULT_MINIMUM_SWIPE_SPEED = 1.0f; const float DEFAULT_MINIMUM_SWIPE_DISTANCE = 3.0f; const float DEFAULT_WHEEL_SCROLL_DISTANCE_STEP_PROPORTION = 0.1f; @@ -256,6 +241,27 @@ namespace Internal namespace // unnamed namespace { +//Type registration + +DALI_TYPE_REGISTRATION_BEGIN( Toolkit::ItemView, Toolkit::Scrollable, NULL) + +DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "minimumSwipeSpeed", FLOAT, MINIMUM_SWIPE_SPEED ) +DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "minimumSwipeDistance", FLOAT, MINIMUM_SWIPE_DISTANCE ) +DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "wheelScrollDistanceStep", FLOAT, WHELL_SCROLL_DISTANCE_SPEED ) +DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "snapToItemEnabled", BOOLEAN, SNAP_TO_ITEM_ENABLED ) +DALI_PROPERTY_REGISTRATION( Toolkit, ItemView, "refreshInterval", FLOAT, REFRESH_INTERVAL ) + +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutPosition", FLOAT, LAYOUT_POSITION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollSpeed", FLOAT, SCROLL_SPEED) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "overshoot", FLOAT, OVERSHOOT) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollDirection", VECTOR2, SCROLL_DIRECTION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "layoutOrientation", INTEGER, LAYOUT_ORIENTATION) +DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, ItemView, "scrollContentSize", FLOAT, SCROLL_CONTENT_SIZE) + +DALI_SIGNAL_REGISTRATION( Toolkit, ItemView, "layoutActivated", LAYOUT_ACTIVATED_SIGNAL ) + +DALI_TYPE_REGISTRATION_END() + bool FindById( const ItemContainer& items, ItemId id ) { for( ConstItemIter iter = items.begin(); items.end() != iter; ++iter ) @@ -1692,6 +1698,86 @@ bool ItemView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* return connected; } +void ItemView::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ) +{ + Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( Dali::BaseHandle( object ) ); + + if( itemView ) + { + ItemView& itemViewImpl( GetImpl( itemView ) ); + switch( index ) + { + case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED: + { + itemViewImpl.SetMinimumSwipeSpeed( value.Get() ); + break; + } + case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE: + { + itemViewImpl.SetMinimumSwipeDistance( value.Get() ); + break; + } + case Toolkit::ItemView::Property::WHELL_SCROLL_DISTANCE_SPEED: + { + itemViewImpl.SetWheelScrollDistanceStep( value.Get() ); + break; + } + case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED: + { + itemViewImpl.SetAnchoring( value.Get() ); + break; + } + case Toolkit::ItemView::Property::REFRESH_INTERVAL: + { + itemViewImpl.SetRefreshInterval( value.Get() ); + break; + } + } + } +} + +Property::Value ItemView::GetProperty( BaseObject* object, Property::Index index ) +{ + Property::Value value; + + Toolkit::ItemView itemView = Toolkit::ItemView::DownCast( Dali::BaseHandle( object ) ); + + if( itemView ) + { + ItemView& itemViewImpl( GetImpl( itemView ) ); + switch( index ) + { + case Toolkit::ItemView::Property::MINIMUM_SWIPE_SPEED: + { + value = itemViewImpl.GetMinimumSwipeSpeed(); + break; + } + case Toolkit::ItemView::Property::MINIMUM_SWIPE_DISTANCE: + { + value = itemViewImpl.GetMinimumSwipeDistance(); + break; + } + case Toolkit::ItemView::Property::WHELL_SCROLL_DISTANCE_SPEED: + { + value = itemViewImpl.GetWheelScrollDistanceStep(); + break; + } + case Toolkit::ItemView::Property::SNAP_TO_ITEM_ENABLED: + { + value = itemViewImpl.GetAnchoring(); + break; + } + case Toolkit::ItemView::Property::REFRESH_INTERVAL: + { + value = itemViewImpl.GetRefreshInterval(); + break; + } + } + } + + return value; +} + } // namespace Internal } // namespace Toolkit diff --git a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h index a18618d..2b3a52f 100644 --- a/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h +++ b/dali-toolkit/internal/controls/scrollable/item-view/item-view-impl.h @@ -308,6 +308,24 @@ public: */ static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + //properties + + /** + * Called when a property of an object of this type is set. + * @param[in] object The object whose property is set. + * @param[in] index The property index. + * @param[in] value The new property value. + */ + static void SetProperty( BaseObject* object, Property::Index index, const Property::Value& value ); + + /** + * Called to retrieve a property of an object of this type. + * @param[in] object The object whose property is to be retrieved. + * @param[in] index The property index. + * @return The current value of the property. + */ + static Property::Value GetProperty( BaseObject* object, Property::Index index ); + private: /** diff --git a/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp b/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp index 98ab55b..e1f79ca 100644 --- a/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp +++ b/dali-toolkit/internal/controls/scrollable/scrollable-impl.cpp @@ -48,7 +48,8 @@ DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Scrollable, Toolkit::Control, Create ); DALI_PROPERTY_REGISTRATION( Toolkit, Scrollable, "overshootEffectColor", VECTOR4, OVERSHOOT_EFFECT_COLOR ) DALI_PROPERTY_REGISTRATION( Toolkit, Scrollable, "overshootAnimationSpeed", FLOAT, OVERSHOOT_ANIMATION_SPEED ) -const int OVERSHOOT_SIZE = Dali::Toolkit::Scrollable::Property::OVERSHOOT_ANIMATION_SPEED + 1; // OVERSHOOT_SIZE is not public yet +DALI_PROPERTY_REGISTRATION( Toolkit, Scrollable, "overshootEnabled", BOOLEAN, OVERSHOOT_ENABLED ) +const int OVERSHOOT_SIZE = Dali::Toolkit::Scrollable::Property::OVERSHOOT_ENABLED + 1; // OVERSHOOT_SIZE is not public yet Dali::PropertyRegistration p1( typeRegistration, "overshootSize", OVERSHOOT_SIZE, Property::VECTOR2, Dali::Toolkit::Internal::Scrollable::SetProperty, Dali::Toolkit::Internal::Scrollable::GetProperty ); DALI_ANIMATABLE_PROPERTY_REGISTRATION( Toolkit, Scrollable, "scrollRelativePosition", VECTOR2, SCROLL_RELATIVE_POSITION) @@ -194,6 +195,11 @@ void Scrollable::SetProperty( BaseObject* object, Property::Index index, const P scrollableImpl.SetOvershootAnimationSpeed( value.Get() ); break; } + case Toolkit::Scrollable::Property::OVERSHOOT_ENABLED: + { + scrollableImpl.SetOvershootEnabled( value.Get() ); + break; + } case OVERSHOOT_SIZE: // OVERSHOOT_SIZE is not public yet { Vector2 input; @@ -229,6 +235,11 @@ Property::Value Scrollable::GetProperty( BaseObject* object, Property::Index ind value = scrollableImpl.GetOvershootAnimationSpeed(); break; } + case Toolkit::Scrollable::Property::OVERSHOOT_ENABLED: + { + value = scrollableImpl.IsOvershootEnabled(); + break; + } case OVERSHOOT_SIZE: // OVERSHOOT_SIZE is not public yet { value = scrollableImpl.mOvershootSize; diff --git a/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h b/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h index e790a82..5431d25 100644 --- a/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h +++ b/dali-toolkit/public-api/controls/scrollable/item-view/item-view.h @@ -63,6 +63,9 @@ public: enum PropertyRange { + PROPERTY_START_INDEX = Toolkit::Scrollable::PROPERTY_END_INDEX + 1, ///< @since DALi 1.1.18 + PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000, ///< Reserve property indices, @since DALi 1.1.18 + ANIMATABLE_PROPERTY_START_INDEX = Toolkit::Scrollable::ANIMATABLE_PROPERTY_END_INDEX + 1, ANIMATABLE_PROPERTY_END_INDEX = ANIMATABLE_PROPERTY_START_INDEX + 1000 ///< Reserve animatable property indices }; @@ -74,12 +77,20 @@ public: { enum { - LAYOUT_POSITION = ANIMATABLE_PROPERTY_START_INDEX, ///< Property, name "layoutPosition", type float - SCROLL_SPEED, ///< Property, name "scrollSpeed", type float - OVERSHOOT, ///< Property, name "overshoot", type float - SCROLL_DIRECTION, ///< Property, name "scrollDirection", type Vector2 - LAYOUT_ORIENTATION, ///< Property, name "layoutOrientation", type integer - SCROLL_CONTENT_SIZE ///< Property, name "scrollContentSize", type float + // Event side properties + MINIMUM_SWIPE_SPEED = PROPERTY_START_INDEX, ///< Property, name "minimumSwipeSpeed", @see SetMinimumSwipeSpeed(), type float, @since DALi 1.1.18 + MINIMUM_SWIPE_DISTANCE, ///< Property, name "minimumSwipeDistance", @see SetMinimumSwipeDistance(), type float, @since DALi 1.1.18 + WHELL_SCROLL_DISTANCE_SPEED, ///< Property, name "wheelScrollDistanceStep", @see SetWheelScrollDistanceStep(), type float, @since DALi 1.1.18 + SNAP_TO_ITEM_ENABLED, ///< Property, name "snapToItemEnabled", @see SetAnchoring(), type bool, @since DALi 1.1.18 + REFRESH_INTERVAL, ///< Property, name "refreshInterval", @see SetRefreshInterval(), type float, @since DALi 1.1.18 + + // Animatable properties + LAYOUT_POSITION = ANIMATABLE_PROPERTY_START_INDEX, ///< Property, name "layoutPosition", type float + SCROLL_SPEED, ///< Property, name "scrollSpeed", type float + OVERSHOOT, ///< Property, name "overshoot", type float + SCROLL_DIRECTION, ///< Property, name "scrollDirection", type Vector2 + LAYOUT_ORIENTATION, ///< Property, name "layoutOrientation", type integer + SCROLL_CONTENT_SIZE ///< Property, name "scrollContentSize", type float }; }; diff --git a/dali-toolkit/public-api/controls/scrollable/scrollable.h b/dali-toolkit/public-api/controls/scrollable/scrollable.h index 76dbb07..3c22468 100644 --- a/dali-toolkit/public-api/controls/scrollable/scrollable.h +++ b/dali-toolkit/public-api/controls/scrollable/scrollable.h @@ -58,8 +58,8 @@ public: */ enum PropertyRange { - PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, - PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000, ///< Reserve property indices + PROPERTY_START_INDEX = Control::CONTROL_PROPERTY_END_INDEX + 1, ///< @since DALi 1.1.18 + PROPERTY_END_INDEX = PROPERTY_START_INDEX + 1000, ///< Reserve property indices, @since DALi 1.1.18 ANIMATABLE_PROPERTY_START_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX, ANIMATABLE_PROPERTY_END_INDEX = ANIMATABLE_PROPERTY_REGISTRATION_START_INDEX + 1000 ///< Reserve animatable property indices @@ -75,6 +75,7 @@ public: // Event side properties OVERSHOOT_EFFECT_COLOR = PROPERTY_START_INDEX, ///< Property, name "overshootEffectColor", @see SetOvershootEffectColor(), type Vector4 OVERSHOOT_ANIMATION_SPEED, ///< Property, name "overshootAnimationSpeed", @see SetOvershootAnimationSpeed(), type float + OVERSHOOT_ENABLED, ///< Property, name "overshootEnabled", @see SetOvershootEnabled(), type bool, @since DALi 1.1.18 // Animatable properties SCROLL_RELATIVE_POSITION = ANIMATABLE_PROPERTY_START_INDEX, ///< Property, name "scrollRelativePosition", type Vector2 diff --git a/docs/content/images/item-view/list.png b/docs/content/images/item-view/list.png new file mode 100755 index 0000000000000000000000000000000000000000..d58892932b0d970dfdc8440520ebb621674dc08e GIT binary patch literal 23136 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 F{15nw;amU! literal 0 HcmV?d00001 diff --git a/node-addon/binding.gyp b/node-addon/binding.gyp index dad473d..142cc74 100644 --- a/node-addon/binding.gyp +++ b/node-addon/binding.gyp @@ -18,6 +18,9 @@ '<(DALI_JS_DIR)/actors/layer-api.cpp', '<(DALI_JS_DIR)/actors/camera-actor-api.cpp', '<(DALI_JS_DIR)/constants/constants-wrapper.cpp', + '<(DALI_JS_DIR)/controls/control-wrapper.cpp', + '<(DALI_JS_DIR)/controls/item-factory-wrapper.cpp', + '<(DALI_JS_DIR)/controls/item-view-api.cpp', '<(DALI_JS_DIR)/animation/animation-api.cpp', '<(DALI_JS_DIR)/animation/animation-wrapper.cpp', '<(DALI_JS_DIR)/animation/constrainer-api.cpp', diff --git a/node-addon/examples/images/icon-0.png b/node-addon/examples/images/icon-0.png new file mode 100755 index 0000000000000000000000000000000000000000..8d300e9fb3876138f5e63ba58236d01969b22e3e GIT binary patch literal 2465 zcmV;S310SzP)Ii=NaA*4U4gvy-Q|Wl z=|hY(#ekcLeB$BS?lnIvn)qS>7duZEcx9VS<;#--ziv=--t$93>YQlUB6K4 zF40#s8m-y@7JaSM{2_X79j#6GMNQX$Cy?J`I(}mk@h7Sb;ELseP}7XDB%MmRCj$n< z8o{~_(zVlN0a%o+2$8?NZe0G^6l5o)IlE6~OCW>*IA z1jK|;H)WV0nxZKg4>6dykfd76p)Jhw^-cj?86~kO) zFpl8|A`us3Mno}XjD_+M5ezAbpx#jzF}>$f=Ax-)LF5ld4Ih@>n}=Y_Hd zVZe&%!PI?ucwrGn4b4u({fAuq^!ZjY#7G0;M0#bs1;MH1(HBe*f7~bw-(Hrz_zeCn zhcR}fWugJLX#KHDxF+bBO)9tW=#m)v_shNb;4mJ$Kb8p7K-6?JfX6Iu3^GPn4~k%U ztqoh@TpNe=wMBL!Y{PJaXnxUvIS6o?(Xw1TG(U#EebfuprznyLo1bA2SnJzT4VV>V zY$~VPIhFb8)Ct_8G7Pg)4G66G;pQecHFcx)ZW$^c?w|LwXGU?$bPJdDjo{pQ7spQm z4IjAJS?}Q8{SM-B-`~TDfg3QS)W1X*@v8oq_dGVCjQaj%Ln8PrnR4nQ8ymJdcy&h{ zamTkfjY<|lfgw4t7BmK29zkuDjqg0%2cI15TbvSo^$`eh5u78w`Cts!l=))9FgBnx z$OQ9{vT3-Sa^+)7Vt(X=QVTvUJF%lKW=@G>*@76p|40$8lCu9uvuk8d;5uUY!Wg1a zt??`2N;sxFF;mA|xUV{f(t#K0!rK4aQ=uT9P~Stes%WfU=6TQ2CYwP&(uZVXj7b>91&I?h4~nZnbE$68!t z4dB+%)baYpk=TIKIs(3af<@W4>#l#4KB}#XC8iEv{F7sJE|^@Q?L9riA-5@a)#9S3 z=c;AKBnwrP#d!|5@sPQ>K;uYlD;uDE=5#uBum@d{-gg(?W@GkD8>`l|;X7qsQfCRXZSszV)^rGU0=p7@LtA8HHw02}b0>QLixb1T$r@P3oF z^z3H?_Nor`sH5wx2$fKQa&qcY^3 zd)f=efQ{Q6wCR~C;}aS`Og2}xs|@*#cj7p4D*wfAI_hHc_PDO#cO-`*7=Kh+2|E!y zw<(_2;`5yG%IDkQ=$oG0l4`)q@Q(9UmjYW8!4(2YwD zKj~<|CU_@@htPut(YQ+kxC{G(P$zBpp=jKt0gPY4+btPFamQM;MNbA?AbvO0OF3B6 z^XVAC;4?7mL(PKngQGnu~khImQtoL;6zg$Ng2k)B0LcwZD(>m1w>)HT& zlz1<(3i&;=RxfF|SJH%hF(3(Egv8f^^RR3_P4pj>I8T-|XoDYzsPR-BFGeaSE`x?G zQ3|n7;#e+m*1pEx&r|HU;#vjow7}bm>ka8iI}(>_Dd$)D*|`kjG*e!fv{A?lg83k8 zK1-R;(Tw^iIxi7lBRm|G`#~Qk%L#u1|G%$nw)8G5iDRK|#Vohqc{}blc(-8lE#OAX zb(?gaZ;LedlFe5>p<$x%8hB^h4TX2a8egH#7vdx@%seZ_#&H=IIRx(nfcJ^J;muH; fG_lh8p8x{@m_rkff%MKG00000NkvXXu0mjf*F>=1 literal 0 HcmV?d00001 diff --git a/node-addon/examples/images/icon-1.png b/node-addon/examples/images/icon-1.png new file mode 100755 index 0000000000000000000000000000000000000000..bfed8aca1b90035a559c2bcac5d6227f2f9f0951 GIT binary patch literal 2146 zcmV-o2%YzdP)|@k57f7 zKuJOALM2UG+K&I%`KOlY%yjOZd(OSno-g^5w$u6VJ>Pfl*_Q-5dXzkhH0BIpJ~M}z z&CC!cGgIY%|7Bih)(bB(&oQf*r-aR3Ok|RW?j@bMkhy?4k6FM>i1NFsy<$uX@wtzA znt6=*4f6=I!3&8_aQO`w$1G+p7tx1kx^AbK@iDW4SvQyrNMzo}T*F+VQS?Y=9AIu| ze!=Y0Eru@K#4Kf2N`jYqKzx#Wz`cAf_f{(RI$#V)l0>0E+{&Cn-o+HT$11t!q~CjLeda-24qT}`Xh4|#V-ma4Lc-FabdtR<{9Q{3)M(318G<#X+fF>V^MoJ9Gll*SD(K;+rM5BDC4S?tsQt})0UpuOZ_Cbx- zfQ=O2eLA`^leyU`15mK6Hkw+-Q_&R4{TVQZ{N1t!_OMknv~2*go^+$5Z9uzCb`3xi zdxg<3W-O1?T@lUz48@fj?Hofng&E!qX8;Bu3XE=!p+Gc^%7CHFbw)GCu}-w@*8p@X zO)z>n4ruyBe+J<9wMHvtcsW?}U<2+a3;h@dmXR+Shc#f0(MUN;!x=Cx*c}5Uz`WS; zp$vGKyfs~Ypdv}M?qfi)(MB7JgAG8R#T`Z$ZI~yT_ZV<5d3cy{>Znn)Yw~2On=^-6 z=FX+{xw)QyIV#w7Qh z|N3cD6D@;Kt*x%dbO6vi4F*gmFU;{tOr&iSC(@d%zT^J2?d`OJMLO2d;C@W!0L^E@ zfGJ)O|Lf_~X+c_ApO1Fe*Hg*avqT*o?#FZ4`n z26>5+1-XXJd3m&$tAxQQJAdA`P%2_7LM{x*aqH?!Id6V$?AShkuV`$fw`*%X6H_5> zfdLtARW)~nX#Ai`Sd9GHD>2m~BavL1c7i&2MpECtzfG4f6LAg2D=`%#9W7z(B9f~0 zvLi-NXJKK;@zA!eE~;v4qXS$&e-*;Dw73^jJ+38c7oV{+J3H)jjGh2K;+--NE=Er zGc5@PEadupd4%3d6)|;YLDzL&v1s&YTi<&TRmR}2)Q4&qa7O19(J5vZhpt}L7E=>Wp&^D&^Cr?Y zD#q2mj>5w38i=Kr2ZQD(PloPBP!Ur-&;q;}Zvgs&e%E$cPHcDQm_=-g)Q6)Yrg~t9 z#;bwmqt?ws%W7)qOAD_)x()Ch-7yv8SfDEc_*U=B=cC1ym9*i~rHH$vcF9w^i2t|W zJ{uJ=)#5v%|0iZ)OyF;FV|ECB*9XbJez>9c z*2Uj5=aYdBU@OeKdS^kvKBJ2^?Ct4E2{NF=XrsPAAe;f1uW``mqa25**LL~AqZr$a zKFV>!<|n}hVE_CoqmeRHiN;|KKsEUdqmwecNkIohgc?ZQ#k^p&avZgyE%j#r`e$D= zdN~eg+8T`k0QSJFG@3aEXt^WQ2Z<5ts%1tu$50lojTLD!&0+E%^JEYk_KB8c)d1iH z^2mW_af$}fkn9?O9@3|cj<)d(^HMAZ1YV(cjh2=H&Gz>9Pti`Oe4hEM(KKrOCYq5` z24DmO+t&YMwCxwr=mF8pF#`bfV-zvljm8ll)FQIbpJsKH0uHWul>9?#1~&o^=2Fp! zR2dK`l(7e<&uHEQpJ?)x)i2szr-M^8o-}Vl&y!!m+->K}u`HB=3D{e@Z4irpP4fJY z>;{N+r5a9(xQBGEG4KwqkpKAg!?6T$VyPgeSl~pt6N5;6r1duPAJt248vtOAAC5HK zFu)6Er0p$9t0VIoom?!7vfw4=gUrSOD1M`)t<1@CPsM8G{mg0PKjv_tV2`9}zte$L zEf>dJ@s;G?B{rT%)+!|pDK^ zK1-UHL|Me2a*vbbo|RA4)VX5?3a%~W-z{(n^D~dMq&wwaiX@-kqO)NpT5u+v71WE)qcTjV17cL2aXWv9qLLe=EOME)nh Y0CoCuY1x~M3IG5A07*qoM6N<$f_?G)-v9sr literal 0 HcmV?d00001 diff --git a/node-addon/examples/images/icon-2.png b/node-addon/examples/images/icon-2.png new file mode 100755 index 0000000000000000000000000000000000000000..db892fb31d99588b53067c035055eaec8e5d51ff GIT binary patch literal 1906 zcmV-&2aWiNP)=5*$b%u;3%GcC&B&F=Gz(f#~?4|5B1 zJ@b3!x6F1E3Z1681`K7EF=vYC=P63JTg*V|>zHkRG9aCKD|0^c4uztJGvf>9yUf+h z)2V8}0OnlgGt4oD_B+kY3g(B*ld9fE75&Tt=@2Uo7M~<tqy?Eg=2lA0xg8b2y7M^Z{&;J^QY(H!fOc=j+XP{VeZSQ( zVayCST@lIvEX7q=?Oa0zg;?GUWdIf+imYy~p-421suMn+xt)@04L2FEhn!7gCnD8= zIaYj608Jl`)CA$*FIcTS!}GzK2ODr3#W0Uy5i<%zAm&H{utw@M9Ko4KQYB4M$$yZ+TvR4me;^AY$P(KOxWK?Tt_R}!_ZR$UK!4S@Aql_k4b5!>IN3a1H3huBPd4?ULaYzGjdBLkzC(rO21ziwf zLlvmQUUEVe91m9m0K_s_Z1r*-i-W}vRRaKQS;VyEc&nQ$z{Fcgf1mm3VZK$%t!}QN zJk%H~+{Uamyh}79uM7xu%7}s4V>Lg69?@iNtiKrV2^rj?G1u0F zGjBd3fqW9rFDG(D2__)6^a&q}e^lE174beG(KBMWDPkJ=>5ReuI7rU*>uVBuh#QCW zaN8(ulxy&j_$cc&nYwjqijp0th?NJpqs~3(HG#l@_U#aDJXu2 zl&##&e$R_@!mG%+wxcG6#IKPut#bRpULDl_i#dy&D6vgREPRucVV0DMlEr`kun7rE zahR6flSIPrkvx}389alEPz6|u!)7Gx2zZ=SABl<$Et1F4l4sAiYDQWS^U8EoYeW&p zkrdrq;xU>>xWsqJ*)(-pW#T#v=nF&zQN8y>Obn2UD3pl5-0-ZUHwUM1x5N1mi+xvG8(oV%rTTC&U`AP{$6wK-}X_`8Gc& s(;}_pL;%>yY$Rue>adB0{7--Z06tq|@3ndoMgRZ+07*qoM6N<$g7DdR3IG5A literal 0 HcmV?d00001 diff --git a/node-addon/image-1.jpg b/node-addon/examples/images/image-1.jpg similarity index 100% rename from node-addon/image-1.jpg rename to node-addon/examples/images/image-1.jpg diff --git a/node-addon/image-2.jpg b/node-addon/examples/images/image-2.jpg similarity index 100% rename from node-addon/image-2.jpg rename to node-addon/examples/images/image-2.jpg diff --git a/node-addon/examples/item-view.js b/node-addon/examples/item-view.js new file mode 100644 index 0000000..5abfcfc --- /dev/null +++ b/node-addon/examples/item-view.js @@ -0,0 +1,142 @@ + var window= { + x:0, + y:0, + width:1920, + height: 1080, + transparent: false, + name:'itemview-example' + }; + + var viewMode={ + 'stereoscopic-mode':'mono', // stereo-horizontal, stereo-vertical, stereo-interlaced, + 'stereoBase': 65 // Distance in millimeters between left/right cameras typically between (50-70mm) + }; + + var options= { + 'window': window, + 'viewMode': viewMode, + } + +//desktop +//var dali = require('../build/Release/dali')( options ); + +//target +var dali = require('dali')( options ); + +var items = []; +var button; +var stageSize; + +var itemView; +var itemFactory; + +var currentLayoutIndex = 0; +var totalItemCount = 100; + +var imageDir = "./images/"; + +var daliApp = {}; + +daliApp.createItemView = function() { + + // Create item view data + var itemViewData = []; + for (var itemId = 0; itemId < totalItemCount; itemId++) + { + var data = {}; + data["template"] = "template-item-list"; // Create items initially with list template + data["icon_path"] = imageDir + "icon-" + itemId % 3 + ".png"; + data["title_text"] = "Item " + itemId; + itemViewData[itemId] = data; + } + + // Create item factory and set the data + itemFactory = new dali.ItemFactory(); + itemFactory.jsonTemplateFile = "./scripts/item-template.json"; + itemFactory.data = itemViewData; + + // Create item view + stageSize = dali.stage.getSize(); + itemView = new dali.Control("ItemView", itemFactory); + itemView.size = [stageSize.x, stageSize.y, 0.0]; + itemView.parentOrigin = dali.CENTER_LEFT; + itemView.anchorPoint = dali.CENTER_LEFT; + itemView.refreshInterval = 4.0; + + // Add item view to the stage + dali.stage.add( itemView ); + + // Create scroll bar for item view + var scrollBar = new dali.Control("ScrollBar"); + scrollBar.parentOrigin = dali.TOP_RIGHT; + scrollBar.anchorPoint = dali.TOP_RIGHT; + scrollBar.widthResizePolicy = "FIT_TO_CHILDREN"; + scrollBar.heightResizePolicy = "FILL_TO_PARENT"; + itemView.add(scrollBar); + + // Add the list and grid layouts + itemView.addLayout(dali.ITEM_LAYOUT_LIST); // layout index 0 + itemView.addLayout(dali.ITEM_LAYOUT_GRID); // layout index 1 + + // Set custom item size for list layout + itemView.setItemSize(0, [stageSize.x, stageSize.y * 0.1, 0.0]); + + // Set custom item size for grid layout + var layoutMargin = 120; + itemView.setItemSize(1, [(stageSize.x - layoutMargin) / 4, stageSize.y * 0.2, 0.0]); + + // Activate the list layout + itemView.activateLayout(0, itemView.size); + + // Create button for layout switching + button = new dali.Control("PushButton"); + button.size = [100.0, 60.0, 0.0]; + button.position = [-20.0, 20.0, 0.0]; + button.parentOrigin = dali.TOP_RIGHT; + button.anchorPoint = dali.TOP_RIGHT; + button.labelText = "Switch"; + dali.stage.add( button ); + + // Connect a signal callback to button pressed signal + button.on("pressed", daliApp.buttonPressedEvent); +} + +daliApp.buttonPressedEvent = function( button ) { + + // Calculate the layout index for the next layout to switch to + currentLayoutIndex++; + currentLayoutIndex = currentLayoutIndex % itemView.getLayoutCount(); + + // Activate the next layout + itemView.activateLayout(currentLayoutIndex, [stageSize.x, stageSize.y, 0.0], 0.0); + + // Change the item template in item view data as we want to change the layout of the items + var data = itemFactory.data; + for (var itemId = 0; itemId < totalItemCount; itemId++) + { + if(currentLayoutIndex == 0) + { + // List layout + data[itemId]["template"] = "template-item-list"; // Create items with list template + } + else + { + // Grid layout + data[itemId]["template"] = "template-item-grid"; // Create items with grid template + } + } + itemFactory.data = data; +} + +function startup() +{ + daliApp.init(); +} + +daliApp.init = function() +{ + daliApp.createItemView(); +} + +startup(); + diff --git a/node-addon/line-mesh.js b/node-addon/examples/line-mesh.js similarity index 98% rename from node-addon/line-mesh.js rename to node-addon/examples/line-mesh.js index f1c9532..faaedb9 100644 --- a/node-addon/line-mesh.js +++ b/node-addon/examples/line-mesh.js @@ -18,7 +18,7 @@ } // desktop -//var dali = require('./build/Release/dali')( options ); +//var dali = require('../build/Release/dali')( options ); // target var dali = require('dali')( options ); diff --git a/node-addon/mesh-morph.js b/node-addon/examples/mesh-morph.js similarity index 99% rename from node-addon/mesh-morph.js rename to node-addon/examples/mesh-morph.js index dd4527a..edbdcf9 100644 --- a/node-addon/mesh-morph.js +++ b/node-addon/examples/mesh-morph.js @@ -17,9 +17,8 @@ 'view-mode': viewMode, } - // desktop -//var dali = require('./build/Release/dali')( options ); +//var dali = require('../build/Release/dali')( options ); // target var dali = require('dali')( options ); diff --git a/node-addon/point-mesh.js b/node-addon/examples/point-mesh.js similarity index 98% rename from node-addon/point-mesh.js rename to node-addon/examples/point-mesh.js index f9c7186..ceb9b1e 100644 --- a/node-addon/point-mesh.js +++ b/node-addon/examples/point-mesh.js @@ -17,11 +17,11 @@ 'view-mode': viewMode, } -var imageDir = "./"; +var imageDir = "./images/"; // desktop -//var dali = require('./build/Release/dali')( options ); +//var dali = require('../build/Release/dali')( options ); // target var dali = require('dali')( options ); diff --git a/node-addon/examples/scripts/item-template.json b/node-addon/examples/scripts/item-template.json new file mode 100644 index 0000000..f909edd --- /dev/null +++ b/node-addon/examples/scripts/item-template.json @@ -0,0 +1,92 @@ +{ + "templates": + { + "template-item-list": + { + "name":"item", + "type":"Actor", + "position":[0,0,0], + "anchorPoint":"TOP_LEFT", + "parentOrigin":"TOP_LEFT", + "actors": + [ + { + "name":"icon", + "type":"ImageView", + "image": + { + "rendererType" : "imageRenderer", + "imageUrl": "{icon_path}" + }, + "position":[20.0, 0.0, 0.0], + "size":[70.0, 70.0, 0.0], + "color":[1.0,1.0,1.0,1.0], + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_LEFT", + "actors": + [ + { + "name":"title", + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_RIGHT", + "type":"TextLabel", + "position": [30.0, 0.0, 0.0], + "size":[200.0, 70.0, 0.0], + "pointSize":30, + "fontFamily":"HelveticaNeue", + "fontStyle":"Bold", + "horizontalAlignment":"BEGIN", + "verticalAlignment":"CENTER", + "textColor": [1.0,0.0,1.0,1.0], + "text":"{title_text}" + } + ] + } + ] + }, + + "template-item-grid": + { + "name":"item", + "type":"Actor", + "position":[0,0,0], + "anchorPoint":"TOP_LEFT", + "parentOrigin":"TOP_LEFT", + "actors": + [ + { + "name":"icon", + "type":"ImageView", + "image": + { + "rendererType" : "imageRenderer", + "imageUrl": "{icon_path}" + }, + "position":[0.0, -10.0, 0.0], + "size":[70.0, 70.0, 0.0], + "color":[1.0,1.0,1.0,1.0], + "anchorPoint":"CENTER", + "parentOrigin":"CENTER", + "actors": + [ + { + "name":"title", + "anchorPoint":"TOP_CENTER", + "parentOrigin":"BOTTOM_CENTER", + "type":"TextLabel", + "position": [0.0,10.0,0.0], + "size":[100.0, 100.0, 0.0], + "pointSize":22, + "fontFamily":"HelveticaNeue", + "fontStyle":"Bold", + "horizontalAlignment":"CENTER", + "textColor": [1.0,0.0,1.0,1.0], + "text":"{title_text}" + } + ] + } + ] + } + } +} + diff --git a/node-addon/texture-mesh.js b/node-addon/examples/texture-mesh.js similarity index 98% rename from node-addon/texture-mesh.js rename to node-addon/examples/texture-mesh.js index 7f6173e..016a7a9 100644 --- a/node-addon/texture-mesh.js +++ b/node-addon/examples/texture-mesh.js @@ -17,11 +17,11 @@ 'view-mode': viewMode, } -var imageDir = "./"; +var imageDir = "./images/"; // desktop -//var dali = require('./build/Release/dali')( options ); +//var dali = require('../build/Release/dali')( options ); // target var dali = require('dali')( options ); diff --git a/node-addon/item-template.json b/node-addon/item-template.json new file mode 100644 index 0000000..f909edd --- /dev/null +++ b/node-addon/item-template.json @@ -0,0 +1,92 @@ +{ + "templates": + { + "template-item-list": + { + "name":"item", + "type":"Actor", + "position":[0,0,0], + "anchorPoint":"TOP_LEFT", + "parentOrigin":"TOP_LEFT", + "actors": + [ + { + "name":"icon", + "type":"ImageView", + "image": + { + "rendererType" : "imageRenderer", + "imageUrl": "{icon_path}" + }, + "position":[20.0, 0.0, 0.0], + "size":[70.0, 70.0, 0.0], + "color":[1.0,1.0,1.0,1.0], + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_LEFT", + "actors": + [ + { + "name":"title", + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_RIGHT", + "type":"TextLabel", + "position": [30.0, 0.0, 0.0], + "size":[200.0, 70.0, 0.0], + "pointSize":30, + "fontFamily":"HelveticaNeue", + "fontStyle":"Bold", + "horizontalAlignment":"BEGIN", + "verticalAlignment":"CENTER", + "textColor": [1.0,0.0,1.0,1.0], + "text":"{title_text}" + } + ] + } + ] + }, + + "template-item-grid": + { + "name":"item", + "type":"Actor", + "position":[0,0,0], + "anchorPoint":"TOP_LEFT", + "parentOrigin":"TOP_LEFT", + "actors": + [ + { + "name":"icon", + "type":"ImageView", + "image": + { + "rendererType" : "imageRenderer", + "imageUrl": "{icon_path}" + }, + "position":[0.0, -10.0, 0.0], + "size":[70.0, 70.0, 0.0], + "color":[1.0,1.0,1.0,1.0], + "anchorPoint":"CENTER", + "parentOrigin":"CENTER", + "actors": + [ + { + "name":"title", + "anchorPoint":"TOP_CENTER", + "parentOrigin":"BOTTOM_CENTER", + "type":"TextLabel", + "position": [0.0,10.0,0.0], + "size":[100.0, 100.0, 0.0], + "pointSize":22, + "fontFamily":"HelveticaNeue", + "fontStyle":"Bold", + "horizontalAlignment":"CENTER", + "textColor": [1.0,0.0,1.0,1.0], + "text":"{title_text}" + } + ] + } + ] + } + } +} + diff --git a/packaging/dali-addon.spec b/packaging/dali-addon.spec index 7bb302f..22bfeae 100644 --- a/packaging/dali-addon.spec +++ b/packaging/dali-addon.spec @@ -69,7 +69,7 @@ rm -rf %{buildroot} cd "%{addonBuildDir}" %make_install -cp %{addonDir}/line-mesh.js %{installDir}/line-mesh.js +cp -R %{addonDir}/examples %{installDir}/examples %clean diff --git a/plugins/dali-script-v8/docs/content/constants.js b/plugins/dali-script-v8/docs/content/constants.js index d321285..a09ed33 100644 --- a/plugins/dali-script-v8/docs/content/constants.js +++ b/plugins/dali-script-v8/docs/content/constants.js @@ -184,6 +184,13 @@ Constants accessible under the dali global object. |PROPERTY_ARRAY | integer value | |PROPERTY_MAP | integer value | |PROPERTY_INVALID_INDEX | integer value | +|PROPERTY_READ_ONLY | integer value | +|PROPERTY_READ_WRITE | integer value | +|PROPERTY_ANIMATABLE | integer value | + +|**Item layout type ** | | +|ITEM_LAYOUT_LIST | integer value | +|ITEM_LAYOUT_GRID | integer value | * @class Constants */ diff --git a/plugins/dali-script-v8/docs/content/item-factory.js b/plugins/dali-script-v8/docs/content/item-factory.js new file mode 100644 index 0000000..b7ae5ab --- /dev/null +++ b/plugins/dali-script-v8/docs/content/item-factory.js @@ -0,0 +1,177 @@ +/** + * +## ItemFactory API + + ItemFactory is for storing the data of {{#crossLink "ItemView"}}ItemView{{/crossLink}} + and creating actors for ItemView on request. Each item in ItemView is identified by a + unique ID, and has a linear order from 0. + + A JSON file should be provided to ItemFactory which defines the templates of items + to be used to create the actors. Multiple templates can be defined in the JSON file + for different type of items. + +### Simple example of creating a JSON template for items + +``` + { + "templates": + { + "template-item": + { + "name":"item", + "type":"Actor", + "position":[0,0,0], + "anchorPoint":"TOP_LEFT", + "parentOrigin":"TOP_LEFT", + "actors": + [ + { + "name":"icon", + "type":"ImageView", + "image": + { + "rendererType" : "imageRenderer", + "imageUrl": "{icon_path}" + }, + "position":[20.0, 0.0, 0.0], + "size":[70.0, 70.0, 0.0], + "color":[1.0,1.0,1.0,1.0], + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_LEFT", + "actors": + [ + { + "name":"title", + "anchorPoint":"CENTER_LEFT", + "parentOrigin":"CENTER_RIGHT", + "type":"TextLabel", + "position": [30.0, 0.0, 0.0], + "size":[200.0, 70.0, 0.0], + "pointSize":30, + "fontFamily":"HelveticaNeue", + "fontStyle":"Bold", + "horizontalAlignment":"BEGIN", + "verticalAlignment":"CENTER", + "textColor": [1.0,0.0,1.0,1.0], + "text":"{title_text}" + } + ] + } + ] + } + } + } +``` + + The data of items should be provided to ItemFactory as an array of property maps + in which each map contains the data for each item, including the template to be used + to build the actor and the pairs of key/value to be used to replace the constants + defined in the template. The order of property maps in the array represents the actual + order of items in ItemView. + + ### Example of defining the data of an ItemView with two items + +``` + var itemViewData = [ + { "template" : "template-item", + "icon_path" : "icon0.png", + "title_text" : "Item 0" }, + { "template" : "template-item", + "icon_path" : "icon1.png", + "title_text" : "Item 1" } + ]; +``` + + This means ItemFactory will use the template "template-item" defined in the JSON file + to create the item for ItemView and replace the constants "icon_path" and "title_text" + in the template with their actual values, e.g. "icon0.png" and "Item 0". Each item can + have different template and different data. + + ### Example of creating an ItemFactory with the above JSON template and link it with an ItemView + +![ ](../assets/img/item-view/list.png) + +``` + // Define the data of 100 items + var itemViewData = []; + for (var itemId = 0; itemId < 100; itemId++) + { + var itemData = {}; + itemData["template"] = "template-item"; + itemData["icon_path"] = "icon" + itemId + ".png"; + itemData["title_text"] = "Item " + itemId; + itemViewData[itemId] = itemData; + } + + // Create the item factory and set the JSON template file and item view data + var itemFactory = new dali.ItemFactory(); + itemFactory.jsonTemplateFile = "./item-template.json"; // Set the JSON template file + itemFactory.data = itemViewData; // Set the ItemView data + + // Create the item view with the given item factory + var itemView = new dali.Control("ItemView", itemFactory); + itemView.size = [stageSize.x, stageSize.y, 0.0]; + itemView.parentOrigin = dali.CENTER_LEFT; + itemView.anchorPoint = dali.CENTER_LEFT; + dali.stage.add( itemView ); + + // Add a list layout to ItemView (multiple layouts can be added to the same ItemView) + itemView.addLayout(dali.ITEM_LAYOUT_LIST); + + // Set custom item size for the list layout + // If set, this will overide the predefined item size in the list layout + itemView.setItemSize(0, [350, 100, 0]); // 0 means the first layout added to ItemView + + // Acticate the list layout (which will layout the items as a list) + itemView.activateLayout(0, itemView.size); // 0 means the first layout added to ItemView +``` + + ### Example of changing the data of items in ItemView dynamically + +``` + var data = itemFactory.data; + data[itemId]["icon_path"] = "new-icon.png"; + data[itemId]["title_text"] = "New Item"; + itemFactory.data = data; // ItemView will update the changed items immediately +``` + + @class ItemFactory + +*/ + +/** + * Sets the file name of JSON template that contains the templates for items. + * + * @example + * itemFactory.jsonTemplateFile = "item-template.json"; // ItemFactory will look for the template from this JSON file + * + * @type String + * @property jsonTemplateFile + */ +JSON_TEMPLATE_FILE + +/** + * Sets the data of ItemView + * + * The data is an array of property maps in which each map contains the data + * for each item, including the template to be used to build the actor and + * the pairs of key/value to be used to replace the constants defined in the + * template. The order of property maps in the array represents the actual + * order of items in ItemView. + * + * @example + * var itemViewData = [ + * { "template" : "template-item", + * "icon_path" : "icon0.png", + * "title_text" : "Item 0" }, + * { "template" : "template-item", + * "icon_path" : "icon1.png", + * "title_text" : "Item 1" } + * ]; + * + * itemFactory.data = itemViewData; // ItemFactory will look for the template from this JSON file + * + * @type Array + * @property data + */ +DATA diff --git a/plugins/dali-script-v8/docs/content/item-view.js b/plugins/dali-script-v8/docs/content/item-view.js new file mode 100644 index 0000000..776c5ea --- /dev/null +++ b/plugins/dali-script-v8/docs/content/item-view.js @@ -0,0 +1,62 @@ +/** + * +## ItemView API + + ItemView is a scrollable layout container with built-in layouts to determine + the logical position of each item in a layout. + + Actors are provided from an external {{#crossLink "ItemFactory"}}ItemFactory{{/crossLink}}, + to display the currently visible items. ItemFactory is for storing the data of ItemView and + creating actors for ItemView on request. Each item in ItemView is identified by a unique ID, + and has a linear order from 0. + + ### Example of creating an ItemView (see {{#crossLink "ItemFactory"}}ItemFactory{{/crossLink}} API for a full example) + +``` + // Define the data of 100 items + var itemViewData = []; + for (var itemId = 0; itemId < 100; itemId++) + { + var itemData = {}; + itemData["template"] = "template-item"; + itemData["title_text"] = "Item " + itemId; + itemViewData[itemId] = itemData; + } + + // Create an item factory and set the JSON template file and item view data + var itemFactory = new dali.ItemFactory(); + itemFactory.jsonTemplateFile = "./item-template.json"; // Set the JSON template file + itemFactory.data = itemViewData; // Set the ItemView data + + // Create the item view with the given item factory + var itemView = new dali.Control("ItemView", itemFactory); + itemView.size = [stageSize.x, stageSize.y, 0.0]; + itemView.parentOrigin = dali.CENTER_LEFT; + itemView.anchorPoint = dali.CENTER_LEFT; + dali.stage.add( itemView ); + + // Add a scroll bar to ItemView (optional) + var scrollBar = new dali.Control("ScrollBar"); + scrollBar.parentOrigin = dali.TOP_RIGHT; + scrollBar.anchorPoint = dali.TOP_RIGHT; + scrollBar.widthResizePolicy = "FIT_TO_CHILDREN"; + scrollBar.heightResizePolicy = "FILL_TO_PARENT"; + scrollBar.indicatorHeightPolicy = "Fixed"; + scrollBar.indicatorFixedHeight = 60.0; + itemView.add(scrollBar); + + // Add a list layout to ItemView (multiple layouts can be added to the same ItemView) + itemView.addLayout(dali.ITEM_LAYOUT_LIST); + + // Set custom item size for the list layout + // If set, this will overide the predefined item size in the list layout + itemView.setItemSize(0, [350, 100, 0]); // 0 means the first layout added to ItemView + + // Acticate the list layout (which will layout the items as a list) + itemView.activateLayout(0, itemView.size); // 0 means the first layout added to ItemView +``` + + @class ItemView + @extends Actor + +*/ diff --git a/plugins/dali-script-v8/file.list b/plugins/dali-script-v8/file.list index e50d5cd..598d90e 100644 --- a/plugins/dali-script-v8/file.list +++ b/plugins/dali-script-v8/file.list @@ -13,6 +13,9 @@ script_v8_plugin_src_files = \ $(v8_plugin_dir)/actors/actor-api.cpp \ $(v8_plugin_dir)/actors/layer-api.cpp \ $(v8_plugin_dir)/actors/camera-actor-api.cpp \ + $(v8_plugin_dir)/controls/control-wrapper.cpp \ + $(v8_plugin_dir)/controls/item-factory-wrapper.cpp \ + $(v8_plugin_dir)/controls/item-view-api.cpp \ $(v8_plugin_dir)/constants/constants-wrapper.cpp \ $(v8_plugin_dir)/animation/animation-api.cpp \ $(v8_plugin_dir)/animation/animation-wrapper.cpp \ diff --git a/plugins/dali-script-v8/src/actors/actor-api.cpp b/plugins/dali-script-v8/src/actors/actor-api.cpp index 5bac818..371639d 100644 --- a/plugins/dali-script-v8/src/actors/actor-api.cpp +++ b/plugins/dali-script-v8/src/actors/actor-api.cpp @@ -36,21 +36,14 @@ namespace V8Plugin namespace // unanmed namespace { + Actor GetActor( v8::Isolate* isolate, const v8::FunctionCallbackInfo& args ) { HandleWrapper* handleWrapper = HandleWrapper::Unwrap( isolate, args.This() ); return Actor::DownCast( handleWrapper->mHandle ); } -} //unanmed namespace - -namespace TextLabelApi -{ - Actor New( const v8::FunctionCallbackInfo< v8::Value >& args ) - { - return Dali::Toolkit::TextLabel::New(); - } -} +} //unanmed namespace /*************************************** * ACTOR API FUNCTIONS @@ -279,7 +272,7 @@ void ActorApi::GetChildCount( const v8::FunctionCallbackInfo& args ) } /** - * Retrieve and child actor by index. + * Retrieve a child actor by index. * * @for Actor * @method getChildAt diff --git a/plugins/dali-script-v8/src/actors/actor-api.h b/plugins/dali-script-v8/src/actors/actor-api.h index 26f8991..bcd600e 100644 --- a/plugins/dali-script-v8/src/actors/actor-api.h +++ b/plugins/dali-script-v8/src/actors/actor-api.h @@ -28,14 +28,6 @@ namespace Dali namespace V8Plugin { -namespace TextLabelApi -{ - /** - * Temporary TextView constructor - */ - Actor New( const v8::FunctionCallbackInfo< v8::Value >& args ); -} - namespace ActorApi { diff --git a/plugins/dali-script-v8/src/actors/actor-wrapper.cpp b/plugins/dali-script-v8/src/actors/actor-wrapper.cpp index 336ed23..a1c0f78 100644 --- a/plugins/dali-script-v8/src/actors/actor-wrapper.cpp +++ b/plugins/dali-script-v8/src/actors/actor-wrapper.cpp @@ -84,7 +84,7 @@ struct ActorApiStruct /** * Lookup table to match a actor type with a constructor and supported API's. - * HandleWrapper::ActorType is used to index this table + * ActorWrapper::ActorType is used to index this table */ const ActorApiStruct ActorApiLookup[]= { @@ -305,7 +305,7 @@ v8::Handle ActorWrapper::WrapActor( v8::Isolate* isolate, Actor acto // create an instance of the template v8::Local localObject = objectTemplate->NewInstance(); - // create teh actor object + // create the actor object ActorWrapper* pointer = new ActorWrapper( actor, Dali::V8Plugin::DaliWrapper::Get().GetDaliGarbageCollector() ); // assign the JavaScript object to the wrapper. @@ -392,42 +392,6 @@ void ActorWrapper::NewActor( const v8::FunctionCallbackInfo< v8::Value >& args) args.GetReturnValue().Set( localObject ); } -void ActorWrapper::NewControl( const v8::FunctionCallbackInfo< v8::Value >& args) -{ - v8::Isolate* isolate = args.GetIsolate(); - v8::HandleScope handleScope( isolate ); - - if( !args.IsConstructCall() ) - { - DALI_SCRIPT_EXCEPTION( isolate, "constructor called without 'new" ); - return; - } - - bool found( false ); - std::string controlName = V8Utils::GetStringParameter( PARAMETER_0, found, isolate, args ); - - if( !found ) - { - DALI_SCRIPT_EXCEPTION( isolate, "missing control name" ); - return; - } - Actor control; - Dali::TypeInfo typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( controlName ); - if( typeInfo ) // handle, check if it has a value - { - Dali::BaseHandle handle = typeInfo.CreateInstance(); - if( handle ) - { - control = Actor::DownCast( handle ); - } - } - - v8::Local localObject = WrapActor( isolate, control, ACTOR ); - - args.GetReturnValue().Set( localObject ); -} - - /** * given an actor type name, e.g. CameraActor returns the type, e.g. ActorWrapper::CAMERA_ACTOR */ @@ -443,8 +407,6 @@ ActorWrapper::ActorType ActorWrapper::GetActorType( const std::string& name ) return ActorWrapper::UNKNOWN_ACTOR; } - - } // namespace V8Plugin } // namespace Dali diff --git a/plugins/dali-script-v8/src/actors/actor-wrapper.h b/plugins/dali-script-v8/src/actors/actor-wrapper.h index fd12185..e84559f 100644 --- a/plugins/dali-script-v8/src/actors/actor-wrapper.h +++ b/plugins/dali-script-v8/src/actors/actor-wrapper.h @@ -76,13 +76,6 @@ public: static void NewActor( const v8::FunctionCallbackInfo< v8::Value >& args); /** - * @brief Creates a new Control wrapped inside a Javascript Object. - * @note: the control type is passed as a parameter e.g. 'TextField' - * @param[in] args v8 function call arguments interpreted - */ - static void NewControl( const v8::FunctionCallbackInfo< v8::Value >& args); - - /** * @brief Wraps an actor of a given type */ static v8::Handle WrapActor(v8::Isolate* isolate, Dali::Actor actor,ActorType actorType); @@ -107,14 +100,16 @@ public: */ static ActorWrapper::ActorType GetActorType( const std::string& name ); -private: +protected: /** - * Helper to make the actor template + * @brief Helper to make the actor template * */ static v8::Handle MakeDaliActorTemplate( v8::Isolate* isolate, ActorType actorType ); +private: + /** * Helper, get an actor template given an actor type */ diff --git a/plugins/dali-script-v8/src/constants/constants-wrapper.cpp b/plugins/dali-script-v8/src/constants/constants-wrapper.cpp index acaacb6..edbd62b 100644 --- a/plugins/dali-script-v8/src/constants/constants-wrapper.cpp +++ b/plugins/dali-script-v8/src/constants/constants-wrapper.cpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace Dali { @@ -243,6 +244,12 @@ const IntegerPair EnumTable[] = { "PROPERTY_ARRAY", Property::ARRAY }, { "PROPERTY_MAP", Property::MAP }, { "PROPERTY_INVALID_INDEX", Property::INVALID_INDEX }, + { "PROPERTY_READ_ONLY", Property::READ_ONLY }, + { "PROPERTY_READ_WRITE", Property::READ_WRITE }, + { "PROPERTY_ANIMATABLE", Property::ANIMATABLE }, + + { "ITEM_LAYOUT_LIST", Toolkit::DefaultItemLayout::LIST }, + { "ITEM_LAYOUT_GRID", Toolkit::DefaultItemLayout::GRID }, }; const unsigned int EnumTableCount = sizeof(EnumTable)/sizeof(EnumTable[0]); diff --git a/plugins/dali-script-v8/src/controls/control-wrapper.cpp b/plugins/dali-script-v8/src/controls/control-wrapper.cpp new file mode 100644 index 0000000..09093a2 --- /dev/null +++ b/plugins/dali-script-v8/src/controls/control-wrapper.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// CLASS HEADER +#include +#include "control-wrapper.h" + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace V8Plugin +{ + +v8::Persistent ControlWrapper::mControlTemplate; +v8::Persistent ControlWrapper::mItemViewTemplate; + +Vector< void* > ControlWrapper::mControlGarbageContainer; + +namespace +{ + + +/** + * pointer to a persistent template handle + */ +struct ControlTemplate +{ + v8::Persistent* controlTemplate; +}; + +/** + * array of templates for each type of control + */ +const ControlTemplate ControlTemplateLookup[]= +{ + { &ControlWrapper::mControlTemplate }, // CONTROL + { &ControlWrapper::mItemViewTemplate } // ITEMVIEW +}; + +/** + * Bitmask of API's that an control can support + */ +enum ControlApiBitMask +{ + CONTROL_API = 1 << 0, + ITEMVIEW_API = 1 << 1 +}; + +/** + * structure used for the ControlApiLookup. + */ +struct ControlApiStruct +{ + const char* controlName; + ControlWrapper::ControlType controlType; + Toolkit::Control (*constructor)( const v8::FunctionCallbackInfo< v8::Value >& args); + int supportApis; +}; + +/** + * Lookup table to match a control type with a constructor and supported API's. + * ControlWrapper::ControlType is used to index this table + */ +const ControlApiStruct ControlApiLookup[]= +{ + {"Control", ControlWrapper::CONTROL, NULL, CONTROL_API }, + {"ItemView", ControlWrapper::ITEMVIEW, ItemViewApi::New, CONTROL_API | ITEMVIEW_API }, +}; + +const unsigned int ControlApiLookupCount = sizeof(ControlApiLookup)/sizeof(ControlApiLookup[0]); + + +/** + * Creates a control given a type name + * Uses the type registry to create an control of the correct type + */ +Toolkit::Control CreateControl( const v8::FunctionCallbackInfo< v8::Value >& args, + const std::string& typeName ) +{ + Toolkit::Control control; + + ControlWrapper::ControlType controlType = ControlWrapper::GetControlType( typeName ); + + // if we don't currently have specific binding for the given control type, + // try to use type registry to create it + if( controlType == ControlWrapper::UNKNOWN_CONTROL ) + { + Dali::TypeInfo typeInfo = Dali::TypeRegistry::Get().GetTypeInfo( typeName ); + if( typeInfo ) + { + Dali::BaseHandle handle = typeInfo.CreateInstance(); + if( handle ) + { + control = Toolkit::Control::DownCast( handle ); + if( !control ) + { + DALI_SCRIPT_EXCEPTION( args.GetIsolate(), "Unknown control type" ); + return Toolkit::Control(); + } + } + } + } + else + { + // run the constructor for this type of control so it can pull out custom parameters + control = (ControlApiLookup[controlType].constructor)( args ); + } + + return control; +} + +/** + * given a control type return what api's it supports + */ +int GetControlSupportedApis( ControlWrapper::ControlType type ) +{ + return ControlApiLookup[type].supportApis; +} + +/** + * Used for the ControlFunctionTable to map function names to functions + * with for a specific API + */ +struct ControlFunctions +{ + const char* name; ///< function name + void (*function)( const v8::FunctionCallbackInfo< v8::Value >& args); + ControlApiBitMask api; +}; + +/** + * Contains a list of all functions that can be called in + * ItemView + */ +const ControlFunctions ControlFunctionTable[]= +{ + + /************************************** + * ItemView API + **************************************/ + { "GetLayoutCount", ItemViewApi::GetLayoutCount, ITEMVIEW_API }, + { "AddLayout", ItemViewApi::AddLayout, ITEMVIEW_API }, + { "RemoveLayout", ItemViewApi::RemoveLayout, ITEMVIEW_API }, + { "ActivateLayout", ItemViewApi::ActivateLayout, ITEMVIEW_API }, + { "GetItemSize", ItemViewApi::GetItemSize, ITEMVIEW_API }, + { "SetItemSize", ItemViewApi::SetItemSize, ITEMVIEW_API }, + { "ScrollToItem", ItemViewApi::ScrollToItem, ITEMVIEW_API }, + { "GetItem", ItemViewApi::GetItem, ITEMVIEW_API }, + { "GetItemId", ItemViewApi::GetItemId, ITEMVIEW_API }, + { "GetItemsRange", ItemViewApi::GetItemsRange, ITEMVIEW_API }, + +}; + +const unsigned int ControlFunctionTableCount = sizeof(ControlFunctionTable)/sizeof(ControlFunctionTable[0]); +} //un-named space + + +ControlWrapper::ControlWrapper( Toolkit::Control control, + GarbageCollectorInterface& gc ) +: ActorWrapper( control, gc ), + mControl( control ) + +{ +} + +ControlWrapper::~ControlWrapper() +{ + mControlGarbageContainer.Release(); +} + +v8::Handle ControlWrapper::WrapControl(v8::Isolate* isolate, Toolkit::Control control ) +{ + v8::EscapableHandleScope handleScope( isolate ); + + // Check whether the control is a Control + ControlWrapper::ControlType controlType = GetControlType( control.GetTypeName() ); + + if( controlType == ControlWrapper::UNKNOWN_CONTROL && Toolkit::Control::DownCast(control) ) + { + controlType = ControlWrapper::CONTROL; + } + + v8::Local object = WrapControl( isolate, control, controlType ); + + return handleScope.Escape( object ); +} + +Toolkit::Control ControlWrapper::GetControl() +{ + return mControl; +} + +v8::Handle ControlWrapper::WrapControl( v8::Isolate* isolate, Toolkit::Control control, ControlType controlType ) +{ + v8::EscapableHandleScope handleScope( isolate ); + v8::Local objectTemplate; + + objectTemplate = GetControlTemplate( isolate, controlType ); + + // create an instance of the template + v8::Local localObject = objectTemplate->NewInstance(); + + // create the control object + ControlWrapper* pointer = new ControlWrapper( control, Dali::V8Plugin::DaliWrapper::Get().GetDaliGarbageCollector() ); + + // assign the JavaScript object to the wrapper. + // This also stores Dali object, in an internal field inside the JavaScript object. + pointer->SetJavascriptObject( isolate, localObject ); + + return handleScope.Escape( localObject ); +} + +v8::Local ControlWrapper::GetControlTemplate( v8::Isolate* isolate, ControlWrapper::ControlType type ) +{ + v8::EscapableHandleScope handleScope( isolate ); + v8::Local objectTemplate; + + if( ControlTemplateLookup[type].controlTemplate->IsEmpty() ) + { + objectTemplate = MakeDaliControlTemplate( isolate, type ); + ControlTemplateLookup[type].controlTemplate->Reset( isolate, objectTemplate ); + } + else + { + // get the object template + objectTemplate = v8::Local::New( isolate, *ControlTemplateLookup[type].controlTemplate ); + } + + return handleScope.Escape( objectTemplate ); +} + +v8::Handle ControlWrapper::MakeDaliControlTemplate( v8::Isolate* isolate, ControlType controlType ) +{ + v8::EscapableHandleScope handleScope( isolate ); + + // all the controls support actor APIs + v8::Local objTemplate = ActorWrapper::MakeDaliActorTemplate( isolate, ActorWrapper::ACTOR ); + + // find out what API's this control supports + int supportApis = GetControlSupportedApis( controlType ); + + // add our function properties + for( unsigned int i = 0; i < ControlFunctionTableCount; ++i ) + { + const ControlFunctions property = ControlFunctionTable[i]; + + // check to see if the control supports a certain type of API + // e.g. ItemView will support CONTROL_API and ITEMVIEW_API + if( supportApis & property.api ) + { + std::string funcName = V8Utils::GetJavaScriptFunctionName( property.name); + + objTemplate->Set( v8::String::NewFromUtf8( isolate, funcName.c_str() ), + v8::FunctionTemplate::New( isolate, property.function ) ); + } + } + + return handleScope.Escape( objTemplate ); +} + +void ControlWrapper::NewControl( const v8::FunctionCallbackInfo< v8::Value >& args) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + if( !args.IsConstructCall() ) + { + DALI_SCRIPT_EXCEPTION( isolate, "constructor called without 'new" ); + return; + } + + bool found( false ); + std::string controlName = V8Utils::GetStringParameter( PARAMETER_0, found, isolate, args ); + + Toolkit::Control control; + if( found && controlName != ControlApiLookup[0].controlName ) + { + control = CreateControl( args, controlName ); // create the control with the given type + } + else + { + control = Toolkit::Control::New(); // no given type, so create the base type of control + } + + if( control ) + { + v8::Local localObject = WrapControl( isolate, control ); + args.GetReturnValue().Set( localObject ); + } + else + { + DALI_SCRIPT_EXCEPTION( isolate, "unsupported control type" ); + } +} + +/** + * Given a control type name, e.g. ItemView returns the type, e.g. ControlWrapper::ITEMVIEW + */ +ControlWrapper::ControlType ControlWrapper::GetControlType( const std::string& name ) +{ + for( unsigned int i = 0 ; i < ControlApiLookupCount ; i++ ) + { + if( ControlApiLookup[i].controlName == name ) + { + return ControlApiLookup[i].controlType; + } + } + return ControlWrapper::UNKNOWN_CONTROL; +} + +void ControlWrapper::RegisterGarbage(void* garbage) +{ + mControlGarbageContainer.PushBack(garbage); +} + +} // namespace V8Plugin + +} // namespace Dali diff --git a/plugins/dali-script-v8/src/controls/control-wrapper.h b/plugins/dali-script-v8/src/controls/control-wrapper.h new file mode 100644 index 0000000..dc4b636 --- /dev/null +++ b/plugins/dali-script-v8/src/controls/control-wrapper.h @@ -0,0 +1,127 @@ +#ifndef __DALI_V8PLUGIN_CONTROL_WRAPPER_H__ +#define __DALI_V8PLUGIN_CONTROL_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace V8Plugin +{ + + +/** + * Wraps a Dali Control. + */ +class ControlWrapper : public ActorWrapper +{ + +public: + + /** + * Control type used an index. + * These enums are used to index the ControlApiLookup table in control-wrapper.cpp. + * Any changes made must be reflected in the ControlApiLookup otherwise it may segfault when creating a control + */ + enum ControlType + { + UNKNOWN_CONTROL = -1, + CONTROL = 0, + ITEMVIEW = 1 + }; + + /** + * Constructor + * @param control DALi control + * @param gc garbage collection interface + */ + ControlWrapper( Toolkit::Control control, + GarbageCollectorInterface& gc ); + + /** + * destructor + */ + virtual ~ControlWrapper(); + + /** + * @brief Creates a new Control wrapped inside a Javascript Object. + * @note: the control type is passed as a parameter e.g. 'ItemView' + * @param[in] args v8 function call arguments interpreted + */ + static void NewControl( const v8::FunctionCallbackInfo< v8::Value >& args); + + /** + * @brief Wraps a control of a given type + */ + static v8::Handle WrapControl(v8::Isolate* isolate, Toolkit::Control control, ControlType controlType); + + /** + * @brief Wraps a control, the type is looked up from the control + */ + static v8::Handle WrapControl(v8::Isolate* isolate, Toolkit::Control control ); + + // The Control ObjectTemplates. + static v8::Persistent mControlTemplate; + static v8::Persistent mItemViewTemplate; + static v8::Persistent mScrollViewTemplate; + + /** + * @return the wrapped control + */ + Toolkit::Control GetControl(); + + /** + * @return the control type + */ + static ControlWrapper::ControlType GetControlType( const std::string& name ); + + /** + * @brief Register the garbage to be released when the wrapped control is deleted. + */ + static void RegisterGarbage(void* garbage); + +private: + + /** + * Helper to make the control template + */ + static v8::Handle MakeDaliControlTemplate( v8::Isolate* isolate, ControlType controlType ); + + /** + * Helper, get a control template given a control type + */ + static v8::Local GetControlTemplate( v8::Isolate* isolate, ControlType type ); + + Toolkit::Control mControl; + static Vector< void* > mControlGarbageContainer; + +}; + +} // namespace V8Plugin + +} // namespace Dali + +#endif // header diff --git a/plugins/dali-script-v8/src/controls/item-factory-wrapper.cpp b/plugins/dali-script-v8/src/controls/item-factory-wrapper.cpp new file mode 100644 index 0000000..62535cb --- /dev/null +++ b/plugins/dali-script-v8/src/controls/item-factory-wrapper.cpp @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// CLASS HEADER +#include "item-factory-wrapper.h" + +// EXTERNAL INCLUDES +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace V8Plugin +{ + +v8::Persistent ItemFactoryWrapper::mItemFactoryTemplate; + +namespace +{ + +typedef std::vector< Property::Map > ItemDataContainer; + +// Implementation of ItemFactory for providing actors to ItemView +class ItemFactory : public Toolkit::ItemFactory +{ +public: + + /** + * Constructor + * @param application class, stored as reference + */ + ItemFactory() + : mJsonFileLoaded(false), + mNumberOfItems(0) + { + mBuilder = Toolkit::Builder::New(); + } + + /** + * Set the name of the JSON file which defines the templates of items. + * @param jsonFile The JSON file + */ + void SetJsonTemplateFile(std::string jsonFile) + { + if(mJsonFile != jsonFile) + { + mJsonFile = jsonFile; + LoadJsonFile(mJsonFile); + + // Check whether any layout activated in ItemView + Toolkit::ItemView itemView = mItemView.GetHandle(); + if(itemView && itemView.GetActiveLayout() != NULL) + { + // Refresh ItemView if item templates are changed + itemView.Refresh(); + } + } + } + + /** + * Returns the name of the JSON file. + * @return The JSON file name + */ + std::string GetJsonTemplate() + { + return mJsonFile; + } + + /** + * Set the data to be used to create new items. + * + * If ItemView is already created, this will immediately update ItemView with the + * new data. + * + * The data is an array of property maps in which each map contains the data for + * each item, including the template to be used to build the actor and the pairs + * of key/value to be used to replace the constants defined in the template. + * The order of property maps in the array represents the actual order of items + * in ItemView. + * + * @param data The array of property maps + */ + void SetData(ItemDataContainer data) + { + ItemDataContainer currentData = mData; + mData = data; + mNumberOfItems = mData.size(); + + // Check whether any layout activated in ItemView + Toolkit::ItemView itemView = mItemView.GetHandle(); + if(itemView && itemView.GetActiveLayout() != NULL) + { + unsigned int currentNumberOfItems = currentData.size(); + unsigned int newNumberOfItems = data.size(); + + // Check whether any items added or deleted from the data + // which requires ItemView to be refreshed with the new data + if(currentNumberOfItems != newNumberOfItems) + { + itemView.Refresh(); + } + else + { + for( unsigned int itemId = 0; itemId < newNumberOfItems; itemId++) + { + // Check whether the item is already built in ItemView + Actor itemActor = itemView.GetItem(itemId); + if(itemActor) + { + // Check if the item needs to be rebuilt + if( !V8Utils::IsPropertyMapIdentical(currentData[itemId], data[itemId]) ) + { + // Rebuild the item with the new data + Actor newItemActor = NewItem(itemId); + + // Replace the old item with the new one + itemView.ReplaceItem( Toolkit::Item( itemId, newItemActor ), 0.0f ); + } + } + } + } + } + } + + /** + * Retrieve the data. + * @return the data. + */ + ItemDataContainer GetData() + { + return mData; + } + + /** + * Store a weak handle of ItemView in order to access ItemView APIs + * from this ItemFactory implementation + * @return the data. + */ + void SetItemView(Toolkit::ItemView itemView) + { + mItemView = itemView; + } + +public: // From Toolkit::ItemFactory + + /** + * Query the number of items available from the factory. + * The maximum available item has an ID of GetNumberOfItems() - 1. + */ + virtual unsigned int GetNumberOfItems() + { + return mJsonFileLoaded ? mNumberOfItems : 0; + } + + /** + * Create an Actor to represent a visible item. + * @param itemId + * @return the created actor. + */ + virtual Actor NewItem(unsigned int itemId) + { + std::string itemTemplate; + + Property::Map constantsMap = mData[itemId]; + for ( unsigned int i = 0, count = constantsMap.Count(); i < count; ++i ) + { + Property::Value& constantValue = constantsMap.GetValue(i); + if(constantsMap.GetKey(i) == "template") + { + constantValue.Get(itemTemplate); + } + else + { + mBuilder.AddConstant( constantsMap.GetKey(i), constantValue ); + } + } + + Actor item = Actor::DownCast( mBuilder.Create(itemTemplate) ); + return item; + } + +private: + + /** + * Load the JSON file. + * @param The JSON file name + */ + void LoadJsonFile(std::string jsonFile) + { + try + { + std::string data; + V8Utils::GetFileContents(jsonFile, data); + + mBuilder.LoadFromString(data); + + mJsonFileLoaded = true; + } + catch(...) + { +// printf("invalid JSON data\n"); + mJsonFileLoaded = false; + } + } + +private: + + std::string mJsonFile; + bool mJsonFileLoaded; + Toolkit::Builder mBuilder; + unsigned int mNumberOfItems; + ItemDataContainer mData; + WeakHandle< Toolkit::ItemView > mItemView; +}; + +} //un-named space + +ItemFactoryWrapper::ItemFactoryWrapper( Toolkit::ItemFactory& factory, GarbageCollectorInterface& gc ) +: BaseWrappedObject( BaseWrappedObject::ITEMFACTORY , gc ), + mItemFactory( factory ) +{ +} + +ItemFactoryWrapper::~ItemFactoryWrapper() +{ +} + +v8::Handle ItemFactoryWrapper::WrapItemFactory(v8::Isolate* isolate, Toolkit::ItemFactory& factory ) +{ + v8::EscapableHandleScope handleScope( isolate ); + v8::Local objectTemplate; + + objectTemplate = GetItemFactoryTemplate( isolate ); + + // create an instance of the template + v8::Local localObject = objectTemplate->NewInstance(); + + // create the ItemFactory wrapper + ItemFactoryWrapper* pointer = new ItemFactoryWrapper( factory, Dali::V8Plugin::DaliWrapper::Get().GetDaliGarbageCollector() ); + + // assign the JavaScript object to the wrapper. + pointer->SetJavascriptObject( isolate, localObject ); + + return handleScope.Escape( localObject ); +} + +v8::Local ItemFactoryWrapper::GetItemFactoryTemplate( v8::Isolate* isolate) +{ + v8::EscapableHandleScope handleScope( isolate ); + v8::Local objectTemplate; + + if( mItemFactoryTemplate.IsEmpty() ) + { + objectTemplate = MakeItemFactoryTemplate( isolate ); + mItemFactoryTemplate.Reset( isolate, objectTemplate ); + } + else + { + // get the object template + objectTemplate = v8::Local::New( isolate, mItemFactoryTemplate ); + } + return handleScope.Escape( objectTemplate ); +} + +v8::Handle ItemFactoryWrapper::MakeItemFactoryTemplate( v8::Isolate* isolate ) +{ + v8::EscapableHandleScope handleScope( isolate ); + + v8::Local objTemplate = v8::ObjectTemplate::New(); + + objTemplate->SetInternalFieldCount( BaseWrappedObject::FIELD_COUNT ); + + // set property setter and getter + objTemplate->SetNamedPropertyHandler( ItemFactoryWrapper::PropertyGet, ItemFactoryWrapper::PropertySet); + + return handleScope.Escape( objTemplate ); +} + +void ItemFactoryWrapper::NewItemFactory( const v8::FunctionCallbackInfo< v8::Value >& args) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate); + + if( !args.IsConstructCall() ) + { + DALI_SCRIPT_EXCEPTION( isolate, "ItemFactory constructor called without 'new'" ); + return; + } + + Toolkit::ItemFactory* factory = new ItemFactory(); + + v8::Local localObject = WrapItemFactory( isolate, *factory ); + args.GetReturnValue().Set( localObject ); +} + +Toolkit::ItemFactory& ItemFactoryWrapper::GetItemFactoryFromParams( int paramIndex, + bool& found, + v8::Isolate* isolate, + const v8::FunctionCallbackInfo< v8::Value >& args ) +{ + found = false; + + v8::HandleScope handleScope( isolate ); + BaseWrappedObject* wrappedObject = V8Utils::GetWrappedDaliObjectParameter( paramIndex, BaseWrappedObject::ITEMFACTORY, isolate, args ); + if( wrappedObject ) + { + found = true; + ItemFactoryWrapper* wrapper = static_cast< ItemFactoryWrapper *>(wrappedObject); + return wrapper->GetItemFactory(); + } + else + { + DALI_SCRIPT_EXCEPTION( isolate, "no valid ItemFactory parameter" ); + Toolkit::ItemFactory* dummyFactory = new ItemFactory(); + return *dummyFactory; // avoid build error + } +} + +ItemFactoryWrapper* ItemFactoryWrapper::Unwrap( v8::Isolate* isolate, v8::Handle< v8::Object> obj) +{ + v8::HandleScope handleScope( isolate ); + + v8::Local field = v8::Local::Cast( obj->GetInternalField(0) ); + void* ptr = field->Value(); + return static_cast< ItemFactoryWrapper *>(ptr); +} + +void ItemFactoryWrapper::PropertyGet( v8::Local propertyName, + const v8::PropertyCallbackInfo& info) +{ + v8::Isolate* isolate = info.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + // get the property name + std::string name = V8Utils::v8StringToStdString( propertyName ); + + if( std::isupper( name[0] )) + { + return; + } + + // unwrap the object + ItemFactoryWrapper* itemFactoryWrapper = Unwrap( isolate, info.This() ); + if( !itemFactoryWrapper ) + { + return; + } + + ItemFactory& factory = static_cast( itemFactoryWrapper->GetItemFactory() ); + + if( name == "jsonTemplateFile" ) + { + std::string jsonTemplateFile = factory.GetJsonTemplate(); + info.GetReturnValue().Set(v8::String::NewFromUtf8(isolate, jsonTemplateFile.c_str())); + } + else if( name == "data" ) + { + ItemDataContainer data = factory.GetData(); + unsigned int itemCount = data.size(); + + v8::Local array= v8::Array::New( isolate, itemCount ); + for( unsigned int i = 0; i < itemCount; i++) + { + v8::Local mapObject = v8::Object::New( isolate ); + V8Utils::CreatePropertyMap( isolate, data[i], mapObject ); + + array->Set( i, mapObject); + } + + info.GetReturnValue().Set(array); + } + else + { + std::string error="Invalid property Get for "+name + "\n"; + DALI_SCRIPT_EXCEPTION( isolate, error ); + } +} + +void ItemFactoryWrapper::PropertySet( v8::Local propertyName, + v8::Local javaScriptValue, + const v8::PropertyCallbackInfo& info) +{ + + v8::Isolate* isolate = info.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + // get the property name + std::string name = V8Utils::v8StringToStdString( propertyName ); + + // unwrap the object + ItemFactoryWrapper* itemFactoryWrapper = Unwrap( isolate, info.This() ); + if( !itemFactoryWrapper ) + { + return; + } + + ItemFactory& factory = static_cast( itemFactoryWrapper->GetItemFactory() ); + + if( name == "jsonTemplateFile" && javaScriptValue->IsString() ) + { + std::string jsonTemplateFile = V8Utils::v8StringToStdString( javaScriptValue ); + factory.SetJsonTemplateFile(jsonTemplateFile); + } + else if( name == "data" && javaScriptValue->IsArray() ) + { + v8::Local array = v8::Local::Cast(javaScriptValue); + + ItemDataContainer data; + + for( unsigned int i = 0; i < array->Length(); ++i ) + { + v8::Local itemData = array->Get(i); + + if( itemData->IsObject() ) + { + Dali::Property::Map map = V8Utils::GetPropertyMapFromObject( isolate, itemData->ToObject() ); + data.push_back(map); + } + } + + factory.SetData(data); + } + else + { + std::string error = "Invalid property Set for " + name + "\n"; + DALI_SCRIPT_EXCEPTION( isolate, error ); + } +} + +Toolkit::ItemFactory& ItemFactoryWrapper::GetItemFactory() +{ + return mItemFactory; +} + +void ItemFactoryWrapper::SetItemView(Toolkit::ItemFactory& itemFactory, Toolkit::ItemView itemView) +{ + ItemFactory& factory = static_cast( itemFactory ); + factory.SetItemView(itemView); +} + +} // namespace V8Plugin + +} // namespace Dali diff --git a/plugins/dali-script-v8/src/controls/item-factory-wrapper.h b/plugins/dali-script-v8/src/controls/item-factory-wrapper.h new file mode 100644 index 0000000..cfb21fb --- /dev/null +++ b/plugins/dali-script-v8/src/controls/item-factory-wrapper.h @@ -0,0 +1,146 @@ +#ifndef __DALI_V8PLUGIN_ITEM_FACTORY_WRAPPER_H__ +#define __DALI_V8PLUGIN_ITEM_FACTORY_WRAPPER_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +namespace V8Plugin +{ + + +/** + * Wraps a Dali ItemFactory. + */ +class ItemFactoryWrapper : public BaseWrappedObject +{ + +public: + + /** + * Constructor + * @param factory DALi ItemFactory + * @param gc garbage collection interface + */ + ItemFactoryWrapper( Toolkit::ItemFactory& factory, + GarbageCollectorInterface& gc ); + + /** + * destructor + */ + virtual ~ItemFactoryWrapper(); + + /** + * @brief Creates a new ItemFactory wrapped inside a Javascript Object. + * @note: the item template and data are passed as a parameter e.g. 'template' + * @param[in] args v8 function call arguments interpreted + */ + static void NewItemFactory( const v8::FunctionCallbackInfo< v8::Value >& args); + + /** + * @brief Wraps an ItemFactory + */ + static v8::Handle WrapItemFactory(v8::Isolate* isolate, Toolkit::ItemFactory& factory ); + + // The ItemFactory ObjectTemplates. + static v8::Persistent mItemFactoryTemplate; + + /** + * @brief Helper to get ItemFactory from the JavaScript object held in the given function argument + * @param[in] paramIndex Argument index the object is held in + * @param[in] found Whether ItemFactory is found in the given function parameter + * @param[in] isolate v8 isolated instance + * @param[in] args v8 function call arguments interpreted + */ + static Toolkit::ItemFactory& GetItemFactoryFromParams( int paramIndex, + bool& found, + v8::Isolate* isolate, + const v8::FunctionCallbackInfo< v8::Value >& args ); + + /** + * @brief Helper to store a weak handle of ItemView in the given ItemFactory + * @param[in] itemFactory The item factory used to provide items to the given item view + * @param[in] itemView The ItemView which uses the given item factory to create items + */ + static void SetItemView(Toolkit::ItemFactory& itemFactory, Toolkit::ItemView itemView); + + /** + * @return the wrapped item factory + */ + Toolkit::ItemFactory& GetItemFactory(); + +private: + + /** + * Helper to make the item factory template + */ + static v8::Handle MakeItemFactoryTemplate( v8::Isolate* isolate ); + + /** + * Helper, get a item factory template + */ + static v8::Local GetItemFactoryTemplate( v8::Isolate* isolate ); + + /** + * @brief get the value for a property for JavaScript object than contains a Dali ItemFactory. + * E.g. Get( "data", JavaScript object that wraps a Dali ItemFactory ) + * @param[in] propertyName property name + * @param[in] info reference to PropertyCallbackInfo structure (contains the Javascript + * object and the return value). + */ + static void PropertyGet( v8::Local propertyName, + const v8::PropertyCallbackInfo& info); + + /** + * @brief Set the value for a property for JavaScript object than contains a Dali ItemFactory. + * E.g. Set( "data", itemData, JavaScript object that wraps a Dali ItemFactory) + * @param[in] propertyName property name + * @param[in] javaScriptValue javascript value to set, this is typically a number + * @param[in] info reference to PropertyCallbackInfo structure (contains the Javascript + * object). + */ + static void PropertySet( v8::Local propertyName, + v8::Local javaScriptValue, + const v8::PropertyCallbackInfo& info); + + + /** + * @brief Extract a item factory wrapper from a javascript object + * @return item factory wrapper + */ + static ItemFactoryWrapper* Unwrap( v8::Isolate* isolate, v8::Handle< v8::Object> obj); + + Toolkit::ItemFactory& mItemFactory; + +}; + +} // namespace V8Plugin + +} // namespace Dali + +#endif // header diff --git a/plugins/dali-script-v8/src/controls/item-view-api.cpp b/plugins/dali-script-v8/src/controls/item-view-api.cpp new file mode 100644 index 0000000..4852a61 --- /dev/null +++ b/plugins/dali-script-v8/src/controls/item-view-api.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// CLASS HEADER +#include "item-view-api.h" + +// EXTERNAL INCLUDES +#include +#include +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace V8Plugin +{ + +namespace // unanmed namespace +{ + +Toolkit::ItemView GetItemView( v8::Isolate* isolate, const v8::FunctionCallbackInfo& args ) +{ + HandleWrapper* handleWrapper = HandleWrapper::Unwrap( isolate, args.This() ); + return Toolkit::ItemView::DownCast( handleWrapper->mHandle ); +} + +} //unanmed namespace + +/*************************************** + * ITEMVIEW API FUNCTIONS + ***************************************/ + +/** + * Constructor + * + * @for ItemView + * @constructor + * @method ItemView + * @return {Object} itemView + */ +Toolkit::Control ItemViewApi::New( const v8::FunctionCallbackInfo< v8::Value >& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + bool found( false ); + Toolkit::ItemFactory& factory = ItemFactoryWrapper::GetItemFactoryFromParams( 1, found, isolate, args ); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid ItemFactory parameter" ); + return Toolkit::Control(); + } + else + { + Toolkit::ItemView itemView = Toolkit::ItemView::New(factory); + ItemFactoryWrapper::SetItemView(factory, itemView); + return itemView; + } +} + +/** + * Query the number of layouts. + * + * @for ItemView + * @method getLayoutCount + * @return {Integer} The number of layouts. + */ +void ItemViewApi::GetLayoutCount( const v8::FunctionCallbackInfo< v8::Value >& args) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + args.GetReturnValue().Set( v8::Integer::New( isolate, itemView.GetLayoutCount() ) ); +} + +/** + * Add a layout + * + * @for ItemView + * @method addLayout + * @param {Integer} layout The layout to be added + * @example + * // layout is one of the following + * dali.ITEM_LAYOUT_LIST + * dali.ITEM_LAYOUT_GRID + * + * itemView.addLayout( dali.ITEM_LAYOUT_LIST ); + */ +void ItemViewApi::AddLayout( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int layout = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid layout parameter" ); + return; + } + + Toolkit::ItemLayoutPtr layoutPtr = Toolkit::DefaultItemLayout::New( static_cast(layout) ); + itemView.AddLayout( *layoutPtr ); +} + +/** + * Remove a layout. + * + * @for ItemView + * @method removeLayout + * @param {Integer} layoutIndex The index of the ItemView layouts which must be less than getLayoutCount(). + */ +void ItemViewApi::RemoveLayout( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int layoutIndex = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid index parameter" ); + return; + } + + itemView.RemoveLayout( layoutIndex ); +} + +/** + * Activate one of the layouts. This will resize the ItemView and relayout actors within the ItemView. + * + * @for ItemView + * @method activateLayout + * @param {Integer} layoutIndex The index of the ItemView layout which must be less than getLayoutCount(). + * @param {Object} targetSize An array of 3 numbers for the target ItemView and layout size. + * @param {Float} [durationSeconds] The time taken to relayout in seconds (0 by default for immediate). + */ +void ItemViewApi::ActivateLayout( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int layoutIndex = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 ); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid layout index parameter" ); + return; + } + + found = false; + Vector3 targetSize = V8Utils::GetVector3Parameter( PARAMETER_1, found, isolate, args ); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "Vector3 targetSize size parameter missing" ); + return; + } + + found = false; + float durationSeconds = V8Utils::GetFloatParameter( PARAMETER_2, found, isolate, args, 0.0f ); // 0 by default for immediate activation + + itemView.ActivateLayout( layoutIndex, targetSize, durationSeconds ); +} + +/** + * Retrieve the target size of an item in the given layout. + * This will return the default size for the layout unless overridden by calling setLayoutItemSize(). + * + * @for ItemView + * @method getItemSize + * @param {Integer} layoutIndex The index of the ItemView layout which must be less than getLayoutCount(). + * @param {Integer} itemId The ID of an item in the layout. + * @param {Object} targetLayoutSize An array of 3 numbers for the target ItemView and layout size. + * @return {Object} The target size of the item {x, y, z}. + */ +void ItemViewApi::GetItemSize( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int layoutIndex = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid layout index parameter" ); + return; + } + + found = false; + int itemId = V8Utils::GetIntegerParameter( PARAMETER_1, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid item ID parameter" ); + return; + } + + found = false; + Vector3 targetLayoutSize = V8Utils::GetVector3Parameter( PARAMETER_2, found, isolate, args ); + if( found ) + { + Toolkit::ItemLayoutPtr layoutPtr = itemView.GetLayout(layoutIndex); + Vector3 itemSize; + layoutPtr->GetItemSize( itemId, targetLayoutSize, itemSize ); + + v8::Local itemSizeObject = v8::Object::New( isolate ); + + itemSizeObject->Set( v8::String::NewFromUtf8( isolate, "x" ), v8::Integer::New( isolate, itemSize.width ) ); + itemSizeObject->Set( v8::String::NewFromUtf8( isolate, "y" ), v8::Integer::New( isolate, itemSize.height ) ); + itemSizeObject->Set( v8::String::NewFromUtf8( isolate, "z" ), v8::Integer::New( isolate, itemSize.depth ) ); + + args.GetReturnValue().Set( itemSizeObject ); + } + else + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid Vector3 target size parameter" ); + } +} + +/** + * Set the size of the item for the given layout which overrides the default item size for the layout. + * + * @for ItemView + * @method setItemSize + * @param {Integer} layoutIndex The index of the ItemView layout which must be less than getLayoutCount(). + * @param {Object} itemSize An array of 3 numbers for the size of the item. + * @example + * itemView.setLayoutItemSize( 0, [100.0, 50.0, 0.0] ); + */ +void ItemViewApi::SetItemSize( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int layoutIndex = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid layout index parameter" ); + return; + } + + found = false; + Vector3 itemSize = V8Utils::GetVector3Parameter( PARAMETER_1, found, isolate, args ); + if( found ) + { + Toolkit::ItemLayoutPtr layoutPtr = itemView.GetLayout(layoutIndex); + layoutPtr->SetItemSize( itemSize ); + } + else + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid item size parameter" ); + } +} + +/** + * Scroll the current layout to a particular item. + * If calling this with zero second of duration immediately after calling activateLayout(), + * it will not work unless the duration of relayout animation for activateLayout is also + * set to zero. + * + * @for ItemView + * @method scrollToItem + * @param {Integer} itemId The ID of an item in the layout. + * @param {Float} [durationSeconds] How long the scrolling takes in seconds (0 by default for instant scrolling to the particular item). + */ +void ItemViewApi::ScrollToItem( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int itemId = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid item Id parameter" ); + return; + } + + found = false; + float durationSeconds = V8Utils::GetFloatParameter( PARAMETER_1, found, isolate, args, 0.0f ); // 0 by default for instant scrolling + + itemView.ScrollToItem( itemId, durationSeconds ); +} + +/** + * Given the Item ID, this returns the accompanying actor. + * + * @for ItemView + * @method getItem + * @param {Integer} itemId The Item ID of the actor required. + * @return {Object} The Actor corresponding to the Item ID. + */ +void ItemViewApi::GetItem( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + int itemId = V8Utils::GetIntegerParameter( PARAMETER_0, found, isolate, args, 0 /* default */); + if( found ) + { + found = false; + Actor actor = itemView.GetItem( itemId ); + if( actor ) + { + found = true; + // wrap the actor + v8::Handle < v8::Object > wrappedActor = ActorWrapper::WrapActor( isolate, actor ); + args.GetReturnValue().Set( wrappedActor ); + } + } + + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid item ID" ); + return; + } +} + +/** + * Returns the Item ID of the specified actor. The actor must be an item of ItemView. + * + * @for ItemView + * @method getItemId + * @param {Object} actor The actor whose Item ID is required. + * @return {Integer} The Item ID of the item. + */ +void ItemViewApi::GetItemId( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + bool found( false ); + Actor actor = V8Utils::GetActorParameter( 0, found, isolate, args ); + if( found ) + { + args.GetReturnValue().Set( itemView.GetItemId(actor) ); + } + else + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid item actor parameter" ); + return; + } +} + +/** + * Get the range of items that are currently in ItemView. + * + * @for ItemView + * @method getItemsRange + * @return {Object} The range of items in the item ID {begin, end}. + */ +void ItemViewApi::GetItemsRange( const v8::FunctionCallbackInfo& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + Toolkit::ItemView itemView = GetItemView( isolate, args ); + + Toolkit::ItemRange range(0, 0); + itemView.GetItemsRange(range); + + v8::Local itemRangeObject = v8::Object::New( isolate ); + + itemRangeObject->Set( v8::String::NewFromUtf8( isolate, "begin" ), v8::Integer::New( isolate, range.begin ) ); + itemRangeObject->Set( v8::String::NewFromUtf8( isolate, "end" ), v8::Integer::New( isolate, range.end ) ); + + args.GetReturnValue().Set( itemRangeObject ); +} + +} // namespace V8Plugin + +} // namespace Dali diff --git a/plugins/dali-script-v8/src/controls/item-view-api.h b/plugins/dali-script-v8/src/controls/item-view-api.h new file mode 100644 index 0000000..7f56653 --- /dev/null +++ b/plugins/dali-script-v8/src/controls/item-view-api.h @@ -0,0 +1,59 @@ +#ifndef __DALI_V8PLUGIN_ITEM_VIEW_API_H__ +#define __DALI_V8PLUGIN_ITEM_VIEW_API_H__ + +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace V8Plugin +{ + +namespace ItemViewApi +{ + + /** + * constructor + */ + Toolkit::Control New( const v8::FunctionCallbackInfo< v8::Value >& args ); + + /** + * ItemView API. See item-view.h for description of functions + */ + void GetLayoutCount( const v8::FunctionCallbackInfo< v8::Value >& args); + void AddLayout( const v8::FunctionCallbackInfo< v8::Value >& args ); + void RemoveLayout( const v8::FunctionCallbackInfo< v8::Value >& args ); + void ActivateLayout( const v8::FunctionCallbackInfo< v8::Value >& args); + void GetItemSize( const v8::FunctionCallbackInfo< v8::Value >& args); + void SetItemSize( const v8::FunctionCallbackInfo< v8::Value >& args); + void ScrollToItem( const v8::FunctionCallbackInfo< v8::Value >& args); + void GetItem( const v8::FunctionCallbackInfo< v8::Value >& args); + void GetItemId( const v8::FunctionCallbackInfo< v8::Value >& args); + void GetItemsRange( const v8::FunctionCallbackInfo< v8::Value >& args); + +}; // namespace ItemViewApi + +} // namespace V8Plugin + +} // namespace Dali + +#endif // header __DALI_V8PLUGIN_ITEM_VIEW_API_H__ diff --git a/plugins/dali-script-v8/src/dali-wrapper.cpp b/plugins/dali-script-v8/src/dali-wrapper.cpp index 132dc5e..92719c7 100644 --- a/plugins/dali-script-v8/src/dali-wrapper.cpp +++ b/plugins/dali-script-v8/src/dali-wrapper.cpp @@ -23,12 +23,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -70,11 +72,12 @@ const ApiFunction ConstructorFunctionTable[]= { "Actor", ActorWrapper::NewActor }, { "CameraActor", ActorWrapper::NewActor }, { "Layer", ActorWrapper::NewActor }, - { "Control", ActorWrapper::NewControl }, + { "Control", ControlWrapper::NewControl }, { "ResourceImage", ImageWrapper::NewImage }, { "BufferImage", ImageWrapper::NewImage }, { "FrameBufferImage", ImageWrapper::NewImage }, { "Animation", AnimationWrapper::NewAnimation}, + { "ItemFactory", ItemFactoryWrapper::NewItemFactory}, { "Shader", ShaderWrapper::NewShader}, { "Sampler", SamplerWrapper::NewSampler}, { "Material", MaterialWrapper::NewMaterial}, diff --git a/plugins/dali-script-v8/src/object/handle-wrapper.cpp b/plugins/dali-script-v8/src/object/handle-wrapper.cpp index 2915670..be1739d 100644 --- a/plugins/dali-script-v8/src/object/handle-wrapper.cpp +++ b/plugins/dali-script-v8/src/object/handle-wrapper.cpp @@ -42,6 +42,7 @@ namespace // un-named name space const ApiFunction HandleFunctionTable[]= { { "RegisterAnimatableProperty", HandleWrapper::RegisterAnimatableProperty }, + { "RegisterCustomProperty", HandleWrapper::RegisterCustomProperty }, }; const unsigned int HandleFunctionTableCount = sizeof(HandleFunctionTable)/sizeof(HandleFunctionTable[0]); @@ -212,8 +213,8 @@ void HandleWrapper::AddInterceptsToTemplate( v8::Isolate* isolate, v8::Local& args ) @@ -251,6 +252,75 @@ void HandleWrapper::RegisterAnimatableProperty( const v8::FunctionCallbackInfo< } } +/** + * Register a new custom property. + * + * The object should support dynamic properties. + * Property names must be unused. + * Property indices are unique to each registered custom property in a given object. + * Properties can be set as non animatable using property attributes. + * returns dali.PROPERTY_INVALID_INDEX if registration failed. + * + * @method registerCustomProperty + * @for Handle + * @param {string} name The name of the property. + * @param {Object} propertyValue The new value of the property. + * @param {integer} accessMode The property access mode (writable, animatable etc). + * @return {integer} The index of the property or dali.PROPERTY_INVALID_INDEX if registration failed + * @example + * + * // access mode is one of the following + * dali.PROPERTY_READ_ONLY + * dali.PROPERTY_READ_WRITE + * dali.PROPERTY_ANIMATABLE + * + * var cellIndexPropertyIndex = actor.registerCustomProperty("cellIndex", 2, dali.PROPERTY_READ_WRITE); + * var myCustomPropertyIndex = handle.registerCustomProperty("myCustomProperty", [10.0, 25.0, 0.0], dali.PROPERTY_READ_ONLY); + * + */ +void HandleWrapper::RegisterCustomProperty( const v8::FunctionCallbackInfo< v8::Value >& args ) +{ + v8::Isolate* isolate = args.GetIsolate(); + v8::HandleScope handleScope( isolate ); + + // unwrap the object + HandleWrapper* handleWrapper = Unwrap( isolate, args.This() ); + if( !handleWrapper ) + { + return; + } + + Handle handle = handleWrapper->mHandle; + + bool found( false ); + std::string propertyName = V8Utils::GetStringParameter( PARAMETER_0, found, isolate, args ); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "bad property name parameter" ); + return; + } + + found = false; + Dali::Property::Value daliPropertyValue = V8Utils::GetPropertyValueParameter(PARAMETER_1, found, isolate, args ); + if( !found || Dali::Property::NONE == daliPropertyValue.GetType() ) + { + DALI_SCRIPT_EXCEPTION( isolate, "bad property value parameter" ); + return; + } + + found = false; + int accessMode = V8Utils::GetIntegerParameter( PARAMETER_2, found, isolate, args, 0 /* default */); + if( !found ) + { + DALI_SCRIPT_EXCEPTION( isolate, "invalid access mode parameter" ); + return; + } + else + { + args.GetReturnValue().Set( v8::Integer::New( isolate, handle.RegisterProperty( propertyName, daliPropertyValue, static_cast(accessMode) ) ) ); + } +} + } // namespace V8Plugin } // namespace Dali diff --git a/plugins/dali-script-v8/src/object/handle-wrapper.h b/plugins/dali-script-v8/src/object/handle-wrapper.h index 2e612c7..7356f01 100644 --- a/plugins/dali-script-v8/src/object/handle-wrapper.h +++ b/plugins/dali-script-v8/src/object/handle-wrapper.h @@ -81,6 +81,13 @@ public: */ static void RegisterAnimatableProperty( const v8::FunctionCallbackInfo< v8::Value >& args ); + /** + * @brief Register a custom property for a JavaScript object that + * contains a Dali Handle. + * @param[in] args v8 function call arguments interpreted + */ + static void RegisterCustomProperty( const v8::FunctionCallbackInfo< v8::Value >& args ); + Handle GetHandle() { return mHandle; } Handle mHandle; ConnectionTracker mConnectionTracker; diff --git a/plugins/dali-script-v8/src/shared/base-wrapped-object.h b/plugins/dali-script-v8/src/shared/base-wrapped-object.h index db1c8a8..0bd9840 100644 --- a/plugins/dali-script-v8/src/shared/base-wrapped-object.h +++ b/plugins/dali-script-v8/src/shared/base-wrapped-object.h @@ -80,6 +80,8 @@ public: IMAGE_ATTRIBUTES, ACTOR, ACTOR_PROPERTY, + ITEMVIEW, + ITEMFACTORY, RENDER_TASK, RENDER_TASK_LIST, TIMER, diff --git a/plugins/dali-script-v8/src/utils/v8-utils.cpp b/plugins/dali-script-v8/src/utils/v8-utils.cpp index 4d09da9..b77d458 100644 --- a/plugins/dali-script-v8/src/utils/v8-utils.cpp +++ b/plugins/dali-script-v8/src/utils/v8-utils.cpp @@ -149,6 +149,118 @@ void GetModuleName( const std::string& fileName, std::string& moduleName ) } } +bool IsPropertyMapIdentical(Property::Map map1, Property::Map map2) +{ + bool dirty = false; + + // Compare number of properties + if ( map1.Count() != map2.Count() ) + { + dirty = true; + } + else + { + for ( unsigned int i = 0, count = map1.Count(); i < count; ++i ) + { + // Compare the key first + if(map1.GetKey(i) != map2.GetKey(i)) + { + dirty = true; + } + else + { + Property::Value& value = map1.GetValue(i); + Property::Value& newValue = map2.GetValue(i); + + // Compare the value type + if(value.GetType() != newValue.GetType()) + { + dirty = true; + } + else + { + // Compare the value + switch( value.GetType() ) + { + case Property::BOOLEAN: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::FLOAT: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::INTEGER: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::RECTANGLE: + { + dirty = ( value.Get< Rect >() != newValue.Get< Rect >() ); + break; + } + case Property::VECTOR2: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::VECTOR3: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::VECTOR4: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::MATRIX3: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::MATRIX: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::ROTATION: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::STRING: + { + dirty = ( value.Get() != newValue.Get() ); + break; + } + case Property::MAP: + { + dirty = ( !IsPropertyMapIdentical( value.Get(), newValue.Get() ) ); + break; + } + default: + { + break; + } + } + } + } + + if(dirty) + { + // Different already, no need any further comparison + break; + } + } + } + + return !dirty; +} + void ReportException( v8::Isolate* isolate, v8::TryCatch* tryCatch) { v8::HandleScope handleScope( isolate ); @@ -879,7 +991,7 @@ void CreatePropertyMap( v8::Isolate* isolate, const Property::Map& map, v8::Loca return; } - for( unsigned int index = 0; index < map.Count() - 1; ++index ) + for( unsigned int index = 0; index < map.Count(); ++index ) { const std::string& key = map.GetKey( index ); Property::Value& value = map.GetValue( index ); diff --git a/plugins/dali-script-v8/src/utils/v8-utils.h b/plugins/dali-script-v8/src/utils/v8-utils.h index 1551868..5a86f39 100644 --- a/plugins/dali-script-v8/src/utils/v8-utils.h +++ b/plugins/dali-script-v8/src/utils/v8-utils.h @@ -96,6 +96,14 @@ void GetFileName( const std::string& fullPathName, std::string& fileName); void GetModuleName( const std::string& fileName, std::string& moduleName ); /** + * Compare whether two DALi property maps are identical + * @param[in] map1 The first property map to be compared + * @param[in] map2 The second property map to be compared + * @return true if the two specified property maps are identical or false if not. + */ +bool IsPropertyMapIdentical(Property::Map map1, Property::Map map2); + +/** * Report an exception by writing as a warning to the Dali Log * * @param[in] try_catch The v8 TryCatch exception object -- 2.7.4